Add new VideoConference module for muting mic/cam (#11798)

* add new VideoConference module for muting mic/cam

Co-authored-by: PrzemyslawTusinski <61138537+PrzemyslawTusinski@users.noreply.github.com>
Co-authored-by: Niels Laute <niels.laute@live.nl>
This commit is contained in:
Andrey Nekrasov
2021-06-29 13:06:12 +03:00
committed by GitHub
parent e450669663
commit 4e23832d52
116 changed files with 7425 additions and 81 deletions

View File

@@ -0,0 +1,15 @@
#include "CameraStateUpdateChannels.h"
#include "naming.h"
std::wstring_view CameraOverlayImageChannel::endpoint()
{
static const std::wstring endpoint = ObtainStableGlobalNameForKernelObject(L"PowerToysVideoConferenceCameraOverlayImageChannelSharedMemory", true);
return endpoint;
}
std::wstring_view CameraSettingsUpdateChannel::endpoint()
{
static const std::wstring endpoint = ObtainStableGlobalNameForKernelObject(L"PowerToysVideoConferenceSettingsChannelSharedMemory", true);
return endpoint;
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <optional>
#include <string_view>
#include <array>
struct alignas(16) CameraSettingsUpdateChannel
{
bool useOverlayImage = false;
bool cameraInUse = false;
std::optional<uint32_t> overlayImageSize;
std::optional<std::array<wchar_t, 256>> sourceCameraName;
bool newOverlayImagePosted = false;
static std::wstring_view endpoint();
};
namespace CameraOverlayImageChannel
{
std::wstring_view endpoint();
}

View File

@@ -0,0 +1,203 @@
#include "Logging.h"
#include <iostream>
#include <fstream>
#include <mutex>
#include <iomanip>
#include <chrono>
#include <filesystem>
#include <mfapi.h>
#pragma warning(disable : 4127)
static std::mutex logMutex;
constexpr inline size_t maxLogSizeMegabytes = 10;
constexpr inline bool alwaysLogVerbose = true;
void LogToFile(std::wstring what, const bool verbose)
{
std::error_code _;
const auto tempPath = std::filesystem::temp_directory_path(_);
if (verbose)
{
const bool verboseIndicatorFilePresent = std::filesystem::exists(tempPath / L"PowerToysVideoConferenceVerbose.flag", _);
if (!alwaysLogVerbose && !verboseIndicatorFilePresent)
{
return;
}
}
time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm tm;
localtime_s(&tm, &now);
char prefix[64];
const auto pid = GetCurrentProcessId();
const auto iter = prefix + sprintf_s(prefix, "[%ld]", pid);
std::strftime(iter, sizeof(prefix) - (prefix - iter), "[%d.%m %H:%M:%S] ", &tm);
std::lock_guard lock{ logMutex };
std::wstring logFilePath = tempPath;
#if defined(_WIN64)
logFilePath += L"\\PowerToysVideoConference_x64.log";
#elif defined(_WIN32)
logFilePath += L"\\PowerToysVideoConference_x86.log";
#endif
size_t logSizeMBs = 0;
try
{
logSizeMBs = static_cast<size_t>(std::filesystem::file_size(logFilePath) >> 20);
}
catch (...)
{
}
if (logSizeMBs > maxLogSizeMegabytes)
{
std::error_code __;
// Truncate the log file to zero
std::filesystem::resize_file(logFilePath, 0, __);
}
std::wofstream myfile;
myfile.open(logFilePath, std::fstream::app);
static const auto newLaunch = [&] {
myfile << prefix << "\n\n<<<NEW SESSION>>";
return 0;
}();
myfile << prefix << what << "\n";
myfile.close();
}
void LogToFile(std::string what, const bool verbose)
{
std::wstring native{ begin(what), end(what) };
LogToFile(std::move(native), verbose);
}
std::string toMediaTypeString(GUID subtype)
{
if (subtype == MFVideoFormat_YUY2)
return "MFVideoFormat_YUY2";
else if (subtype == MFVideoFormat_RGB32)
return "MFVideoFormat_RGB32";
else if (subtype == MFVideoFormat_RGB24)
return "MFVideoFormat_RGB24";
else if (subtype == MFVideoFormat_ARGB32)
return "MFVideoFormat_ARGB32";
else if (subtype == MFVideoFormat_RGB555)
return "MFVideoFormat_RGB555";
else if (subtype == MFVideoFormat_RGB565)
return "MFVideoFormat_RGB565";
else if (subtype == MFVideoFormat_RGB8)
return "MFVideoFormat_RGB8";
else if (subtype == MFVideoFormat_L8)
return "MFVideoFormat_L8";
else if (subtype == MFVideoFormat_L16)
return "MFVideoFormat_L16";
else if (subtype == MFVideoFormat_D16)
return "MFVideoFormat_D16";
else if (subtype == MFVideoFormat_AYUV)
return "MFVideoFormat_AYUV";
else if (subtype == MFVideoFormat_YUY2)
return "MFVideoFormat_YUY2";
else if (subtype == MFVideoFormat_YVYU)
return "MFVideoFormat_YVYU";
else if (subtype == MFVideoFormat_YVU9)
return "MFVideoFormat_YVU9";
else if (subtype == MFVideoFormat_UYVY)
return "MFVideoFormat_UYVY";
else if (subtype == MFVideoFormat_NV11)
return "MFVideoFormat_NV11";
else if (subtype == MFVideoFormat_NV12)
return "MFVideoFormat_NV12";
else if (subtype == MFVideoFormat_YV12)
return "MFVideoFormat_YV12";
else if (subtype == MFVideoFormat_I420)
return "MFVideoFormat_I420";
else if (subtype == MFVideoFormat_IYUV)
return "MFVideoFormat_IYUV";
else if (subtype == MFVideoFormat_Y210)
return "MFVideoFormat_Y210";
else if (subtype == MFVideoFormat_Y216)
return "MFVideoFormat_Y216";
else if (subtype == MFVideoFormat_Y410)
return "MFVideoFormat_Y410";
else if (subtype == MFVideoFormat_Y416)
return "MFVideoFormat_Y416";
else if (subtype == MFVideoFormat_Y41P)
return "MFVideoFormat_Y41P";
else if (subtype == MFVideoFormat_Y41T)
return "MFVideoFormat_Y41T";
else if (subtype == MFVideoFormat_Y42T)
return "MFVideoFormat_Y42T";
else if (subtype == MFVideoFormat_P210)
return "MFVideoFormat_P210";
else if (subtype == MFVideoFormat_P216)
return "MFVideoFormat_P216";
else if (subtype == MFVideoFormat_P010)
return "MFVideoFormat_P010";
else if (subtype == MFVideoFormat_P016)
return "MFVideoFormat_P016";
else if (subtype == MFVideoFormat_v210)
return "MFVideoFormat_v210";
else if (subtype == MFVideoFormat_v216)
return "MFVideoFormat_v216";
else if (subtype == MFVideoFormat_v410)
return "MFVideoFormat_v410";
else if (subtype == MFVideoFormat_MP43)
return "MFVideoFormat_MP43";
else if (subtype == MFVideoFormat_MP4S)
return "MFVideoFormat_MP4S";
else if (subtype == MFVideoFormat_M4S2)
return "MFVideoFormat_M4S2";
else if (subtype == MFVideoFormat_MP4V)
return "MFVideoFormat_MP4V";
else if (subtype == MFVideoFormat_WMV1)
return "MFVideoFormat_WMV1";
else if (subtype == MFVideoFormat_WMV2)
return "MFVideoFormat_WMV2";
else if (subtype == MFVideoFormat_WMV3)
return "MFVideoFormat_WMV3";
else if (subtype == MFVideoFormat_WVC1)
return "MFVideoFormat_WVC1";
else if (subtype == MFVideoFormat_MSS1)
return "MFVideoFormat_MSS1";
else if (subtype == MFVideoFormat_MSS2)
return "MFVideoFormat_MSS2";
else if (subtype == MFVideoFormat_MPG1)
return "MFVideoFormat_MPG1";
else if (subtype == MFVideoFormat_DVSL)
return "MFVideoFormat_DVSL";
else if (subtype == MFVideoFormat_DVSD)
return "MFVideoFormat_DVSD";
else if (subtype == MFVideoFormat_DVHD)
return "MFVideoFormat_DVHD";
else if (subtype == MFVideoFormat_DV25)
return "MFVideoFormat_DV25";
else if (subtype == MFVideoFormat_DV50)
return "MFVideoFormat_DV50";
else if (subtype == MFVideoFormat_DVH1)
return "MFVideoFormat_DVH1";
else if (subtype == MFVideoFormat_DVC)
return "MFVideoFormat_DVC";
else if (subtype == MFVideoFormat_H264)
return "MFVideoFormat_H264";
else if (subtype == MFVideoFormat_H265)
return "MFVideoFormat_H265";
else if (subtype == MFVideoFormat_MJPG)
return "MFVideoFormat_MJPG";
else if (subtype == MFVideoFormat_420O)
return "MFVideoFormat_420O";
else if (subtype == MFVideoFormat_HEVC)
return "MFVideoFormat_HEVC";
else if (subtype == MFVideoFormat_HEVC_ES)
return "MFVideoFormat_HEVC_ES";
else if (subtype == MFVideoFormat_VP80)
return "MFVideoFormat_VP80";
else if (subtype == MFVideoFormat_VP90)
return "MFVideoFormat_VP90";
else if (subtype == MFVideoFormat_ORAW)
return "MFVideoFormat_ORAW";
else
return "Other VideoFormat";
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include <string>
#include <guiddef.h>
#include <system_error>
#include <wil/com.h>
#include <Windows.h>
void LogToFile(std::string what, const bool verbose = false);
void LogToFile(std::wstring what, const bool verbose = false);
std::string toMediaTypeString(GUID subtype);
#define RETURN_IF_FAILED_WITH_LOGGING(val) \
hr = (val); \
if (FAILED(hr)) \
{ \
LogToFile(std::string(__FUNCTION__ "() ") + #val + ": " + std::system_category().message(hr)); \
return hr; \
}
#define RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(val) \
hr = val; \
if (FAILED(hr)) \
{ \
LogToFile(std::string(__FUNCTION__ "() ") + #val + ": " + std::system_category().message(hr)); \
return nullptr; \
}
#define VERBOSE_LOG \
std::string functionNameTMPVAR = __FUNCTION__; \
LogToFile(std::string(functionNameTMPVAR + " enter"), true); \
auto verboseLogOnScopeEnd = wil::scope_exit([&] { \
LogToFile(std::string(functionNameTMPVAR + " exit"), true); \
});
#if defined(PowerToysInterop)
#undef LOG
#define LOG(...)
#else
#define LOG(str) LogToFile(str, false);
#endif
inline bool failed(HRESULT hr)
{
return hr != S_OK;
}
inline bool failed(bool val)
{
return val == false;
}
template<typename T>
inline bool failed(wil::com_ptr_nothrow<T>& ptr)
{
return ptr == nullptr;
}
#define OK_OR_BAIL(expr) \
if (failed(expr)) \
return {};

View File

@@ -0,0 +1,152 @@
#include "MicrophoneDevice.h"
#include "Logging.h"
#include <Functiondiscoverykeys_devpkey.h>
MicrophoneDevice::MicrophoneDevice(wil::com_ptr_nothrow<IMMDevice> device, wil::com_ptr_nothrow<IAudioEndpointVolume> endpoint) :
_device{ std::move(device) },
_endpoint{ std::move(endpoint) }
{
if (!_device || !_endpoint)
{
throw std::logic_error("MicrophoneDevice was initialized with null objects");
}
_device->GetId(&_id);
wil::com_ptr_nothrow<IPropertyStore> props;
_device->OpenPropertyStore(
STGM_READ, &props);
if (props)
{
props->GetValue(PKEY_Device_FriendlyName, &_friendly_name);
}
else
{
LOG("MicrophoneDevice::MicrophoneDevice couldn't open property store");
}
}
MicrophoneDevice::~MicrophoneDevice()
{
if (_notifier)
{
_endpoint->UnregisterControlChangeNotify(_notifier.get());
}
}
bool MicrophoneDevice::active() const noexcept
{
DWORD state = 0;
_device->GetState(&state);
return state == DEVICE_STATE_ACTIVE;
}
void MicrophoneDevice::set_muted(const bool muted) noexcept
{
_endpoint->SetMute(muted, nullptr);
}
bool MicrophoneDevice::muted() const noexcept
{
BOOL muted = FALSE;
_endpoint->GetMute(&muted);
return muted;
}
void MicrophoneDevice::toggle_muted() noexcept
{
set_muted(!muted());
}
std::wstring_view MicrophoneDevice::id() const noexcept
{
return _id ? _id.get() : FALLBACK_ID;
}
std::wstring_view MicrophoneDevice::name() const noexcept
{
return _friendly_name.pwszVal ? _friendly_name.pwszVal : FALLBACK_NAME;
}
void MicrophoneDevice::set_mute_changed_callback(mute_changed_cb_t callback) noexcept
{
_mute_changed_callback = std::move(callback);
_notifier = winrt::make<VolumeNotifier>(this);
_endpoint->RegisterControlChangeNotify(_notifier.get());
}
std::optional<MicrophoneDevice> MicrophoneDevice::getDefault()
{
auto deviceEnumerator = wil::CoCreateInstanceNoThrow<MMDeviceEnumerator, IMMDeviceEnumerator>();
if (!deviceEnumerator)
{
LOG("MicrophoneDevice::getDefault MMDeviceEnumerator returned null");
return std::nullopt;
}
wil::com_ptr_nothrow<IMMDevice> captureDevice;
deviceEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &captureDevice);
if (!captureDevice)
{
LOG("MicrophoneDevice::getDefault captureDevice is null");
return std::nullopt;
}
wil::com_ptr_nothrow<IAudioEndpointVolume> microphoneEndpoint;
captureDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<LPVOID*>(&microphoneEndpoint));
if (!microphoneEndpoint)
{
LOG("MicrophoneDevice::getDefault captureDevice is null");
return std::nullopt;
}
return std::make_optional<MicrophoneDevice>(std::move(captureDevice), std::move(microphoneEndpoint));
}
std::vector<MicrophoneDevice> MicrophoneDevice::getAllActive()
{
std::vector<MicrophoneDevice> microphoneDevices;
auto deviceEnumerator = wil::CoCreateInstanceNoThrow<MMDeviceEnumerator, IMMDeviceEnumerator>();
if (!deviceEnumerator)
{
LOG("MicrophoneDevice::getAllActive MMDeviceEnumerator returned null");
return microphoneDevices;
}
wil::com_ptr_nothrow<IMMDeviceCollection> captureDevices;
deviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &captureDevices);
if (!captureDevices)
{
LOG("MicrophoneDevice::getAllActive EnumAudioEndpoints returned null");
return microphoneDevices;
}
UINT nDevices = 0;
captureDevices->GetCount(&nDevices);
microphoneDevices.reserve(nDevices);
for (UINT i = 0; i < nDevices; ++i)
{
wil::com_ptr_nothrow<IMMDevice> device;
captureDevices->Item(i, &device);
if (!device)
{
continue;
}
wil::com_ptr_nothrow<IAudioEndpointVolume> microphoneEndpoint;
device->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<LPVOID*>(&microphoneEndpoint));
if (!microphoneEndpoint)
{
continue;
}
microphoneDevices.emplace_back(std::move(device), std::move(microphoneEndpoint));
}
return microphoneDevices;
}
MicrophoneDevice::VolumeNotifier::VolumeNotifier(MicrophoneDevice* subscribedDevice) :
_subscribedDevice{ subscribedDevice }
{
}
HRESULT __stdcall MicrophoneDevice::VolumeNotifier::OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA data)
{
_subscribedDevice->_mute_changed_callback(data->bMuted);
return S_OK;
}

View File

@@ -0,0 +1,65 @@
#pragma once
#define NOMINMAX
#include <Windows.h>
#include <Unknwn.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <string_view>
#include <optional>
#include <vector>
#include <functional>
#include <Mmdeviceapi.h>
#include <Endpointvolume.h>
class MicrophoneDevice
{
public:
using mute_changed_cb_t = std::function<void(bool muted)>;
private:
friend struct VolumeNotifier;
struct VolumeNotifier : winrt::implements<VolumeNotifier, IAudioEndpointVolumeCallback>
{
MicrophoneDevice* _subscribedDevice = nullptr;
VolumeNotifier(MicrophoneDevice* subscribedDevice);
virtual HRESULT __stdcall OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA data) override;
};
wil::unique_cotaskmem_string _id;
wil::unique_prop_variant _friendly_name;
mute_changed_cb_t _mute_changed_callback;
winrt::com_ptr<IAudioEndpointVolumeCallback> _notifier;
wil::com_ptr_nothrow<IAudioEndpointVolume> _endpoint;
wil::com_ptr_nothrow<IMMDevice> _device;
constexpr static inline std::wstring_view FALLBACK_NAME = L"Unknown device";
constexpr static inline std::wstring_view FALLBACK_ID = L"UNKNOWN_ID";
public:
MicrophoneDevice(MicrophoneDevice&&) noexcept = default;
MicrophoneDevice(wil::com_ptr_nothrow<IMMDevice> device, wil::com_ptr_nothrow<IAudioEndpointVolume> endpoint);
~MicrophoneDevice();
bool active() const noexcept;
void set_muted(const bool muted) noexcept;
bool muted() const noexcept;
void toggle_muted() noexcept;
std::wstring_view id() const noexcept;
std::wstring_view name() const noexcept;
void set_mute_changed_callback(mute_changed_cb_t callback) noexcept;
static std::optional<MicrophoneDevice> getDefault();
static std::vector<MicrophoneDevice> getAllActive();
};

View File

@@ -0,0 +1,188 @@
#include "SerializedSharedMemory.h"
inline char* SerializedSharedMemory::lock_flag_addr() noexcept
{
return reinterpret_cast<char*>(_memory._data + _memory._size);
}
inline void SerializedSharedMemory::lock() noexcept
{
if (_read_only)
{
return;
}
while (LOCKED == _InterlockedCompareExchange8(lock_flag_addr(), LOCKED, !LOCKED))
{
while (*lock_flag_addr() == LOCKED)
{
_mm_pause();
}
}
}
inline void SerializedSharedMemory::unlock() noexcept
{
if (_read_only)
{
return;
}
_InterlockedExchange8(lock_flag_addr(), !LOCKED);
}
SerializedSharedMemory::SerializedSharedMemory(std::array<wil::unique_handle, 2> handles,
memory_t memory,
const bool readonly) noexcept
:
_handles{ std::move(handles) }, _memory{ std::move(memory) }, _read_only(readonly)
{
}
SerializedSharedMemory::~SerializedSharedMemory() noexcept
{
if (_memory._data)
{
UnmapViewOfFile(_memory._data);
}
}
SerializedSharedMemory::SerializedSharedMemory(SerializedSharedMemory&& rhs) noexcept
{
*this = std::move(rhs);
}
SerializedSharedMemory& SerializedSharedMemory::operator=(SerializedSharedMemory&& rhs) noexcept
{
_handles = {};
_handles.swap(rhs._handles);
_memory = std::move(rhs._memory);
rhs._memory = {};
_read_only = rhs._read_only;
rhs._read_only = true;
return *this;
}
std::optional<SerializedSharedMemory> SerializedSharedMemory::create(const std::wstring_view object_name,
const size_t size,
const bool read_only,
SECURITY_ATTRIBUTES* maybe_attributes) noexcept
{
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa = { sizeof SECURITY_ATTRIBUTES };
if (!maybe_attributes)
{
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = false;
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
!SetSecurityDescriptorDacl(&sd, true, nullptr, false))
{
return std::nullopt;
}
}
// We need an extra byte for locking if it's not readonly
const ULARGE_INTEGER UISize{ .QuadPart = size + !read_only };
wil::unique_handle hMapFile{ CreateFileMappingW(INVALID_HANDLE_VALUE,
maybe_attributes ? maybe_attributes : &sa,
read_only ? PAGE_READONLY : PAGE_READWRITE,
UISize.HighPart,
UISize.LowPart,
object_name.data()) };
if (!hMapFile)
{
return std::nullopt;
}
auto shmem = static_cast<uint8_t*>(
MapViewOfFile(hMapFile.get(), read_only ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, static_cast<SIZE_T>(UISize.QuadPart)));
if (!shmem)
{
return std::nullopt;
}
std::array<wil::unique_handle, 2> handles = { std::move(hMapFile), {} };
return SerializedSharedMemory{ std::move(handles), memory_t{ shmem, size }, read_only };
}
std::optional<SerializedSharedMemory> SerializedSharedMemory::open(const std::wstring_view object_name,
const size_t size,
const bool read_only) noexcept
{
wil::unique_handle hMapFile{ OpenFileMappingW(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, object_name.data()) };
if (!hMapFile)
{
return std::nullopt;
}
auto shmem = static_cast<uint8_t*>(
MapViewOfFile(hMapFile.get(), read_only ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, size + !read_only));
if (!shmem)
{
return std::nullopt;
}
std::array<wil::unique_handle, 2> handles = { std::move(hMapFile), {} };
return SerializedSharedMemory{ std::move(handles), memory_t{ shmem, size }, read_only };
}
std::optional<SerializedSharedMemory> SerializedSharedMemory::create_readonly(
const std::wstring_view object_name,
const std::wstring_view file_path,
SECURITY_ATTRIBUTES* maybe_attributes) noexcept
{
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa = { sizeof SECURITY_ATTRIBUTES };
if (!maybe_attributes)
{
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = false;
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION) ||
!SetSecurityDescriptorDacl(&sd, true, nullptr, false))
{
return std::nullopt;
}
}
wil::unique_handle hFile{ CreateFileW(file_path.data(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
maybe_attributes ? maybe_attributes : &sa,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
nullptr) };
if (!hFile)
{
return std::nullopt;
}
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(hFile.get(), &fileSize))
{
return std::nullopt;
}
wil::unique_handle hMapFile{ CreateFileMappingW(hFile.get(),
maybe_attributes ? maybe_attributes : &sa,
PAGE_READONLY,
fileSize.HighPart,
fileSize.LowPart,
object_name.data()) };
if (!hMapFile)
{
return std::nullopt;
}
auto shmem = static_cast<uint8_t*>(MapViewOfFile(nullptr, FILE_MAP_READ, 0, 0, static_cast<size_t>(fileSize.QuadPart)));
if (shmem)
{
return std::nullopt;
}
std::array<wil::unique_handle, 2> handles = { std::move(hMapFile), std::move(hFile) };
return SerializedSharedMemory{ std::move(handles), memory_t{ shmem, static_cast<size_t>(fileSize.QuadPart) }, true };
}
void SerializedSharedMemory::access(std::function<void(memory_t)> access_routine) noexcept
{
lock();
access_routine(_memory);
unlock();
}

View File

@@ -0,0 +1,54 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <string>
#include <optional>
#include <wil/resource.h>
#include <functional>
#include <array>
// Wrapper class allowing sharing readonly/writable memory with a serialized access via atomic locking.
// Note that it doesn't protect against a 3rd party concurrently modifying physical file contents.
class SerializedSharedMemory
{
public:
struct memory_t
{
uint8_t * _data = nullptr;
size_t _size = 0;
};
static std::optional<SerializedSharedMemory> create(const std::wstring_view object_name,
const size_t size,
const bool read_only,
SECURITY_ATTRIBUTES* maybe_attributes = nullptr) noexcept;
static std::optional<SerializedSharedMemory> create_readonly(
const std::wstring_view object_name,
const std::wstring_view file_path,
SECURITY_ATTRIBUTES* maybe_attributes = nullptr) noexcept;
static std::optional<SerializedSharedMemory> open(const std::wstring_view object_name,
const size_t size,
const bool read_only) noexcept;
void access(std::function<void(memory_t)> access_routine) noexcept;
inline size_t size() const noexcept { return _memory._size; }
~SerializedSharedMemory() noexcept;
SerializedSharedMemory(SerializedSharedMemory&&) noexcept;
SerializedSharedMemory& operator=(SerializedSharedMemory&&) noexcept;
private:
std::array<wil::unique_handle, 2> _handles;
memory_t _memory;
bool _read_only = true;
constexpr static inline int64_t LOCKED = 1;
char* lock_flag_addr() noexcept;
void lock() noexcept;
void unlock() noexcept;
SerializedSharedMemory(std::array<wil::unique_handle, 2> handles, memory_t memory, const bool readonly) noexcept;
};

View File

@@ -0,0 +1,100 @@
#include "VideoCaptureDeviceList.h"
#include "Logging.h"
#include <mfapi.h>
#include <Mfidl.h>
#include <wil/resource.h>
#include <wil/com.h>
void VideoCaptureDeviceList::Clear()
{
for (UINT32 i = 0; i < m_numberDevices; i++)
{
CoTaskMemFree(m_deviceFriendlyNames[i]);
if (m_ppDevices[i])
{
m_ppDevices[i]->Release();
}
}
CoTaskMemFree(m_ppDevices);
m_ppDevices = nullptr;
if (m_deviceFriendlyNames)
{
delete[] m_deviceFriendlyNames;
}
m_deviceFriendlyNames = nullptr;
m_numberDevices = 0;
}
HRESULT VideoCaptureDeviceList::EnumerateDevices()
{
HRESULT hr = S_OK;
wil::com_ptr<IMFAttributes> pAttributes;
Clear();
// Initialize an attribute store. We will use this to
// specify the enumeration parameters.
hr = MFCreateAttributes(&pAttributes, 1);
// Ask for source type = video capture devices
if (SUCCEEDED(hr))
{
hr = pAttributes->SetGUID(
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);
}
else
{
LOG("VideoCaptureDeviceList::EnumerateDevices(): Couldn't MFCreateAttributes");
}
// Enumerate devices.
if (SUCCEEDED(hr))
{
hr = MFEnumDeviceSources(pAttributes.get(), &m_ppDevices, &m_numberDevices);
}
else
{
LOG("VideoCaptureDeviceList::EnumerateDevices(): Couldn't SetGUID");
}
if (FAILED(hr))
{
LOG("VideoCaptureDeviceList::EnumerateDevices(): MFEnumDeviceSources failed");
return hr;
}
m_deviceFriendlyNames = new (std::nothrow) wchar_t*[m_numberDevices];
for (UINT32 i = 0; i < m_numberDevices; i++)
{
UINT32 nameLength = 0;
m_ppDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &m_deviceFriendlyNames[i], &nameLength);
}
return hr;
}
HRESULT VideoCaptureDeviceList::GetDevice(UINT32 index, IMFActivate** ppActivate)
{
if (index >= Count())
{
return E_INVALIDARG;
}
*ppActivate = m_ppDevices[index];
(*ppActivate)->AddRef();
return S_OK;
}
std::wstring_view VideoCaptureDeviceList::GetDeviceName(UINT32 index)
{
if (index >= Count())
{
return {};
}
return m_deviceFriendlyNames[index];
}

View File

@@ -0,0 +1,33 @@
#pragma once
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <Windows.h>
#include <mfobjects.h>
#include <string_view>
class VideoCaptureDeviceList
{
UINT32 m_numberDevices;
// TODO: use wil
IMFActivate** m_ppDevices = nullptr;
wchar_t** m_deviceFriendlyNames = nullptr;
public:
VideoCaptureDeviceList() :
m_ppDevices(NULL), m_numberDevices(0)
{
}
~VideoCaptureDeviceList()
{
Clear();
}
UINT32 Count() const { return m_numberDevices; }
void Clear();
HRESULT EnumerateDevices();
HRESULT GetDevice(UINT32 index, IMFActivate** ppActivate);
std::wstring_view GetDeviceName(UINT32 index);
};

View File

@@ -0,0 +1,140 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" />
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemDefinitionGroup>
<Link>
<AdditionalDependencies>mfplat.lib;Mfsensorgroup.lib;OneCoreUAP.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies);</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{459e0768-7ebd-4c41-bba1-6db3b3815e0a}</ProjectGuid>
<RootNamespace>VideoConferenceShared</RootNamespace>
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Platform)'!='Win32'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='Win32'">
<OutDir>..\..\..\..\x86\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>..\..\..\..\x86\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup>
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<TreatWarningAsError>true</TreatWarningAsError>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;</AdditionalIncludeDirectories>
<DebugInformationFormat Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="CameraStateUpdateChannels.cpp" />
<ClCompile Include="Logging.cpp" />
<ClCompile Include="SerializedSharedMemory.cpp" />
<ClCompile Include="naming.cpp" />
<ClCompile Include="username.cpp" />
<ClCompile Include="MicrophoneDevice.cpp" />
<ClCompile Include="VideoCaptureDeviceList.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="CameraStateUpdateChannels.h" />
<ClInclude Include="Logging.h" />
<ClInclude Include="SerializedSharedMemory.h" />
<ClInclude Include="naming.h" />
<ClInclude Include="MicrophoneDevice.h" />
<ClInclude Include="VideoCaptureDeviceList.h" />
<ClInclude Include="username.h" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200519.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,19 @@
#include "naming.h"
#include "username.h"
std::wstring ObtainStableGlobalNameForKernelObject(const std::wstring_view name, const bool restricted)
{
static const std::optional<std::wstring> username = ObtainActiveUserName();
std::wstring result = L"Global\\";
if (restricted)
{
result += L"Restricted\\";
}
if (username)
{
result += *username;
}
result += name;
return result;
}

View File

@@ -0,0 +1,5 @@
#pragma once
#include <string_view>
#include <string>
std::wstring ObtainStableGlobalNameForKernelObject(const std::wstring_view name, const bool restricted);

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.200519.2" targetFramework="native" />
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
</packages>

View File

@@ -0,0 +1,20 @@
#include "username.h"
#include <Windows.h>
#include <wtsapi32.h>
std::optional<std::wstring> ObtainActiveUserName()
{
const DWORD sessionId = WTSGetActiveConsoleSessionId();
WCHAR* pUserName;
DWORD _ = 0;
if (!WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, &pUserName, &_))
{
return std::nullopt;
}
WTSGetActiveConsoleSessionId();
std::wstring result{ pUserName };
WTSFreeMemory(pUserName);
return result;
}

View File

@@ -0,0 +1,9 @@
#pragma once
#include <optional>
#include <string>
#include <string_view>
std::optional<std::wstring> ObtainActiveUserName();
std::wstring ObtainStableGlobalNameForKernelObject(const std::wstring_view name, const bool restricted);