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
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
116 changed files with 7425 additions and 81 deletions

View file

@ -46,6 +46,7 @@ Aissue
akamaihd
ALarger
alekhyareddy
alignas
ALIGNLEFT
ALLAPPS
Alloc
@ -142,9 +143,11 @@ autoplay
Autorun
AUTOSIZECOLUMNS
autoupdate
awakeversion
AValid
avialable
awakeversion
AWAYMODE
AYUV
azurecr
azurewebsites
backend
@ -185,6 +188,7 @@ bms
BNumber
Bokm
BOKMAL
boolalpha
Bools
bootstrapper
Bopomofo
@ -213,6 +217,7 @@ BValue
bytearray
callbackptr
callhistory
Camer
Cangjie
cangjieime
CANRENAME
@ -285,6 +290,7 @@ CMock
CMONITORS
cmyk
cnt
coc
coclass
codebase
codecvt
@ -293,9 +299,10 @@ codereview
Codespaces
COINIT
colorconv
colorfilter
colorhistory
colorhistorylimit
colorfilter
COLORKEY
colorpicker
COLORREF
colorscheme
@ -429,6 +436,7 @@ dcomp
DComposition
ddd
ddee
ddf
Deact
declspec
decltype
@ -472,11 +480,14 @@ devenum
deviceencryption
devicemanagenent
DEVMON
devpkey
DEVSOURCE
DFactory
DHCP
Dialpad
diffing
difftime
DIIRFLAG
dimm
directaccess
dirname
@ -489,6 +500,7 @@ Displayandhidethedesktop
DISPLAYCHANGE
displayname
divyan
djsoref
DLACTIVEXCTLS
DLCONTROL
dlg
@ -501,9 +513,10 @@ dllexport
dllhost
dllmain
DNLEN
docsmsft
Dns
docsmsft
doctype
dogancelik
domainlexicon
DONTVALIDATEPATH
dotnet
@ -531,7 +544,11 @@ dupenv
dutil
DVASPECT
DVASPECTINFO
DVH
DVHD
DVR
DVSD
DVSL
DVTARGETDEVICE
DWindow
DWINRT
@ -550,6 +567,8 @@ dword
dworigin
dwrite
dxgi
dxgiformat
dxguid
dynamiclock
EABF
EAC
@ -602,6 +621,7 @@ efa
efgh
EFile
egistry
elif
elseif
emailandaccounts
Emoji
@ -610,6 +630,7 @@ ENABLEDPOPUP
endforeach
endif
endl
endpointvolume
endregion
Enque
ENTERSIZEMOVE
@ -662,7 +683,7 @@ exlist
EXPCMDFLAGS
EXPCMDSTATE
explr
Expr
expr
exsb
EXSEL
exstyle
@ -679,6 +700,7 @@ FANCYZONESDRAWLAYOUTTEST
FANCYZONESEDITOR
Farbraum
FARPROC
fdw
feimage
ffcd
FFDDDDDD
@ -690,13 +712,16 @@ FILEFLAGS
FILEFLAGSMASK
FILEOP
FILEOS
filepath
FILESUBTYPE
FILESYSPATH
filesystem
FILETIME
FILETYPE
FILEVERSION
Filtergraph
Filterkeyboard
Filterx
finalizer
findfast
findmydevice
@ -705,6 +730,7 @@ FIXEDFILEINFO
FLASHZONES
FLASHZONESONQUICKSWITCH
Fle
flt
fluentui
flyout
fmtlib
@ -726,6 +752,7 @@ FTYPE
FULLNAME
fullscreen
func
Functiondiscoverykeys
fwlink
fwrite
fxcop
@ -736,6 +763,7 @@ Gamebar
gamedvr
gamemode
GBs
GCLP
gcnew
gdi
gdiplus
@ -778,8 +806,10 @@ Hashset
hbitmap
hbmp
hbr
HBRBACKGROUND
HBRUSH
hcblack
HCERTSTORE
hcwhite
hdc
HDF
@ -791,6 +821,8 @@ hdrop
HDS
HEB
helptext
HEVC
hfile
HGLOBAL
hhk
HHmmss
@ -865,11 +897,13 @@ IApp
IApplication
IAppx
IAsync
IAudio
IAuto
IBackground
IBase
IBeam
IBind
ICapture
icase
iccex
ICEBLUE
@ -922,9 +956,11 @@ IFancy
ifdef
IFeatures
IFile
IFilter
ifndef
IFolder
ifstream
IGraph
iid
IImage
Iindex
@ -944,8 +980,11 @@ imagingdevices
IMain
IMarkdown
ime
IMedia
IMem
imeutil
img
iminstall
IMoniker
IMonitor
IMouse
@ -1002,6 +1041,7 @@ IObject
iobjectwithsitesetsite
IOle
iolewindowcontextsensitivehelp
iomanip
iostream
IPackage
IPath
@ -1023,6 +1063,7 @@ IProperty
IPublic
IQuery
IRead
IReference
IReflect
IRegistered
IRegistration
@ -1055,6 +1096,7 @@ ith
IThrottled
IThumbnail
ITrigger
itsme
IUI
IUnknown
IUri
@ -1065,9 +1107,11 @@ IVector
IView
IVirtual
IWeb
IXml
IWIC
IWindows
IXml
ixx
IYUV
IZone
IZoom
JArray
@ -1116,17 +1160,17 @@ Keytool
keyup
KILLFOCUS
Knownfolders
KSPROPERTY
Kybd
LAlt
Lambson
lamotile
langword
langword
Lastdevice
LASTEXITCODE
Laute
launchfaceenrollment
launchfingerprintenrollment
Laute
laute
laviusmotileng
LAYOUTRTL
@ -1140,9 +1184,11 @@ Lclean
LCONTROL
LCtrl
Ldone
ldx
LEFTSCROLLBAR
lego
len
LEQ
LError
Lessthan
LEVELID
@ -1166,6 +1212,7 @@ LINQTo
Linux
listbox
listview
lld
llkhf
Llvm
lmcons
@ -1246,6 +1293,7 @@ MAINICON
Mainwindow
majortype
makeappx
makecab
MAKEINTRESOURCE
MAKEINTRESOURCEW
MAKELPARAM
@ -1264,6 +1312,7 @@ MATCHMODE
MAXIMIZEBOX
MAXSHORTCUTSIZE
maxversiontested
MBs
MBUTTON
MBUTTONDBLCLK
MBUTTONDOWN
@ -1273,7 +1322,7 @@ MDICHILD
MDL
mdpreviewhandler
MEDIASUBTYPE
MEDIATYPE
mediatype
Melman
memcpy
memset
@ -1285,8 +1334,17 @@ messageboxes
METACHARSET
metadata
metafile
mfapi
mfc
mfcribbon
mfidl
mfobjects
mfplat
mfreadwrite
Mfsensorgroup
mftransform
mfuuid
mic
microsoft
Midl
mii
@ -1299,10 +1357,12 @@ miniz
minlevel
MINMAXINFO
Miracast
mirophone
MJPG
mkdir
Mlcfg
MLogo
mmdeviceapi
MMI
Mmsys
mobilehotspot
@ -1369,6 +1429,7 @@ mutex
mutexes
muxc
mvvm
myfile
MYICON
NAMECHANGE
nameof
@ -1412,6 +1473,7 @@ netsh
netstandard
Neue
newcolor
newdev
newitem
newpath
newrow
@ -1421,6 +1483,7 @@ niels
nielslaute
NIF
nightlight
nitroin
NLD
nlog
NLSTEXT
@ -1438,6 +1501,7 @@ nodoc
noexcept
NOFRAMES
NOINHERITLAYOUT
NOINTERFACE
NOLINKINFO
NOMINMAX
NOMOVE
@ -1521,6 +1585,7 @@ OPTIMIZEFORINVOKE
optin
optionalfeatures
OPTIONSGROUP
ORAW
ORPHANEDDIALOGTITLE
oss
ostr
@ -1532,6 +1597,7 @@ otheroptions
otherusers
OUTOFCONTEXT
OUTOFMEMORY
outpin
Outptr
outputtype
outro
@ -1553,6 +1619,7 @@ PARENTRELATIVEPARSING
parray
PARTIALCONFIRMATIONDIALOGTITLE
pathcch
PAUDIO
pbc
Pbgra
pcb
@ -1593,6 +1660,7 @@ Pipelinhttps
pipename
pitem
PKBDLLHOOKSTRUCT
PKEY
placeholders
plib
PLK
@ -1611,6 +1679,7 @@ popd
popup
POPUPWINDOW
posix
Postion
powerappscds
powercfg
powerlauncher
@ -1626,6 +1695,7 @@ powertoyswiki
Powrprof
ppenum
ppidl
ppmt
pprm
pproc
ppsi
@ -1643,11 +1713,13 @@ Prefixer
Preinstalled
preload
PREMULTIPLIED
preperty
prevhost
previewer
PREVIEWGROUP
PREVIEWHANDLERFRAMEINFO
previewpane
previouscamera
PREVIOUSVERSIONSINSTALLED
prevpane
prgms
@ -1664,6 +1736,7 @@ PROGRAMFILES
progressbar
Proj
projectname
PROPBAG
propkey
propvarutil
prpui
@ -1676,6 +1749,7 @@ psfgao
Psr
psrm
psrree
pstr
pstream
pstrm
psz
@ -1693,6 +1767,7 @@ PVOID
pwa
pwcs
PWSTR
pwsz
pwtd
qianlifeng
qit
@ -1738,20 +1813,27 @@ rectp
rects
recyclebin
redirectedfrom
reencode
reencoded
refactor
refactoring
REFCLSID
refcount
REFGUID
REFIID
REGCLS
regedit
regex
REGFILTER
REGFILTERPINS
regionformatting
regionlanguage
REGISTERCLASSFAILED
Registery
registrypath
regkey
REGPINTYPES
regsvr
reimplementing
reloadable
Remapper
@ -1803,13 +1885,14 @@ RKey
RMENU
RNumber
roadmap
robocopy
Roboto
roslyn
royvou
Rpc
RRF
rshift
RSHIFT
rshift
Rsp
rst
Rstrtmgr
@ -1859,6 +1942,7 @@ SEARCHFOR
SEARCHREPLACEGROUP
searchterm
Secur
seekg
Segoe
Sekan
SENDCHANGE
@ -1904,8 +1988,8 @@ Shl
shldisp
shlobj
shlwapi
shmem
shobjidl
shortsplit
SHORTCUTATLEAST
shortcutcontrol
Shortcutguide
@ -1917,6 +2001,7 @@ SHORTCUTSTARTWITHMODIFIER
Shortcuttool
shortdate
SHORTPATH
shortsplit
showcolorname
SHOWDEFAULT
SHOWELEVATIONPROMPT
@ -1945,6 +2030,7 @@ SIZENESW
SIZENS
SIZENWSE
sizeof
sizeread
SIZEWE
sketchapp
SKIPOWNPROCESS
@ -1974,6 +2060,7 @@ spesi
splitwstring
sppd
sppre
sprintf
spsi
spsia
spsrif
@ -2031,6 +2118,7 @@ storagesense
stoul
stoull
strcmp
streampos
strftime
Stringified
Stringify
@ -2057,8 +2145,8 @@ surfacehub
sut
SVE
svg
SVGIO
SVGIn
SVGIO
svgpreviewhandler
SWC
SWFO
@ -2068,6 +2156,7 @@ swprintf
SWRESTORE
SYMED
SYMOPT
SYNCMFT
SYNCPAINT
sys
SYSCHAR
@ -2083,9 +2172,9 @@ syslog
SYSMENU
systemd
SYSTEMTIME
Tadele
sz
tabletmode
Tadele
tadele
Tahoma
talenthrcore
@ -2111,6 +2200,7 @@ tchar
tcscpy
TCustom
TDevice
tellg
Templated
templatenamespace
Temporarilypeekatthedesktop
@ -2132,8 +2222,8 @@ THISCOMPONENT
thre
tif
TILEDWINDOW
timediff
timedate
timediff
Timeline
TIMERID
timeunion
@ -2143,6 +2233,7 @@ TLayout
tlb
tlbimp
tmp
TMPVAR
TNP
todo
toggleswitch
@ -2161,12 +2252,15 @@ towlower
towupper
tracelogging
traies
transcoded
transparrent
TRAYMOUSEMESSAGE
TRK
trl
trueplay
truetype
trunc
tspan
TStr
tsx
TYMED
@ -2192,8 +2286,10 @@ UIPI
UIs
UITo
ULARGE
ulazy
ULLONG
ulong
ULONGLONG
umd
unchecks
uncomment
@ -2222,6 +2318,7 @@ unknwn
UNLEN
unlicense
Unmap
unmute
UNORM
unregister
unregistering
@ -2256,16 +2353,20 @@ uuidof
uwp
UWPUI
uxtheme
UYVY
validmodulename
vcamp
vccorlib
vcdl
VCINSTALLDIR
vcm
vcomp
vcredist
VCRT
vcruntime
vcvars
vcxproj
vdi
VDId
vec
VERBSONLY
@ -2275,10 +2376,14 @@ VERSIONINFO
Versioning
VFT
vid
VIDCAP
videoconference
videoconferencevirtualdriver
VIDEOINFOHEADER
videoplayback
viewbox
viewmodel
vih
virtualization
visiblecolorformats
Visibletrue
@ -2298,6 +2403,7 @@ VSCBD
vscode
VSCROLL
vse
vsix
vsonline
vstemplate
VSTHRD
@ -2320,6 +2426,9 @@ wcscpy
wcslen
wcsncmp
wcsnicmp
WDK
wdksetup
wdkvsix
wdp
wdupenv
We'd
@ -2346,6 +2455,8 @@ wikipedia
wil
wildcards
winapi
wincodec
Wincodecsdk
wincolor
windef
windevbuildagents
@ -2401,6 +2512,7 @@ WMKEYUP
wmp
WMSYSKEYDOWN
WMSYSKEYUP
WMV
wnd
WNDCLASS
WNDCLASSEX
@ -2431,9 +2543,11 @@ wstringstream
wsz
wtoi
WTS
wtsapi
WTSAT
wu
wubi
WVC
Wwan
www
wxs
@ -2448,6 +2562,7 @@ XBUTTON
XBUTTONDBLCLK
XBUTTONDOWN
XBUTTONUP
xcopy
XDiff
XDocument
XElement
@ -2479,7 +2594,10 @@ yourinfo
YourUserName
YStr
YUY
yuyoyuppe
YUYV
YVU
YVYU
ZEROINIT
ZIndex
zipfolder
@ -2491,9 +2609,3 @@ ZONESETCHANGE
Zoneszonabletester
Zoomusingmagnifier
zzz
coc
djsoref
dogancelik
itsme
nitroin
ulazy

View file

@ -7,3 +7,5 @@ set SolutionDir=%cd%
popd
SET IsPipeline=1
call msbuild ../tools/BugReportTool/BugReportTool.sln /p:Configuration=Release /p:Platform=x64 /p:CIBuild=true || exit /b 1
call msbuild ../tools/WebcamReportTool/WebcamReportTool.sln /p:Configuration=Release /p:Platform=x64 /p:CIBuild=true || exit /b 1

View file

@ -61,6 +61,11 @@ build:
- 'x64/**/*.pdb'
exclude:
- 'x64/Release/obj/**/*.pdb'
# TODO(yuyoyuppe): uncomment when VCM should be enabled
#- from: 'x86/Release'
# to: 'Build_Output'
# include:
# - 'modules\VideoConference\VideoConferenceProxyFilter_x86.dll'
- from: 'x64/Release'
to: 'Build_Output'
include:
@ -159,6 +164,9 @@ build:
- 'modules\PowerRename\PowerRenameExt.dll'
- 'modules\ShortcutGuide\ShortcutGuide\PowerToys.ShortcutGuide.exe'
- 'modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.dll'
# TODO(yuyoyuppe): uncomment when VCM should be enabled
#- 'modules\VideoConference\VideoConferenceModule.dll'
#- 'modules\VideoConference\VideoConferenceProxyFilter_x64.dll'
- 'Settings\ManagedTelemetry.dll'
- 'Settings\Microsoft.PowerToys.Settings.UI.exe'
- 'Settings\Microsoft.PowerToys.Settings.UI.Lib.dll'
@ -177,6 +185,7 @@ build:
to: 'Build_Output'
include:
- 'BugReportTool\BugReportTool.exe'
- 'WebcamReportTool\WebcamReportTool.exe'
signing_options:
sign_inline: true # This does signing a soon as this command completes
- !!buildcommand
@ -237,4 +246,3 @@ static_analysis_options:
files_to_scan:
- exclude:
- '**/*.lcl'

View file

@ -1,3 +1,4 @@
cd /D "%~dp0"
nuget restore ../tools/BugReportTool/BugReportTool.sln || exit /b 1
nuget restore ../tools/WebcamReportTool/WebcamReportTool.sln || exit /b 1

View file

@ -1,3 +1,12 @@
cd /D "%~dp0"
nuget restore ../PowerToys.sln || exit /b 1
powershell.exe -Command "Invoke-WebRequest -OutFile %tmp%\wdksetup.exe https://go.microsoft.com/fwlink/p/?linkid=2085767"
%tmp%\wdksetup.exe /q
copy "C:\Program Files (x86)\Windows Kits\10\Vsix\VS2019\WDK.vsix" %tmp%\wdkvsix.zip
powershell Expand-Archive %tmp%\wdkvsix.zip -DestinationPath %tmp%\wdkvsix -Force
robocopy /e %tmp%\wdkvsix\$MSBuild\Microsoft\VC\v160 "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160" || IF %ERRORLEVEL% LEQ 7 EXIT 0
robocopy /e %tmp%\wdkvsix\$VCTargets "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\VCTargets" || IF %ERRORLEVEL% LEQ 7 EXIT 0

View file

@ -75,7 +75,7 @@
<!-- Props that are constant for both Debug and Release configurations -->
<PropertyGroup Label="Configuration">
<PlatformToolset>v142</PlatformToolset>
<PlatformToolset Condition="'$(OverridePlatformToolset)'!='True'">v142</PlatformToolset>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>

View file

@ -333,7 +333,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Community.PowerToys.Run.Plugin.UnitConverter.UnitTest", "src\modules\launcher\Plugins\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest\Community.PowerToys.Run.Plugin.UnitConverter.UnitTest.csproj", "{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}"
EndProject
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shortcutguide", "shortcutguide", "{106CBECA-0701-4FC3-838C-9DF816A19AE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuideModuleInterface", "src\modules\ShortcutGuide\ShortcutGuideModuleInterface\ShortcutGuideModuleInterface.vcxproj", "{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}"
@ -342,376 +341,597 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ShortcutGuide", "src\module
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZonesModuleInterface", "src\modules\fancyzones\FancyZonesModuleInterface\FancyZonesModuleInterface.vcxproj", "{48804216-2A0E-4168-A6D8-9CD068D14227}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZones", "src\modules\fancyzones\FancyZones\FancyZones.vcxproj", "{390AE700-B55F-4202-91EA-A822EB75B9BD}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FancyZones", "src\modules\fancyzones\FancyZones\FancyZones.vcxproj", "{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToys.Update", "src\Update\PowerToys.Update.vcxproj", "{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.WindowsSettings", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj", "{5043CECE-E6A7-4867-9CBE-02D27D83747A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceShared", "src\modules\videoconference\VideoConferenceShared\VideoConferenceShared.vcxproj", "{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceModule", "src\modules\videoconference\VideoConferenceModule\Video Conference.vcxproj", "{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceProxyFilter", "src\modules\videoconference\VideoConferenceProxyFilter\VideoConferenceProxyFilter.vcxproj", "{AC2857B4-103D-4D6D-9740-926EBF785042}"
ProjectSection(ProjectDependencies) = postProject
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VideoConference", "VideoConference", "{470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.ActiveCfg = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x64.Build.0 = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Debug|x86.ActiveCfg = Debug|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.ActiveCfg = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x64.Build.0 = Release|x64
{9412D5C6-2CF2-4FC2-A601-B55508EA9B27}.Release|x86.ActiveCfg = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x64.ActiveCfg = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x64.Build.0 = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Debug|x86.ActiveCfg = Debug|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x64.ActiveCfg = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x64.Build.0 = Release|x64
{F9C68EDF-AC74-4B77-9AF1-005D9C9F6A99}.Release|x86.ActiveCfg = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.ActiveCfg = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x64.Build.0 = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Debug|x86.ActiveCfg = Debug|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.ActiveCfg = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x64.Build.0 = Release|x64
{9C6A7905-72D4-4BF5-B256-ABFDAEF68AE9}.Release|x86.ActiveCfg = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.ActiveCfg = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x64.Build.0 = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Debug|x86.ActiveCfg = Debug|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.ActiveCfg = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x64.Build.0 = Release|x64
{1A066C63-64B3-45F8-92FE-664E1CCE8077}.Release|x86.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x64.Build.0 = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Debug|x86.ActiveCfg = Debug|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.ActiveCfg = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x64.Build.0 = Release|x64
{5CCC8468-DEC8-4D36-99D4-5C891BEBD481}.Release|x86.ActiveCfg = Release|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x64.ActiveCfg = Debug|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x64.Build.0 = Debug|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Debug|x86.ActiveCfg = Debug|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x64.ActiveCfg = Release|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x64.Build.0 = Release|x64
{B25AC7A5-FB9F-4789-B392-D5C85E948670}.Release|x86.ActiveCfg = Release|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x64.ActiveCfg = Debug|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x64.Build.0 = Debug|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Debug|x86.ActiveCfg = Debug|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.ActiveCfg = Release|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x64.Build.0 = Release|x64
{51920F1F-C28C-4ADF-8660-4238766796C2}.Release|x86.ActiveCfg = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.ActiveCfg = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x64.Build.0 = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Debug|x86.ActiveCfg = Debug|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.ActiveCfg = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x64.Build.0 = Release|x64
{0E072714-D127-460B-AFAD-B4C40B412798}.Release|x86.ActiveCfg = Release|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.ActiveCfg = Debug|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x64.Build.0 = Debug|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Debug|x86.ActiveCfg = Debug|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x64.ActiveCfg = Release|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x64.Build.0 = Release|x64
{A3935CF4-46C5-4A88-84D3-6B12E16E6BA2}.Release|x86.ActiveCfg = Release|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x64.ActiveCfg = Debug|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x64.Build.0 = Debug|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Debug|x86.ActiveCfg = Debug|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x64.ActiveCfg = Release|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x64.Build.0 = Release|x64
{2151F984-E006-4A9F-92EF-C6DDE3DC8413}.Release|x86.ActiveCfg = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x64.ActiveCfg = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x64.Build.0 = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Debug|x86.ActiveCfg = Debug|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x64.ActiveCfg = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x64.Build.0 = Release|x64
{64A80062-4D8B-4229-8A38-DFA1D7497749}.Release|x86.ActiveCfg = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.ActiveCfg = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x64.Build.0 = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Debug|x86.ActiveCfg = Debug|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.ActiveCfg = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x64.Build.0 = Release|x64
{0485F45C-EA7A-4BB5-804B-3E8D14699387}.Release|x86.ActiveCfg = Release|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x64.ActiveCfg = Debug|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x64.Build.0 = Debug|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Debug|x86.ActiveCfg = Debug|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x64.ActiveCfg = Release|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x64.Build.0 = Release|x64
{89F34AF7-1C34-4A72-AA6E-534BCF972BD9}.Release|x86.ActiveCfg = Release|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.ActiveCfg = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x64.Build.0 = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Debug|x86.ActiveCfg = Debug|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.ActiveCfg = Release|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x64.Build.0 = Release|x64
{2BE46397-4DFA-414C-9BD4-41E4BBF8CB34}.Release|x86.ActiveCfg = Release|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x64.ActiveCfg = Debug|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x64.Build.0 = Debug|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Debug|x86.ActiveCfg = Debug|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x64.ActiveCfg = Release|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x64.Build.0 = Release|x64
{0B43679E-EDFA-4DA0-AD30-F4628B308B1B}.Release|x86.ActiveCfg = Release|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.ActiveCfg = Debug|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x64.Build.0 = Debug|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Debug|x86.ActiveCfg = Debug|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.ActiveCfg = Release|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x64.Build.0 = Release|x64
{E0CC7526-D85E-43AC-844F-D5DF0D2F5AB8}.Release|x86.ActiveCfg = Release|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.ActiveCfg = Debug|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x64.Build.0 = Debug|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Debug|x86.ActiveCfg = Debug|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.ActiveCfg = Release|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x64.Build.0 = Release|x64
{D29DDD63-E2CF-4657-9FD5-2AEDE4257E5D}.Release|x86.ActiveCfg = Release|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.ActiveCfg = Debug|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x64.Build.0 = Debug|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Debug|x86.ActiveCfg = Debug|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.ActiveCfg = Release|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x64.Build.0 = Release|x64
{17DA04DF-E393-4397-9CF0-84DABE11032E}.Release|x86.ActiveCfg = Release|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|x64.ActiveCfg = Debug|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|x64.Build.0 = Debug|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Debug|x86.ActiveCfg = Debug|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|x64.ActiveCfg = Release|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|x64.Build.0 = Release|x64
{8AFFA899-0B73-49EC-8C50-0FADDA57B2FC}.Release|x86.ActiveCfg = Release|x64
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.ActiveCfg = Debug|x64
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x64.Build.0 = Debug|x64
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Debug|x86.ActiveCfg = Debug|x64
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.ActiveCfg = Release|x64
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x64.Build.0 = Release|x64
{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}.Release|x86.ActiveCfg = Release|x64
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|x64.ActiveCfg = Debug|x64
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|x64.Build.0 = Debug|x64
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Debug|x86.ActiveCfg = Debug|x64
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.ActiveCfg = Release|x64
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x64.Build.0 = Release|x64
{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}.Release|x86.ActiveCfg = Release|x64
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|x64
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|x64
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|x64
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|x64.ActiveCfg = Release|x64
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|x64.Build.0 = Release|x64
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Release|x86.ActiveCfg = Release|x64
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x64.ActiveCfg = Debug|x64
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x64.Build.0 = Debug|x64
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Debug|x86.ActiveCfg = Debug|x64
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.ActiveCfg = Release|x64
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.Build.0 = Release|x64
{59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.ActiveCfg = Release|x64
{4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Debug|x64.ActiveCfg = Debug|x64
{4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Debug|x64.Build.0 = Debug|x64
{4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Debug|x86.ActiveCfg = Debug|x64
{4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Release|x64.ActiveCfg = Release|x64
{4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Release|x64.Build.0 = Release|x64
{4D971245-7A70-41D5-BAA0-DDB5684CAF51}.Release|x86.ActiveCfg = Release|x64
{74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x64.ActiveCfg = Debug|x64
{74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x64.Build.0 = Debug|x64
{74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Debug|x86.ActiveCfg = Debug|x64
{74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x64.ActiveCfg = Release|x64
{74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x64.Build.0 = Release|x64
{74F1B9ED-F59C-4FE7-B473-7B453E30837E}.Release|x86.ActiveCfg = Release|x64
{FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|x64.ActiveCfg = Debug|x64
{FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|x64.Build.0 = Debug|x64
{FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Debug|x86.ActiveCfg = Debug|x64
{FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|x64.ActiveCfg = Release|x64
{FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|x64.Build.0 = Release|x64
{FDB3555B-58EF-4AE6-B5F1-904719637AB4}.Release|x86.ActiveCfg = Release|x64
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.ActiveCfg = Debug|x64
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x64.Build.0 = Debug|x64
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Debug|x86.ActiveCfg = Debug|x64
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|x64.ActiveCfg = Release|x64
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|x64.Build.0 = Release|x64
{C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0}.Release|x86.ActiveCfg = Release|x64
{F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|x64.ActiveCfg = Debug|x64
{F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|x64.Build.0 = Debug|x64
{F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Debug|x86.ActiveCfg = Debug|x64
{F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|x64.ActiveCfg = Release|x64
{F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|x64.Build.0 = Release|x64
{F8B870EB-D5F5-45BA-9CF7-A5C459818820}.Release|x86.ActiveCfg = Release|x64
{E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|x64.ActiveCfg = Debug|x64
{E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|x64.Build.0 = Debug|x64
{E364F67B-BB12-4E91-B639-355866EBCD8B}.Debug|x86.ActiveCfg = Debug|x64
{E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x64.ActiveCfg = Release|x64
{E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x64.Build.0 = Release|x64
{E364F67B-BB12-4E91-B639-355866EBCD8B}.Release|x86.ActiveCfg = Release|x64
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|x64.ActiveCfg = Debug|x64
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|x64.Build.0 = Debug|x64
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Debug|x86.ActiveCfg = Debug|x64
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|x64.ActiveCfg = Release|x64
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|x64.Build.0 = Release|x64
{F97E5003-F263-4D4A-A964-0F1F3C82DEF2}.Release|x86.ActiveCfg = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.ActiveCfg = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.Build.0 = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x86.ActiveCfg = Debug|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.ActiveCfg = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.Build.0 = Release|x64
{AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x86.ActiveCfg = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.ActiveCfg = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.Build.0 = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x86.ActiveCfg = Debug|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.ActiveCfg = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.Build.0 = Release|x64
{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x86.ActiveCfg = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.ActiveCfg = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.Build.0 = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x86.ActiveCfg = Debug|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.ActiveCfg = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.Build.0 = Release|x64
{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x86.ActiveCfg = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.ActiveCfg = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x64.Build.0 = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Debug|x86.ActiveCfg = Debug|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.ActiveCfg = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x64.Build.0 = Release|x64
{DA425894-6E13-404F-8DCB-78584EC0557A}.Release|x86.ActiveCfg = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.ActiveCfg = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x64.Build.0 = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Debug|x86.ActiveCfg = Debug|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.ActiveCfg = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x64.Build.0 = Release|x64
{060D75DA-2D1C-48E6-A4A1-6F0718B64661}.Release|x86.ActiveCfg = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.ActiveCfg = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x64.Build.0 = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Debug|x86.ActiveCfg = Debug|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.ActiveCfg = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x64.Build.0 = Release|x64
{748417CA-F17E-487F-9411-CAFB6D3F4877}.Release|x86.ActiveCfg = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.ActiveCfg = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x64.Build.0 = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Debug|x86.ActiveCfg = Debug|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.ActiveCfg = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x64.Build.0 = Release|x64
{217DF501-135C-4E38-BFC8-99D4821032EA}.Release|x86.ActiveCfg = Release|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.ActiveCfg = Debug|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x64.Build.0 = Debug|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Debug|x86.ActiveCfg = Debug|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.ActiveCfg = Release|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x64.Build.0 = Release|x64
{47310AB4-9034-4BD1-8D8B-E88AD21A171B}.Release|x86.ActiveCfg = Release|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x64.ActiveCfg = Debug|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x64.Build.0 = Debug|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x86.ActiveCfg = Debug|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x86.Build.0 = Debug|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Debug|x86.Deploy.0 = Debug|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x64.ActiveCfg = Release|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x64.Build.0 = Release|x64
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x86.ActiveCfg = Release|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x86.Build.0 = Release|Win32
{A7D5099E-F0FD-4BF3-8522-5A682759F915}.Release|x86.Deploy.0 = Release|Win32
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x64.ActiveCfg = Debug|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x64.Build.0 = Debug|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Debug|x86.ActiveCfg = Debug|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x64.ActiveCfg = Release|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x64.Build.0 = Release|x64
{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}.Release|x86.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x64.Build.0 = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Debug|x86.ActiveCfg = Debug|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.ActiveCfg = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64
{F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x64.ActiveCfg = Debug|x64
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x64.Build.0 = Debug|x64
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Debug|x86.ActiveCfg = Debug|x64
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x64.ActiveCfg = Release|x64
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x64.Build.0 = Release|x64
{787B8AA6-CA93-4C84-96FE-DF31110AD1C4}.Release|x86.ActiveCfg = Release|x64
{08C8C05F-0362-41BC-818C-724572DF8B06}.Debug|x64.ActiveCfg = Debug|x64
{08C8C05F-0362-41BC-818C-724572DF8B06}.Debug|x64.Build.0 = Debug|x64
{08C8C05F-0362-41BC-818C-724572DF8B06}.Debug|x86.ActiveCfg = Debug|x64
{08C8C05F-0362-41BC-818C-724572DF8B06}.Release|x64.ActiveCfg = Release|x64
{08C8C05F-0362-41BC-818C-724572DF8B06}.Release|x64.Build.0 = Release|x64
{08C8C05F-0362-41BC-818C-724572DF8B06}.Release|x86.ActiveCfg = Release|x64
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Debug|x64.ActiveCfg = Debug|x64
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Debug|x64.Build.0 = Debug|x64
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Debug|x86.ActiveCfg = Debug|x64
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.ActiveCfg = Release|x64
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x64.Build.0 = Release|x64
{5D00D290-4016-4CFE-9E41-1E7C724509BA}.Release|x86.ActiveCfg = Release|x64
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.ActiveCfg = Debug|x64
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x64.Build.0 = Debug|x64
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Debug|x86.ActiveCfg = Debug|x64
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Release|x64.ActiveCfg = Release|x64
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Release|x64.Build.0 = Release|x64
{4AED67B6-55FD-486F-B917-E543DEE2CB3C}.Release|x86.ActiveCfg = Release|x64
{42851751-CBC8-45A6-97F5-7A0753F7B4D1}.Debug|x64.ActiveCfg = Debug|x64
{42851751-CBC8-45A6-97F5-7A0753F7B4D1}.Debug|x64.Build.0 = Debug|x64
{42851751-CBC8-45A6-97F5-7A0753F7B4D1}.Debug|x86.ActiveCfg = Debug|x64
{42851751-CBC8-45A6-97F5-7A0753F7B4D1}.Release|x64.ActiveCfg = Release|x64
{42851751-CBC8-45A6-97F5-7A0753F7B4D1}.Release|x64.Build.0 = Release|x64
{42851751-CBC8-45A6-97F5-7A0753F7B4D1}.Release|x86.ActiveCfg = Release|x64
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}.Debug|x64.ActiveCfg = Debug|x64
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}.Debug|x64.Build.0 = Debug|x64
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}.Debug|x86.ActiveCfg = Debug|x64
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}.Release|x64.ActiveCfg = Release|x64
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}.Release|x64.Build.0 = Release|x64
{1EF1EEF0-10F0-4F2E-8550-39B6D8044D3E}.Release|x86.ActiveCfg = Release|x64
{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}.Debug|x64.ActiveCfg = Debug|x64
{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}.Debug|x64.Build.0 = Debug|x64
{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}.Debug|x86.ActiveCfg = Debug|x64
{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}.Release|x64.ActiveCfg = Release|x64
{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}.Release|x64.Build.0 = Release|x64
{8FFE09DA-FA4F-4EE1-B3A2-AD5497FBD1AD}.Release|x86.ActiveCfg = Release|x64
{655C9AF2-18D3-4DA6-80E4-85504A7722BA}.Debug|x64.ActiveCfg = Debug|x64
{655C9AF2-18D3-4DA6-80E4-85504A7722BA}.Debug|x64.Build.0 = Debug|x64
{655C9AF2-18D3-4DA6-80E4-85504A7722BA}.Debug|x86.ActiveCfg = Debug|x64
{655C9AF2-18D3-4DA6-80E4-85504A7722BA}.Release|x64.ActiveCfg = Release|x64
{655C9AF2-18D3-4DA6-80E4-85504A7722BA}.Release|x64.Build.0 = Release|x64
{655C9AF2-18D3-4DA6-80E4-85504A7722BA}.Release|x86.ActiveCfg = Release|x64
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Debug|x64.ActiveCfg = Debug|x64
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Debug|x64.Build.0 = Debug|x64
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Debug|x86.ActiveCfg = Debug|x64
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Release|x64.ActiveCfg = Release|x64
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Release|x64.Build.0 = Release|x64
{BA58206B-1493-4C75-BFEA-A85768A1E156}.Release|x86.ActiveCfg = Release|x64
{03276A39-D4E9-417C-8FFD-200B0EE5E871}.Debug|x64.ActiveCfg = Debug|x64
{03276A39-D4E9-417C-8FFD-200B0EE5E871}.Debug|x64.Build.0 = Debug|x64
{03276A39-D4E9-417C-8FFD-200B0EE5E871}.Debug|x86.ActiveCfg = Debug|x64
{03276A39-D4E9-417C-8FFD-200B0EE5E871}.Release|x64.ActiveCfg = Release|x64
{03276A39-D4E9-417C-8FFD-200B0EE5E871}.Release|x64.Build.0 = Release|x64
{03276A39-D4E9-417C-8FFD-200B0EE5E871}.Release|x86.ActiveCfg = Release|x64
{B81FB7B6-D30E-428F-908A-41422EFC1172}.Debug|x64.ActiveCfg = Debug|x64
{B81FB7B6-D30E-428F-908A-41422EFC1172}.Debug|x64.Build.0 = Debug|x64
{B81FB7B6-D30E-428F-908A-41422EFC1172}.Debug|x86.ActiveCfg = Debug|x64
{B81FB7B6-D30E-428F-908A-41422EFC1172}.Release|x64.ActiveCfg = Release|x64
{B81FB7B6-D30E-428F-908A-41422EFC1172}.Release|x64.Build.0 = Release|x64
{B81FB7B6-D30E-428F-908A-41422EFC1172}.Release|x86.ActiveCfg = Release|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Debug|x64.ActiveCfg = Debug|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Debug|x64.Build.0 = Debug|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Debug|x86.ActiveCfg = Debug|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x64.ActiveCfg = Release|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x64.Build.0 = Release|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x86.ActiveCfg = Release|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Debug|x64.ActiveCfg = Debug|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Debug|x64.Build.0 = Debug|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Debug|x86.ActiveCfg = Debug|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Release|x64.ActiveCfg = Release|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Release|x64.Build.0 = Release|x64
{632BBE62-5421-49EA-835A-7FFA4F499BD6}.Release|x86.ActiveCfg = Release|x64
{4FA206A5-F69F-4193-BF8F-F6EEB496734C}.Debug|x64.ActiveCfg = Debug|x64
{4FA206A5-F69F-4193-BF8F-F6EEB496734C}.Debug|x64.Build.0 = Debug|x64
{4FA206A5-F69F-4193-BF8F-F6EEB496734C}.Debug|x86.ActiveCfg = Debug|x64
{4FA206A5-F69F-4193-BF8F-F6EEB496734C}.Release|x64.ActiveCfg = Release|x64
{4FA206A5-F69F-4193-BF8F-F6EEB496734C}.Release|x64.Build.0 = Release|x64
{4FA206A5-F69F-4193-BF8F-F6EEB496734C}.Release|x86.ActiveCfg = Release|x64
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}.Debug|x64.ActiveCfg = Debug|x64
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}.Debug|x64.Build.0 = Debug|x64
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}.Debug|x86.ActiveCfg = Debug|x64
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}.Release|x64.ActiveCfg = Release|x64
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}.Release|x64.Build.0 = Release|x64
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}.Release|x86.ActiveCfg = Release|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x64.ActiveCfg = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x64.Build.0 = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Debug|x86.ActiveCfg = Debug|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x64.ActiveCfg = Release|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x64.Build.0 = Release|x64
{7E1E3F13-2BD6-3F75-A6A7-873A2B55C60F}.Release|x86.ActiveCfg = Release|x64
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}.Debug|x64.ActiveCfg = Debug|x64
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}.Debug|x64.Build.0 = Debug|x64
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}.Debug|x86.ActiveCfg = Debug|x64
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}.Release|x64.ActiveCfg = Release|x64
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}.Release|x64.Build.0 = Release|x64
{FD8EB419-FF9C-4D88-BB6F-BF6CED37747B}.Release|x86.ActiveCfg = Release|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Debug|x64.ActiveCfg = Debug|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Debug|x64.Build.0 = Debug|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Debug|x86.ActiveCfg = Debug|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Release|x64.ActiveCfg = Release|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Release|x64.Build.0 = Release|x64
{DA5A6FE9-0040-40CC-83CC-764AE5306590}.Release|x86.ActiveCfg = Release|x64
{0351ADA4-0C32-4652-9BA0-41F7B602372B}.Debug|x64.ActiveCfg = Debug|x64
{0351ADA4-0C32-4652-9BA0-41F7B602372B}.Debug|x64.Build.0 = Debug|x64
{0351ADA4-0C32-4652-9BA0-41F7B602372B}.Debug|x86.ActiveCfg = Debug|x64
{0351ADA4-0C32-4652-9BA0-41F7B602372B}.Release|x64.ActiveCfg = Release|x64
{0351ADA4-0C32-4652-9BA0-41F7B602372B}.Release|x64.Build.0 = Release|x64
{0351ADA4-0C32-4652-9BA0-41F7B602372B}.Release|x86.ActiveCfg = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.ActiveCfg = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x64.Build.0 = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Debug|x86.ActiveCfg = Debug|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.ActiveCfg = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x64.Build.0 = Release|x64
{D9B8FC84-322A-4F9F-BBB9-20915C47DDFD}.Release|x86.ActiveCfg = Release|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Debug|x64.ActiveCfg = Debug|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Debug|x64.Build.0 = Debug|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Debug|x86.ActiveCfg = Debug|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Release|x64.ActiveCfg = Release|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Release|x64.Build.0 = Release|x64
{6955446D-23F7-4023-9BB3-8657F904AF99}.Release|x86.ActiveCfg = Release|x64
{58736667-1027-4AD7-BFDF-7A3A6474103A}.Debug|x64.ActiveCfg = Debug|x64
{58736667-1027-4AD7-BFDF-7A3A6474103A}.Debug|x64.Build.0 = Debug|x64
{58736667-1027-4AD7-BFDF-7A3A6474103A}.Debug|x86.ActiveCfg = Debug|x64
{58736667-1027-4AD7-BFDF-7A3A6474103A}.Release|x64.ActiveCfg = Release|x64
{58736667-1027-4AD7-BFDF-7A3A6474103A}.Release|x64.Build.0 = Release|x64
{58736667-1027-4AD7-BFDF-7A3A6474103A}.Release|x86.ActiveCfg = Release|x64
{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}.Debug|x64.ActiveCfg = Debug|x64
{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}.Debug|x64.Build.0 = Debug|x64
{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}.Debug|x86.ActiveCfg = Debug|x64
{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}.Release|x64.ActiveCfg = Release|x64
{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}.Release|x64.Build.0 = Release|x64
{1D5BE09D-78C0-4FD7-AF00-AE7C1AF7C525}.Release|x86.ActiveCfg = Release|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.ActiveCfg = Debug|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x64.Build.0 = Debug|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Debug|x86.ActiveCfg = Debug|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.ActiveCfg = Release|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x64.Build.0 = Release|x64
{031AC72E-FA28-4AB7-B690-6F7B9C28AA73}.Release|x86.ActiveCfg = Release|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.ActiveCfg = Debug|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x64.Build.0 = Debug|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Debug|x86.ActiveCfg = Debug|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.ActiveCfg = Release|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x64.Build.0 = Release|x64
{0B593A6C-4143-4337-860E-DB5710FB87DB}.Release|x86.ActiveCfg = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x64.ActiveCfg = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x64.Build.0 = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Debug|x86.ActiveCfg = Debug|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.ActiveCfg = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.Build.0 = Release|x64
{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x86.ActiveCfg = Release|x64
{7319089E-46D6-4400-BC65-E39BDF1416EE}.Debug|x64.ActiveCfg = Debug|x64
{7319089E-46D6-4400-BC65-E39BDF1416EE}.Debug|x64.Build.0 = Debug|x64
{7319089E-46D6-4400-BC65-E39BDF1416EE}.Debug|x86.ActiveCfg = Debug|x64
{7319089E-46D6-4400-BC65-E39BDF1416EE}.Release|x64.ActiveCfg = Release|x64
{7319089E-46D6-4400-BC65-E39BDF1416EE}.Release|x64.Build.0 = Release|x64
{7319089E-46D6-4400-BC65-E39BDF1416EE}.Release|x86.ActiveCfg = Release|x64
{CABA8DFB-823B-4BF2-93AC-3F31984150D9}.Debug|x64.ActiveCfg = Debug|x64
{CABA8DFB-823B-4BF2-93AC-3F31984150D9}.Debug|x64.Build.0 = Debug|x64
{CABA8DFB-823B-4BF2-93AC-3F31984150D9}.Debug|x86.ActiveCfg = Debug|x64
{CABA8DFB-823B-4BF2-93AC-3F31984150D9}.Release|x64.ActiveCfg = Release|x64
{CABA8DFB-823B-4BF2-93AC-3F31984150D9}.Release|x64.Build.0 = Release|x64
{CABA8DFB-823B-4BF2-93AC-3F31984150D9}.Release|x86.ActiveCfg = Release|x64
{98537082-0FDB-40DE-ABD8-0DC5A4269BAB}.Debug|x64.ActiveCfg = Debug|x64
{98537082-0FDB-40DE-ABD8-0DC5A4269BAB}.Debug|x64.Build.0 = Debug|x64
{98537082-0FDB-40DE-ABD8-0DC5A4269BAB}.Debug|x86.ActiveCfg = Debug|x64
{98537082-0FDB-40DE-ABD8-0DC5A4269BAB}.Release|x64.ActiveCfg = Release|x64
{98537082-0FDB-40DE-ABD8-0DC5A4269BAB}.Release|x64.Build.0 = Release|x64
{98537082-0FDB-40DE-ABD8-0DC5A4269BAB}.Release|x86.ActiveCfg = Release|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Debug|x64.ActiveCfg = Debug|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Debug|x64.Build.0 = Debug|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Debug|x86.ActiveCfg = Debug|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x64.ActiveCfg = Release|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x64.Build.0 = Release|x64
{C3A17DCA-217B-462C-BB0C-BE086AF80081}.Release|x86.ActiveCfg = Release|x64
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x64.ActiveCfg = Debug|x64
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x64.Build.0 = Debug|x64
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Debug|x86.ActiveCfg = Debug|x64
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Release|x64.ActiveCfg = Release|x64
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Release|x64.Build.0 = Release|x64
{4BABF3FE-3451-42FD-873F-3C332E18DCEF}.Release|x86.ActiveCfg = Release|x64
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Debug|x64.ActiveCfg = Debug|x64
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Debug|x64.Build.0 = Debug|x64
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Debug|x86.ActiveCfg = Debug|x64
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Release|x64.ActiveCfg = Release|x64
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Release|x64.Build.0 = Release|x64
{0648DF05-5DDA-4BE1-B5F2-584926EBDB65}.Release|x86.ActiveCfg = Release|x64
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Debug|x64.ActiveCfg = Debug|x64
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Debug|x64.Build.0 = Debug|x64
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Debug|x86.ActiveCfg = Debug|x64
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.ActiveCfg = Release|x64
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x64.Build.0 = Release|x64
{6ED2F4FC-E122-4CEE-90F1-97E4CCC8BC7A}.Release|x86.ActiveCfg = Release|x64
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x64.ActiveCfg = Debug|x64
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x64.Build.0 = Debug|x64
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Debug|x86.ActiveCfg = Debug|x64
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x64.ActiveCfg = Release|x64
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x64.Build.0 = Release|x64
{BA661F5B-1D5A-4FFC-9BF1-FC39DF280BDD}.Release|x86.ActiveCfg = Release|x64
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x64.ActiveCfg = Debug|x64
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x64.Build.0 = Debug|x64
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Debug|x86.ActiveCfg = Debug|x64
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x64.ActiveCfg = Release|x64
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x64.Build.0 = Release|x64
{E496B7FC-1E99-4BAB-849B-0E8367040B02}.Release|x86.ActiveCfg = Release|x64
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x64.ActiveCfg = Debug|x64
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x64.Build.0 = Debug|x64
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Debug|x86.ActiveCfg = Debug|x64
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x64.ActiveCfg = Release|x64
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x64.Build.0 = Release|x64
{7F4B3A60-BC27-45A7-8000-68B0B6EA7466}.Release|x86.ActiveCfg = Release|x64
{8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x64.ActiveCfg = Debug|x64
{8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x64.Build.0 = Debug|x64
{8DF78B53-200E-451F-9328-01EB907193AE}.Debug|x86.ActiveCfg = Debug|x64
{8DF78B53-200E-451F-9328-01EB907193AE}.Release|x64.ActiveCfg = Release|x64
{8DF78B53-200E-451F-9328-01EB907193AE}.Release|x64.Build.0 = Release|x64
{8DF78B53-200E-451F-9328-01EB907193AE}.Release|x86.ActiveCfg = Release|x64
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x64.ActiveCfg = Debug|x64
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x64.Build.0 = Debug|x64
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Debug|x86.ActiveCfg = Debug|x64
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x64.ActiveCfg = Release|x64
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x64.Build.0 = Release|x64
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49}.Release|x86.ActiveCfg = Release|x64
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.ActiveCfg = Debug|x64
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x64.Build.0 = Debug|x64
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Debug|x86.ActiveCfg = Debug|x64
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.ActiveCfg = Release|x64
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x64.Build.0 = Release|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|x64.ActiveCfg = Debug|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|x64.Build.0 = Debug|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Release|x64.ActiveCfg = Release|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Release|x64.Build.0 = Release|x64
{62173D9A-6724-4C00-A1C8-FB646480A9EC}.Release|x86.ActiveCfg = Release|x64
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A}.Debug|x64.ActiveCfg = Debug|x64
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A}.Debug|x64.Build.0 = Debug|x64
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A}.Debug|x86.ActiveCfg = Debug|x64
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A}.Release|x64.ActiveCfg = Release|x64
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A}.Release|x64.Build.0 = Release|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Debug|x64.ActiveCfg = Debug|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Debug|x64.Build.0 = Debug|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Release|x64.ActiveCfg = Release|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Release|x64.Build.0 = Release|x64
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A}.Release|x86.ActiveCfg = Release|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Debug|x64.ActiveCfg = Debug|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Debug|x64.Build.0 = Debug|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Debug|x86.ActiveCfg = Debug|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Release|x64.ActiveCfg = Release|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Release|x64.Build.0 = Release|x64
{D940E07F-532C-4FF3-883F-790DA014F19A}.Release|x86.ActiveCfg = Release|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|x64.ActiveCfg = Debug|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|x64.Build.0 = Debug|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Debug|x86.ActiveCfg = Debug|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Release|x64.ActiveCfg = Release|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Release|x64.Build.0 = Release|x64
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4}.Release|x86.ActiveCfg = Release|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Debug|x64.ActiveCfg = Debug|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Debug|x64.Build.0 = Debug|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Debug|x86.ActiveCfg = Debug|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Release|x64.ActiveCfg = Release|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Release|x64.Build.0 = Release|x64
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1}.Release|x86.ActiveCfg = Release|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Debug|x64.ActiveCfg = Debug|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Debug|x64.Build.0 = Debug|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Debug|x86.ActiveCfg = Debug|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x64.ActiveCfg = Release|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x64.Build.0 = Release|x64
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81}.Release|x86.ActiveCfg = Release|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x64.ActiveCfg = Debug|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x64.Build.0 = Debug|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Debug|x86.ActiveCfg = Debug|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x64.ActiveCfg = Release|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x64.Build.0 = Release|x64
{2EDB3EB4-FA92-4BFF-B2D8-566584837231}.Release|x86.ActiveCfg = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.ActiveCfg = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x64.Build.0 = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Debug|x86.ActiveCfg = Debug|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x64.ActiveCfg = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x64.Build.0 = Release|x64
{390AE700-B55F-4202-91EA-A822EB75B9BD}.Debug|x64.ActiveCfg = Debug|x64
{390AE700-B55F-4202-91EA-A822EB75B9BD}.Debug|x64.Build.0 = Debug|x64
{390AE700-B55F-4202-91EA-A822EB75B9BD}.Release|x64.ActiveCfg = Release|x64
{390AE700-B55F-4202-91EA-A822EB75B9BD}.Release|x64.Build.0 = Release|x64
{48804216-2A0E-4168-A6D8-9CD068D14227}.Release|x86.ActiveCfg = Release|x64
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}.Debug|x64.ActiveCfg = Debug|x64
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}.Debug|x64.Build.0 = Debug|x64
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}.Debug|x86.ActiveCfg = Debug|x64
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}.Release|x64.ActiveCfg = Release|x64
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}.Release|x64.Build.0 = Release|x64
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700}.Release|x86.ActiveCfg = Release|x64
{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}.Debug|x64.ActiveCfg = Debug|x64
{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}.Debug|x64.Build.0 = Debug|x64
{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}.Debug|x86.ActiveCfg = Debug|x64
{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}.Release|x64.ActiveCfg = Release|x64
{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}.Release|x64.Build.0 = Release|x64
{44CE9AE1-4390-42C5-BACC-0FD6B40AA203}.Release|x86.ActiveCfg = Release|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Debug|x64.ActiveCfg = Debug|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Debug|x64.Build.0 = Debug|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Debug|x86.ActiveCfg = Debug|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.ActiveCfg = Release|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.Build.0 = Release|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.ActiveCfg = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.Build.0 = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x86.ActiveCfg = Debug|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x86.Build.0 = Debug|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.Build.0 = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x86.ActiveCfg = Release|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x86.Build.0 = Release|Win32
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|x64.ActiveCfg = Debug|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|x64.Build.0 = Debug|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|x86.ActiveCfg = Debug|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|x64.ActiveCfg = Release|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|x64.Build.0 = Release|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|x86.ActiveCfg = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x64.ActiveCfg = Debug|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x64.Build.0 = Debug|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x86.ActiveCfg = Debug|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x86.Build.0 = Debug|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.ActiveCfg = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.Build.0 = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x86.ActiveCfg = Release|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -809,16 +1029,20 @@ Global
{23D2070D-E4AD-4ADD-85A7-083D9C76AD49} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{62173D9A-6724-4C00-A1C8-FB646480A9EC} = {38BDB927-829B-4C65-9CD9-93FB05D66D65}
{127F38E0-40AA-4594-B955-5616BF206882} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{5E7360A8-D048-4ED3-8F09-0BFD64C5529A} = {127F38E0-40AA-4594-B955-5616BF206882}
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{D940E07F-532C-4FF3-883F-790DA014F19A} = {127F38E0-40AA-4594-B955-5616BF206882}
{BB23A474-5058-4F75-8FA3-5FE3DE53CDF4} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{3E424AD2-19E5-4AE6-B833-F53963EB5FC1} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{106CBECA-0701-4FC3-838C-9DF816A19AE2} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{2D604C07-51FC-46BB-9EB7-75AECC7F5E81} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
{2EDB3EB4-FA92-4BFF-B2D8-566584837231} = {106CBECA-0701-4FC3-838C-9DF816A19AE2}
{48804216-2A0E-4168-A6D8-9CD068D14227} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{390AE700-B55F-4202-91EA-A822EB75B9BD} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
{AC2857B4-103D-4D6D-9740-926EBF785042} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
{470FBAF9-E1F8-4F3E-8786-198A1C81C8A8} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View file

@ -66,7 +66,12 @@ Various tools used by PowerToys. Includes the Visual Studio 2019 project templat
2. Visual Studio Community/Professional/Enterprise 2019
3. Once you've cloned and started the `PowerToys.sln`, in the solution explorer, if you see a dialog that says `install extra components`, click `install`
### Compile source code
**Optional step:**<br/>
4. to build the Video Conference module, install the [WDK version 1903](https://docs.microsoft.com/en-us/windows-hardware/drivers/other-wdk-downloads) ([direct download link](https://go.microsoft.com/fwlink/?linkid=2085767))<br />
During the installation, make sure that, when prompted, the `Install Windows Driver Kit Visual Studio extension` option is checked.
### Compiling Source Code
- Open `PowerToys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
- The PowerToys binaries will be in your repo under `x64\Release\`.

View file

@ -3,7 +3,7 @@
<Import Project="..\packages\WiX.3.11.2\build\wix.props" Condition="Exists('..\packages\WiX.3.11.2\build\wix.props')" />
<Import Project="..\..\src\Version.props" />
<PropertyGroup>
<DefineConstants>Version=$(Version);</DefineConstants>
<DefineConstants>Version=$(Version)</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
@ -74,7 +74,6 @@
</Target>
<PropertyGroup>
<PreBuildEvent>IF NOT DEFINED IsPipeline (
call "$([MSBuild]::GetVsInstallRoot())\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0
SET PTRoot=..\..\..\..
call "..\..\publish.cmd"

View file

@ -8,9 +8,11 @@
<?define KeyboardManagerProjectName="KeyboardManager"?>
<?define PowerRenameProjectName="PowerRename"?>
<?define ColorPickerProjectName="ColorPicker"?>
<?define VideoConferenceProjectName="VideoConference"?>
<?define AwakeProjectName="Awake"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?define BinX32Dir="$(var.RepoDir)x86\$(var.Configuration)\" ?>
<?define BinX64Dir="$(var.RepoDir)x64\$(var.Configuration)\" ?>
<?define ShortcutGuideExecutable=$(var.BinX64Dir)\modules\ShortcutGuide\ShortcutGuide?>
<?define ShortcutGuideModuleInterface=$(var.BinX64Dir)\modules\ShortcutGuide\ShortcutGuideModuleInterface?>
@ -218,6 +220,10 @@
</Directory>
<Directory Id="ShortcutGuideModuleInterfaceInstallFolder" Name="ShortcutGuideModuleInterface"/>
</Directory>
<!-- TODO(yuyoyuppe): uncomment when VCM should be enabled -->
<!-- <Directory Id="VideoConferenceInstallFolder" Name="$(var.VideoConferenceProjectName)">
<Directory Id="VideoConferenceIconsFolder" Name="Icons" />
</Directory> -->
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
<Directory Id="AwakeInstallFolder" Name="$(var.AwakeProjectName)">
@ -442,6 +448,9 @@
<Component Id="BugReportTool_exe" Guid="0F8E3E9F-2E86-4660-A3BF-AE4DD431B93C" Win64="yes">
<File Source="$(var.BinX64Dir)BugReportTool\BugReportTool.exe" Id="BugReportTool.exe" KeyPath="yes" Checksum="yes" />
</Component>
<Component Id="WebcamReportTool_exe" Guid="B6005DAC-8C26-4865-91B3-99F098422C13" Win64="yes">
<File Source="$(var.BinX64Dir)WebcamReportTool\WebcamReportTool.exe" Id="WebcamReportTool.exe" Checksum="yes" />
</Component>
</DirectoryRef>
<DirectoryRef Id="ModulesInstallFolder" FileSource="$(var.BinX64Dir)modules\">
@ -637,6 +646,37 @@
</Component>
</DirectoryRef>
<!-- TODO(yuyoyuppe): uncomment when VCM should be enabled -->
<!-- <DirectoryRef Id="VideoConferenceInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\">
<Component Id="Module_VideoConference" Guid="5996527a-40fc-432e-b3ac-abc0b4bd3887" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 18362</Condition>
<File SelfRegCost="1" Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceProxyFilter_x64.dll" KeyPath="yes">
<Class Id="{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}" Context="InprocServer32" ThreadingModel="apartment" Description="PowerToys VideoConference Mute" />
</File>
<File SelfRegCost="1" Source="$(var.BinX32Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceProxyFilter_x86.dll">
<Class Id="{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}" Context="InprocServer32" ThreadingModel="apartment" Description="PowerToys VideoConference Mute" />
</File>
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceModule.dll" />
</Component>
</DirectoryRef>
<DirectoryRef Id="VideoConferenceIconsFolder" FileSource="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons">
<Component Id="Module_VideoConferenceIcons" Guid="5996527a-40fc-432e-b34c-abc0b4bd3887" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 18362</Condition>
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-NotInUse Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-NotInUse Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-Off Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-Off Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-On Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\Off-On Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-NotInUse Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-NotInUse Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-Off Light.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-Off Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-On Dark.png" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\Icons\On-On Light.png" />
</Component>
</DirectoryRef> -->
<DirectoryRef Id="ShortcutGuideExecutableInstallFolder" FileSource="$(var.ShortcutGuideExecutable)">
<Component Id="Module_ShortcutGuideExecutable" Guid="DA6E5710-F1DF-44EB-A316-300FA39544E9" Win64="yes">
<File Source="$(var.ShortcutGuideExecutable)\PowerToys.ShortcutGuide.exe" KeyPath="yes" />
@ -751,7 +791,7 @@
<File Source="$(var.BinX64Dir)Settings\PowerToys.Settings.exe"/>
<File Source="$(var.BinX64Dir)Settings\Microsoft.PowerToys.Settings.UI.exe"/>
<!-- dll -->
<?foreach File in concrt140_app.dll;Microsoft.Bcl.AsyncInterfaces.dll;System.IO.Abstractions.dll;Microsoft.PowerToys.Settings.UI.Lib.dll;PowerToys.Settings.dll;Microsoft.Toolkit.dll;Microsoft.Toolkit.Uwp.dll;Microsoft.Toolkit.Uwp.UI.dll;Microsoft.Toolkit.Win32.UI.XamlHost.dll;Microsoft.Toolkit.Win32.UI.XamlHost.Managed.dll;Microsoft.Toolkit.Wpf.UI.Controls.dll;Microsoft.Toolkit.Wpf.UI.XamlHost.dll;Microsoft.UI.Xaml.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;msvcp140_1_app.dll;msvcp140_2_app.dll;msvcp140_app.dll;Newtonsoft.Json.dll;PowerToysInterop.dll;System.Runtime.CompilerServices.Unsafe.dll;System.Text.Encodings.Web.dll;System.Text.Json.dll;vcamp140_app.dll;vccorlib140_app.dll;vcomp140_app.dll;vcruntime140_1_app.dll;vcruntime140_app.dll;ManagedTelemetry.dll;ManagedCommon.dll;ColorCode.Core.dll;ColorCode.UWP.dll;Microsoft.Graphics.Canvas.winmd;Microsoft.Toolkit.Parsers.dll;Microsoft.Toolkit.Uwp.UI.Animations.dll;Microsoft.Toolkit.Uwp.UI.Controls.dll?>
<?foreach File in concrt140_app.dll;Microsoft.Bcl.AsyncInterfaces.dll;System.IO.Abstractions.dll;Microsoft.PowerToys.Settings.UI.Lib.dll;PowerToys.Settings.dll;Microsoft.Toolkit.dll;Microsoft.Toolkit.Uwp.dll;Microsoft.Toolkit.Uwp.UI.dll;Microsoft.Toolkit.Win32.UI.XamlHost.dll;Microsoft.Toolkit.Win32.UI.XamlHost.Managed.dll;Microsoft.Toolkit.Wpf.UI.Controls.dll;Microsoft.Toolkit.Wpf.UI.XamlHost.dll;Microsoft.UI.Xaml.dll;Microsoft.Xaml.Interactions.dll;Microsoft.Xaml.Interactivity.dll;msvcp140_1_app.dll;msvcp140_2_app.dll;msvcp140_app.dll;Newtonsoft.Json.dll;PowerToysInterop.dll;System.Runtime.CompilerServices.Unsafe.dll;System.Text.Encodings.Web.dll;System.Text.Json.dll;vcamp140_app.dll;vccorlib140_app.dll;vcomp140_app.dll;vcruntime140_1_app.dll;vcruntime140_app.dll;ManagedTelemetry.dll;ManagedCommon.dll;ColorCode.Core.dll;ColorCode.UWP.dll;Microsoft.Graphics.Canvas.winmd;Microsoft.Toolkit.Parsers.dll;Microsoft.Toolkit.Uwp.UI.Animations.dll;Microsoft.Toolkit.Uwp.UI.Controls.dll;System.Deployment.dll;System.Windows.Forms.dll;System.Runtime.Serialization.Formatters.Soap.dll?>
<File Id="SettingsV2_$(var.File)" Source="$(var.BinX64Dir)Settings\$(var.File)" />
<?endforeach?>
<!-- json -->
@ -791,7 +831,7 @@
</DirectoryRef>
<DirectoryRef Id="SettingsV2AssetsModulesInstallFolder" FileSource="$(var.BinX64Dir)Settings\Assets\Modules">
<Component Id="SettingsV2AssetsModules" Guid="A0B961A9-77D0-4223-88A9-E3B41BD9C329" Win64="yes">
<?foreach File in ColorPicker.png;FancyZones.png;Awake.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png?>
<?foreach File in ColorPicker.png;FancyZones.png;Awake.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png;VideoConference.png?>
<File Id="SettingsV2AssetsModules_$(var.File)" Source="$(var.BinX64Dir)Settings\Assets\Modules\$(var.File)" />
<?endforeach?>
</Component>
@ -882,6 +922,9 @@
<ComponentRef Id="ShortcutGuideSvgs" />
<ComponentRef Id="Module_ShortcutGuideModuleInterface" />
<ComponentRef Id="Module_ShortcutGuideExecutable" />
<!-- TODO(yuyoyuppe): uncomment when VCM should be enabled -->
<!-- <ComponentRef Id="Module_VideoConference" />
<ComponentRef Id="Module_VideoConferenceIcons" /> -->
<ComponentRef Id="Module_FancyZones" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" />
@ -911,6 +954,7 @@
</ComponentGroup>
<ComponentGroup Id="ToolComponents" Directory="ToolsFolder">
<ComponentRef Id="BugReportTool_exe" />
<ComponentRef Id="WebcamReportTool_exe" />
</ComponentGroup>
</Fragment>

View file

@ -18,6 +18,7 @@ TRACELOGGING_DEFINE_PROVIDER(
const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0'
const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0'
static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}";
static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
// Creates a Scheduled Task to run at logon for the current user.
@ -596,6 +597,165 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
return WcaFinalize(er);
}
UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall)
{
#ifdef CIBuild // On pipeline we are using microsoft certification
WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
return WcaFinalize(ERROR_SUCCESS);
#else
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
LPWSTR certificatePath = nullptr;
HCERTSTORE hCertStore = nullptr;
HANDLE hfile = nullptr;
DWORD size = INVALID_FILE_SIZE;
char * pFileContent = nullptr;
hr = WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize", hr);
hr = WcaGetProperty(L"CustomActionData", &certificatePath);
ExitOnFailure(hr, "Failed to get install preperty", hr);
hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, L"AuthRoot");
if (!hCertStore)
{
hr = GetLastError();
ExitOnFailure(hr, "Cannot put principal run level: %x", hr);
}
hfile = CreateFile(certificatePath, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hfile == INVALID_HANDLE_VALUE)
{
hr = GetLastError();
ExitOnFailure(hr, "Certificate file open failed", hr);
}
size = GetFileSize(hfile, nullptr);
if (size == INVALID_FILE_SIZE)
{
hr = GetLastError();
ExitOnFailure(hr, "Certificate file size not valid", hr);
}
pFileContent = (char*)malloc(size);
DWORD sizeread;
if (!ReadFile(hfile, pFileContent, size, &sizeread, nullptr))
{
hr = GetLastError();
ExitOnFailure(hr, "Certificate file read failed", hr);
}
if (!CertAddEncodedCertificateToStore(hCertStore,
X509_ASN_ENCODING,
(const BYTE*)pFileContent,
size,
CERT_STORE_ADD_ALWAYS,
nullptr))
{
hr = GetLastError();
ExitOnFailure(hr, "Adding certificate failed", hr);
}
free(pFileContent);
LExit:
ReleaseStr(certificatePath);
if (hCertStore)
{
CertCloseStore(hCertStore, 0);
}
if (hfile)
{
CloseHandle(hfile);
}
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to add certificate to store"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
#endif
}
UINT __stdcall InstallVirtualCameraDriverCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
LPWSTR driverPath = nullptr;
hr = WcaInitialize(hInstall, "InstallVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaGetProperty(L"CustomActionData", &driverPath);
ExitOnFailure(hr, "Failed to get install preperty");
BOOL requiresReboot;
DiInstallDriverW(GetConsoleWindow(), driverPath, DIIRFLAG_FORCE_INF, &requiresReboot);
hr = GetLastError();
ExitOnFailure(hr, "Failed to install driver");
LExit:
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Failed to install virtual camera driver"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall UninstallVirtualCameraDriverCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
LPWSTR driverPath = nullptr;
hr = WcaInitialize(hInstall, "UninstallVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize");
hr = WcaGetProperty(L"CustomActionData", &driverPath);
ExitOnFailure(hr, "Failed to get uninstall preperty");
BOOL requiresReboot;
DiUninstallDriverW(GetConsoleWindow(), driverPath, 0, &requiresReboot);
switch (GetLastError())
{
case ERROR_ACCESS_DENIED:
case ERROR_FILE_NOT_FOUND:
case ERROR_INVALID_FLAGS:
case ERROR_IN_WOW64:
{
hr = GetLastError();
ExitOnFailure(hr, "Failed to uninstall driver");
break;
}
}
LExit:
if (!SUCCEEDED(hr))
{
PMSIHANDLE hRecord = MsiCreateRecord(0);
MsiRecordSetString(hRecord, 0, TEXT("Filed to iminstall virtual camera driver"));
MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall TerminateProcessesCA(MSIHANDLE hInstall)
{

View file

@ -12,4 +12,8 @@ EXPORTS
TelemetryLogUninstallFailCA
TelemetryLogRepairCancelCA
TelemetryLogRepairFailCA
TerminateProcessesCA
TerminateProcessesCA
TerminateProcessesCA
CertifyVirtualCameraDriverCA
InstallVirtualCameraDriverCA
UninstallVirtualCameraDriverCA

View file

@ -56,7 +56,7 @@
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;..\..\$(PlatformShortName)\$(Configuration)\;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>msi.lib;wcautil.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;ApplicationUpdate.lib;Notifications.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Newdev.lib;Crypt32.lib;msi.lib;wcautil.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;ApplicationUpdate.lib;Notifications.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -124,4 +124,4 @@
</PropertyGroup>
<Error Condition="!Exists('..\packages\WiX.3.11.2\build\wix.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WiX.3.11.2\build\wix.props'))" />
</Target>
</Project>
</Project>

View file

@ -4,6 +4,7 @@
#define DPSAPI_VERSION 1
// Windows Header Files:
#include <windows.h>
#include <newdev.h>
#include <strsafe.h>
#include <msiquery.h>
#include <Msi.h>

View file

@ -1,6 +1,13 @@
#pragma once
#include "pch.h"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <thread>
#include <optional>
#include <string>
#include <functional>
class FileWatcher
{

View file

@ -28,10 +28,12 @@
<ClInclude Include="pch.h" />
<ClInclude Include="settings_helpers.h" />
<ClInclude Include="settings_objects.h" />
<ClInclude Include="FileWatcher.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="settings_helpers.cpp" />
<ClCompile Include="settings_objects.cpp" />
<ClCompile Include="FileWatcher.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>

View file

@ -8,6 +8,7 @@ namespace PTSettingsHelper
{
constexpr inline const wchar_t* log_settings_filename = L"log_settings.json";
std::wstring get_powertoys_general_save_file_location();
std::wstring get_module_save_file_location(std::wstring_view powertoy_key);
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name);
std::wstring get_root_save_folder_location();

View file

@ -1,6 +1,6 @@
#pragma once
#include <TraceLoggingProvider.h>
#include <TraceLoggingDefines.h>
#include "TraceLoggingProvider.h"
#include "TraceLoggingDefines.h"
TRACELOGGING_DECLARE_PROVIDER(g_hProvider);

View file

@ -47,6 +47,7 @@
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>PowerToysInterop;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<AdditionalIncludeDirectories>$(SolutionDir)src\common\interop;../../;../;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<OmitDefaultLibName>false</OmitDefaultLibName>
@ -54,7 +55,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Mf.lib;WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
@ -83,6 +84,8 @@
<WriteLinesToFile File="Generated Files\AssemblyInfo.cpp" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<ItemGroup>
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.h" />
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.h" />
<ClInclude Include="HotkeyManager.h" />
<ClInclude Include="KeyboardHook.h" />
<ClInclude Include="pch.h" />
@ -90,6 +93,12 @@
<ClInclude Include="shared_constants.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp">
<CompileAsManaged>false</CompileAsManaged>
</ClCompile>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp">
<CompileAsManaged>false</CompileAsManaged>
</ClCompile>
<ClCompile Include="Generated Files\AssemblyInfo.cpp" />
<ClCompile Include="HotkeyManager.cpp" />
<ClCompile Include="interop.cpp" />
@ -109,6 +118,12 @@
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View file

@ -27,7 +27,13 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shared_constants.h">
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shared_constants.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@ -50,10 +56,16 @@
<ClCompile Include="keyboard_layout.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="interop.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>
</Project>

View file

@ -13,12 +13,17 @@
// Therefore the simplest way is to compile these functions as native using the pragmas below.
#pragma managed(push, off)
#include "../utils/os-detect.h"
// TODO: move to a separate library in common
#include "../../modules/videoconference/VideoConferenceShared/MicrophoneDevice.h"
#include "../../modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.h"
#pragma managed(pop)
#include <common/version/version.h>
using namespace System;
using namespace System::Runtime::InteropServices;
using System::Collections::Generic::List;
// https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
namespace interop
@ -122,6 +127,32 @@ public
static String ^ GetProductVersion() {
return gcnew String(get_product_version().c_str());
}
static List<String ^> ^ GetAllActiveMicrophoneDeviceNames() {
auto names = gcnew List<String ^>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names->Add(gcnew String(device.name().data()));
}
return names;
}
static List<String ^> ^
GetAllVideoCaptureDeviceNames() {
auto names = gcnew List<String ^>();
VideoCaptureDeviceList vcdl;
vcdl.EnumerateDevices();
for (UINT32 i = 0; i < vcdl.Count(); ++i)
{
auto name = gcnew String(vcdl.GetDeviceName(i).data());
if (name != L"PowerToys VideoConference Mute")
{
names->Add(name);
}
}
return names;
}
};
public

View file

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

View file

@ -7,13 +7,13 @@
#include <common/utils/resources.h>
#include <common/utils/winapi_error.h>
#include <common/utils/window.h>
#include <common/SettingsAPI/FileWatcher.h>
#include "FancyZones.h"
#include "FancyZonesLib/Settings.h"
#include "FancyZonesLib/ZoneWindow.h"
#include "FancyZonesLib/FancyZonesData.h"
#include "FancyZonesLib/ZoneSet.h"
#include "FancyZonesLib/FileWatcher.h"
#include "FancyZonesLib/WindowMoveHandler.h"
#include "FancyZonesLib/FancyZonesWinHookEventIDs.h"
#include "FancyZonesLib/util.h"

View file

@ -41,7 +41,6 @@
<ClInclude Include="FancyZones.h" />
<ClInclude Include="FancyZonesDataTypes.h" />
<ClInclude Include="FancyZonesWinHookEventIDs.h" />
<ClInclude Include="FileWatcher.h" />
<ClInclude Include="GenericKeyHook.h" />
<ClInclude Include="FancyZonesData.h" />
<ClInclude Include="JsonHelpers.h" />
@ -67,7 +66,6 @@
<ClCompile Include="FancyZonesDataTypes.cpp" />
<ClCompile Include="FancyZonesWinHookEventIDs.cpp" />
<ClCompile Include="FancyZonesData.cpp" />
<ClCompile Include="FileWatcher.cpp" />
<ClCompile Include="JsonHelpers.cpp" />
<ClCompile Include="MonitorWorkAreaHandler.cpp" />
<ClCompile Include="OnThreadExecutor.cpp" />

View file

@ -78,9 +78,6 @@
<ClInclude Include="ZoneWindowDrawing.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FileWatcher.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CallTracer.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -140,9 +137,6 @@
<ClCompile Include="OnThreadExecutor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="FileWatcher.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CallTracer.cpp">
<Filter>Source Files</Filter>
</ClCompile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Disabled">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Disabled" clip-path="url(#clip-Off-Disabled)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#585858" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camera_disabled_icon" data-name="Camera disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#585858"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Disabled_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Disabled_Light" data-name="Off-Disabled Light" clip-path="url(#clip-Off-Disabled_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#c1c1c1" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camera_disabled_icon" data-name="Camera disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#c1c1c1"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Off_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Off_Dark" data-name="Off-Off Dark" clip-path="url(#clip-Off-Off_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)" fill="#fff"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-Off_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-Off_Light" data-name="Off-Off Light" clip-path="url(#clip-Off-Off_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)"/>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-On_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-On_Dark" data-name="Off-On Dark" clip-path="url(#clip-Off-On_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-Off-On_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="Off-On_Light" data-name="Off-On Light" clip-path="url(#clip-Off-On_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Mic_off_icon" data-name="Mic off icon" d="M23.943,2.531l-1.019-1V1.5a1.448,1.448,0,0,1,.119-.586A1.5,1.5,0,0,1,23.37.437,1.538,1.538,0,0,1,24.453,0h4.076a1.538,1.538,0,0,1,1.083.437,1.5,1.5,0,0,1,.326.477,1.448,1.448,0,0,1,.119.586V8.531l-1.019-1V1.5a.472.472,0,0,0-.143-.352A.529.529,0,0,0,28.528,1H24.453a.491.491,0,0,0-.358.141.509.509,0,0,0-.151.359ZM32.095,8v2.523l-1.019-.992V8ZM35,15.148l-.716.7-3-2.945a3.218,3.218,0,0,1-1.106.8A3.388,3.388,0,0,1,28.831,14H27v1h2.038v1H23.943V15h2.038V14H24.15a3.206,3.206,0,0,1-1.266-.25,3.307,3.307,0,0,1-1.035-.687,3.239,3.239,0,0,1-.7-1.016,3.16,3.16,0,0,1-.263-1.25V8h1.019v2.8a2.093,2.093,0,0,0,.175.852,2.238,2.238,0,0,0,.486.7,2.2,2.2,0,0,0,.708.469,2.493,2.493,0,0,0,.876.18h4.681a2.187,2.187,0,0,0,.947-.211,2.427,2.427,0,0,0,.78-.586l-.812-.8a1.486,1.486,0,0,1-.533.438,1.613,1.613,0,0,1-.685.164H24.453a1.538,1.538,0,0,1-1.083-.437,1.5,1.5,0,0,1-.326-.477,1.448,1.448,0,0,1-.119-.586V4.711L19,.852l.716-.7ZM28.528,11a.5.5,0,0,0,.287-.086.526.526,0,0,0,.191-.234L23.943,5.711V10.5a.472.472,0,0,0,.143.352.529.529,0,0,0,.366.148Z" transform="translate(-9 14)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone off</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Disabled">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Disabled" clip-path="url(#clip-On-Disabled)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#1f1f1f"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#585858" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camer_disabled_icon" data-name="Camer disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#585858"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Disabled_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Disabled_Light" data-name="On-Disabled Light" clip-path="url(#clip-On-Disabled_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#c1c1c1" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera not in use</tspan></text>
<path id="Camer_disabled_icon" data-name="Camer disabled icon" d="M12.5,509.031a3.408,3.408,0,0,1,1.359.272,3.634,3.634,0,0,1,1.117.745,3.348,3.348,0,0,1,.75,1.1A3.555,3.555,0,0,1,16,512.508a3.35,3.35,0,0,1-.273,1.351,3.608,3.608,0,0,1-.75,1.11,3.371,3.371,0,0,1-1.109.745,3.617,3.617,0,0,1-1.367.272,3.407,3.407,0,0,1-1.359-.272,3.634,3.634,0,0,1-1.117-.745,3.347,3.347,0,0,1-.75-1.1A3.555,3.555,0,0,1,9,512.508a3.351,3.351,0,0,1,.273-1.351,3.608,3.608,0,0,1,.75-1.11,3.371,3.371,0,0,1,1.109-.745A3.616,3.616,0,0,1,12.5,509.031ZM10,512.508a2.393,2.393,0,0,0,.2.963,2.585,2.585,0,0,0,.531.792,2.423,2.423,0,0,0,.8.536,2.491,2.491,0,0,0,.977.194,2.591,2.591,0,0,0,.719-.1,2.241,2.241,0,0,0,.656-.31l-3.461-3.439a2.334,2.334,0,0,0-.3.652A2.76,2.76,0,0,0,10,512.508Zm4.586,1.366a2.333,2.333,0,0,0,.3-.652,2.759,2.759,0,0,0,.109-.714,2.393,2.393,0,0,0-.2-.963,2.428,2.428,0,0,0-.539-.784,2.725,2.725,0,0,0-.8-.536,2.353,2.353,0,0,0-.969-.2,2.593,2.593,0,0,0-.719.1,2.245,2.245,0,0,0-.656.311ZM16,504v5.326a4.453,4.453,0,0,0-1-.823v-2.9l-3,1.5v.714a4.157,4.157,0,0,0-.508.078,4.1,4.1,0,0,0-.492.14v-2.981H1v5.962H8a4.038,4.038,0,0,0-.219.994H0v-7.95H12v1.925Z" transform="translate(171 -486)" fill="#c1c1c1"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Off_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Off_Dark" data-name="On-Off Dark" clip-path="url(#clip-On-Off_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-Off_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-Off_Light" data-name="On-Off Light" clip-path="url(#clip-On-Off_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera off</tspan></text>
<path id="Camera_off_icon" data-name="Camera off icon" d="M12,24.9l4-2.038v8.279l-1.953-1q-.141-.072-.258-.119t-.242-.1a1.825,1.825,0,0,1-.227-.119l-.25-.159a1.726,1.726,0,0,1-.2-.159,1.556,1.556,0,0,1-.156-.167q-.07-.088-.148-.175l-.18-.2L11,27.533v-3.59H7.477l-1-1.019H12Zm3,4.593V24.508l-3,1.536v1.91ZM.148,19.716.852,19l15,15.284-.7.716-3.859-3.924H0V22.924H3.289ZM1,30.057h9.289l-6-6.113H1Z" transform="translate(171 -5)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-On_Dark">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-On_Dark" data-name="On-On Dark" clip-path="url(#clip-On-On_Dark)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#1f1f1f">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#313131"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#313131"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)" fill="#fff"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" fill="#fff" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)" fill="#fff"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -0,0 +1,19 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="322" height="44" viewBox="0 0 322 44">
<defs>
<clipPath id="clip-On-On_Light">
<rect width="322" height="44"/>
</clipPath>
</defs>
<g id="On-On_Light" data-name="On-On Light" clip-path="url(#clip-On-On_Light)">
<rect width="322" height="44" fill="#f1f1f1"/>
<g id="Background" fill="#f1f1f1">
<path d="M 321.5 43.5 L 0.5 43.5 L 0.5 0.5 L 321.5 0.5 L 321.5 43.5 Z" stroke="none"/>
<path d="M 1 1 L 1 43 L 321 43 L 321 1 L 1 1 M 0 0 L 322 0 L 322 44 L 0 44 L 0 0 Z" stroke="none" fill="#dbdbdb"/>
</g>
<rect id="Divider" width="1" height="24" transform="translate(161 10)" fill="#dbdbdb"/>
<text id="Camera_label" data-name="Camera label" transform="translate(200 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Camera on</tspan></text>
<path id="Camera_on_icon" data-name="Camera on icon" d="M16,512.359,12,510.3V512.3H0v-8.231H12v1.993L16,504Zm-5-7.266H1v6.173H11Zm4,.571-3,1.551v1.929l3,1.551Z" transform="translate(171 -486)"/>
<text id="Mic_label" data-name="Mic label" transform="translate(38 13)" font-size="12" font-family="SegoeUI, Segoe UI"><tspan x="0" y="13">Microphone on</tspan></text>
<path id="Mic_on_icon" data-name="Mic on icon" d="M259.518,12.092a1.476,1.476,0,0,1-.589-.118,1.5,1.5,0,0,1-.8-.8,1.486,1.486,0,0,1-.118-.59V1.511a1.486,1.486,0,0,1,.118-.59,1.5,1.5,0,0,1,.8-.8A1.476,1.476,0,0,1,259.518,0h4.02a1.476,1.476,0,0,1,.589.118,1.5,1.5,0,0,1,.8.8,1.487,1.487,0,0,1,.118.59V10.58a1.487,1.487,0,0,1-.118.59,1.5,1.5,0,0,1-.8.8,1.476,1.476,0,0,1-.589.118Zm-.5-1.511a.51.51,0,0,0,.5.5h4.02a.51.51,0,0,0,.5-.5V1.511a.485.485,0,0,0-.149-.354.482.482,0,0,0-.353-.15h-4.02a.482.482,0,0,0-.353.15.485.485,0,0,0-.149.354Zm8.04-2.519v2.85a3.106,3.106,0,0,1-.251,1.244,3.2,3.2,0,0,1-1.7,1.7,3.085,3.085,0,0,1-1.241.252H262.03v1.008h2.01v1.008h-5.025V15.115h2.01V14.107h-1.837a3.085,3.085,0,0,1-1.241-.252,3.2,3.2,0,0,1-1.7-1.7A3.107,3.107,0,0,1,256,10.911V8.061h1.005v2.85a2.128,2.128,0,0,0,.173.85,2.2,2.2,0,0,0,.463.693,2.281,2.281,0,0,0,.7.472,1.981,1.981,0,0,0,.848.173h4.68a2.113,2.113,0,0,0,.848-.173,2.195,2.195,0,0,0,.691-.464,2.288,2.288,0,0,0,.471-.7,2,2,0,0,0,.173-.85V8.061Z" transform="translate(-244.073 14)"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

@ -0,0 +1,14 @@
# Video Conference Mute
# Introduction
The Video Conference Mute module allows muting microphone and/or web camera video stream during video calls or other activity.
# Usage
If you'd like to mute your web camera, please select "PowerToys VideoConference Mute" device in your web camera-using app, then restart it.
During a video call, you can use default shortcuts to mute microphone, web camera or both. You'll see a toolbar indicating corresponding mute statuses.
# Options
You can tweak the toolbar position on the screen as well as set web camera overlay image during muting.
# Backlog

View file

@ -0,0 +1,335 @@
#include "pch.h"
#include "Toolbar.h"
#include <windowsx.h>
#include <common/Themes/windows_colors.h>
#include "Logging.h"
#include "VideoConferenceModule.h"
Toolbar* toolbar = nullptr;
const int REFRESH_RATE = 100;
const int OVERLAY_SHOW_TIME = 500;
const int BORDER_OFFSET = 12;
Toolbar::Toolbar()
{
toolbar = this;
darkImages.camOnMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-On Dark.png");
darkImages.camOffMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-Off Dark.png");
darkImages.camOnMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-On Dark.png");
darkImages.camOffMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-Off Dark.png");
darkImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-NotInUse Dark.png");
darkImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-NotInUse Dark.png");
lightImages.camOnMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-On Light.png");
lightImages.camOffMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-Off Light.png");
lightImages.camOnMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-On Light.png");
lightImages.camOffMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-Off Light.png");
lightImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/On-NotInUse Light.png");
lightImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"modules/VideoConference/Icons/Off-NotInUse Light.png");
}
void Toolbar::scheduleModuleSettingsUpdate()
{
moduleSettingsUpdateScheduled = true;
}
void Toolbar::scheduleGeneralSettingsUpdate()
{
generalSettingsUpdateScheduled = true;
}
LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
return 0;
case WM_LBUTTONDOWN:
{
int x = GET_X_LPARAM(lparam);
int y = GET_Y_LPARAM(lparam);
if (x < 322 / 2)
{
VideoConferenceModule::reverseMicrophoneMute();
}
else
{
VideoConferenceModule::reverseVirtualCameraMuteState();
}
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
case WM_CREATE:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics graphic(hdc);
ToolbarImages* themeImages = &toolbar->darkImages;
if (toolbar->theme == L"light" || (toolbar->theme == L"system" && !WindowsColors::is_dark_mode()))
{
themeImages = &toolbar->lightImages;
}
else
{
themeImages = &toolbar->darkImages;
}
Gdiplus::Image* toolbarImage = nullptr;
if (!toolbar->cameraInUse)
{
if (toolbar->microphoneMuted)
{
toolbarImage = themeImages->camUnusedMicOff;
}
else
{
toolbarImage = themeImages->camUnusedMicOn;
}
}
else if (toolbar->microphoneMuted)
{
if (toolbar->cameraMuted)
{
toolbarImage = themeImages->camOffMicOff;
}
else
{
toolbarImage = themeImages->camOnMicOff;
}
}
else
{
if (toolbar->cameraMuted)
{
toolbarImage = themeImages->camOffMicOn;
}
else
{
toolbarImage = themeImages->camOnMicOn;
}
}
graphic.DrawImage(toolbarImage, 0, 0, toolbarImage->GetWidth(), toolbarImage->GetHeight());
EndPaint(hwnd, &ps);
break;
}
case WM_TIMER:
{
if (toolbar->generalSettingsUpdateScheduled)
{
instance->onGeneralSettingsChanged();
toolbar->generalSettingsUpdateScheduled = false;
}
if (toolbar->moduleSettingsUpdateScheduled)
{
instance->onModuleSettingsChanged();
toolbar->moduleSettingsUpdateScheduled = false;
}
toolbar->cameraInUse = VideoConferenceModule::getVirtualCameraInUse();
InvalidateRect(hwnd, NULL, NULL);
using namespace std::chrono;
const auto nowMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
const bool showOverlayTimeout = nowMillis - toolbar->lastTimeCamOrMicMuteStateChanged > OVERLAY_SHOW_TIME;
static bool previousShow = false;
bool show = false;
if (toolbar->cameraInUse)
{
show = toolbar->HideToolbarWhenUnmuted ? toolbar->microphoneMuted || toolbar->cameraMuted : true;
}
else if (toolbar->previouscameraInUse)
{
VideoConferenceModule::unmuteAll();
}
else
{
show = toolbar->microphoneMuted;
}
show = show || !showOverlayTimeout;
if (show)
{
ShowWindow(hwnd, SW_SHOW);
}
else
{
ShowWindow(hwnd, SW_HIDE);
}
if (previousShow != show)
{
previousShow = show;
LOG(show ? "Toolbar visibility changed to shown" : "Toolbar visibility changed to hidden");
}
KillTimer(hwnd, toolbar->nTimerId);
toolbar->previouscameraInUse = toolbar->cameraInUse;
break;
}
default:
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
toolbar->nTimerId = SetTimer(hwnd, 101, REFRESH_RATE, nullptr);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
void Toolbar::show(std::wstring position, std::wstring monitorString)
{
for (auto& hwnd : hwnds)
{
PostMessageW(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
int overlayWidth = darkImages.camOffMicOff->GetWidth();
int overlayHeight = darkImages.camOffMicOff->GetHeight();
// Register the window class
LPCWSTR CLASS_NAME = L"MuteNotificationWindowClass";
WNDCLASS wc{};
wc.hInstance = GetModuleHandleW(nullptr);
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpfnWndProc = WindowProcessMessages;
RegisterClassW(&wc);
// Create the window
DWORD dwExtStyle = 0;
DWORD dwStyle = WS_POPUPWINDOW;
std::vector<MonitorInfo> monitorInfos;
if (monitorString == L"All monitors")
{
monitorInfos = MonitorInfo::GetMonitors(false);
}
else //"Main monitor" or non-present
{
monitorInfos.push_back(MonitorInfo::GetPrimaryMonitor());
}
for (auto& monitorInfo : monitorInfos)
{
int positionX = 0;
int positionY = 0;
if (position == L"Top left corner")
{
positionX = monitorInfo.left() + BORDER_OFFSET;
positionY = monitorInfo.top() + BORDER_OFFSET;
}
else if (position == L"Top center")
{
positionX = monitorInfo.middle().x - overlayWidth / 2;
positionY = monitorInfo.top() + BORDER_OFFSET;
}
else if (position == L"Bottom left corner")
{
positionX = monitorInfo.left() + BORDER_OFFSET;
positionY = monitorInfo.bottom() - overlayHeight - BORDER_OFFSET;
}
else if (position == L"Bottom center")
{
positionX = monitorInfo.middle().x - overlayWidth / 2;
positionY = monitorInfo.bottom() - overlayHeight - BORDER_OFFSET;
}
else if (position == L"Bottom right corner")
{
positionX = monitorInfo.right() - overlayWidth - BORDER_OFFSET;
positionY = monitorInfo.bottom() - overlayHeight - BORDER_OFFSET;
}
else //"Top right corner" or non-present
{
positionX = monitorInfo.right() - overlayWidth - BORDER_OFFSET;
positionY = monitorInfo.top() + BORDER_OFFSET;
}
HWND hwnd;
hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
CLASS_NAME,
CLASS_NAME,
WS_POPUP,
positionX,
positionY,
overlayWidth,
overlayHeight,
nullptr,
nullptr,
GetModuleHandleW(nullptr),
nullptr);
auto transparrentColorKey = RGB(0, 0, 255);
HBRUSH brush = CreateSolidBrush(transparrentColorKey);
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, (LONG_PTR)brush);
SetLayeredWindowAttributes(hwnd, transparrentColorKey, 0, LWA_COLORKEY);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
hwnds.push_back(hwnd);
}
}
void Toolbar::hide()
{
for (auto& hwnd : hwnds)
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
}
bool Toolbar::getCameraMute()
{
return cameraMuted;
}
void Toolbar::setCameraMute(bool mute)
{
if (mute != cameraMuted)
{
lastTimeCamOrMicMuteStateChanged = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
cameraMuted = mute;
}
bool Toolbar::getMicrophoneMute()
{
return microphoneMuted;
}
void Toolbar::setMicrophoneMute(bool mute)
{
if (mute != microphoneMuted)
{
lastTimeCamOrMicMuteStateChanged = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
microphoneMuted = mute;
}
void Toolbar::setHideToolbarWhenUnmuted(bool hide)
{
HideToolbarWhenUnmuted = hide;
}
void Toolbar::setTheme(std::wstring theme)
{
Toolbar::theme = theme;
}

View file

@ -0,0 +1,61 @@
#pragma once
#include <Windows.h>
#include <gdiplus.h>
#include <atomic>
#include <common/Display/monitors.h>
struct ToolbarImages
{
Gdiplus::Image* camOnMicOn = nullptr;
Gdiplus::Image* camOffMicOn = nullptr;
Gdiplus::Image* camOnMicOff = nullptr;
Gdiplus::Image* camOffMicOff = nullptr;
Gdiplus::Image* camUnusedMicOn = nullptr;
Gdiplus::Image* camUnusedMicOff = nullptr;
};
class Toolbar
{
public:
Toolbar();
void scheduleModuleSettingsUpdate();
void scheduleGeneralSettingsUpdate();
void show(std::wstring position, std::wstring monitorString);
void hide();
bool getCameraMute();
void setCameraMute(bool mute);
bool getMicrophoneMute();
void setMicrophoneMute(bool mute);
void setTheme(std::wstring theme);
void setHideToolbarWhenUnmuted(bool hide);
private:
static LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
// Window callback can't be non-static so this members can't as well
std::vector<HWND> hwnds;
ToolbarImages darkImages;
ToolbarImages lightImages;
bool cameraMuted = false;
bool cameraInUse = false;
bool previouscameraInUse = false;
bool microphoneMuted = false;
std::wstring theme = L"system";
bool HideToolbarWhenUnmuted = true;
uint64_t lastTimeCamOrMicMuteStateChanged;
std::atomic_bool moduleSettingsUpdateScheduled = false;
std::atomic_bool generalSettingsUpdateScheduled = false;
UINT_PTR nTimerId;
};

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="pch.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="keyboard_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shortcut_guide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="target_state.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="overlay_window.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
<ClInclude Include="keyboard_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shortcut_guide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="target_state.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="overlay_window.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Header Files">
<UniqueIdentifier>{2c7c97f7-0d87-4230-a4b2-baf2cfc35d58}</UniqueIdentifier>
</Filter>
<Filter Include="Source Files">
<UniqueIdentifier>{aa4b6713-589d-42ef-804d-3a045833f83f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="shortcut_guide.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,183 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" 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|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>overlaywindow</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>VideoConferenceModule</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionSettings">
<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>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;OVERLAYWINDOW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..\..\..\;..\..\;..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>xcopy /y /I "$(ProjectDir)Icons\*" "$(OutDir)Icons"
xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;OVERLAYWINDOW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\..\;..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>mfplat.lib;mf.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<PostBuildEvent>
<Command>xcopy /y /I "$(ProjectDir)Icons\*" "$(OutDir)Icons"
xcopy /y /I "$(ProjectDir)black.bmp*" "$(OutDir)"</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="VideoConferenceModule.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="Toolbar.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="VideoConferenceModule.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VideoConferenceShared\VideoConferenceShared.vcxproj">
<Project>{459e0768-7ebd-4c41-bba1-6db3b3815e0a}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Icons\Off-NotInUse Dark.svg" />
<None Include="Icons\Off-NotInUse Light.svg" />
<None Include="Icons\Off-Off Dark.svg" />
<None Include="Icons\Off-Off Light.svg" />
<None Include="Icons\Off-On Dark.svg" />
<None Include="Icons\Off-On Light.svg" />
<None Include="Icons\On-NotInUse Dark.svg" />
<None Include="Icons\On-NotInUse Light.svg" />
<None Include="Icons\On-Off Dark.svg" />
<None Include="Icons\On-Off Light.svg" />
<None Include="Icons\On-On Dark.svg" />
<None Include="Icons\On-On Light.svg" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Image Include="black.bmp" />
<Image Include="Icons\Off-NotInUse Dark.png" />
<Image Include="Icons\Off-NotInUse Light.png" />
<Image Include="Icons\Off-Off Dark.png" />
<Image Include="Icons\Off-Off Light.png" />
<Image Include="Icons\Off-On Dark.png" />
<Image Include="Icons\Off-On Light.png" />
<Image Include="Icons\On-NotInUse Dark.png" />
<Image Include="Icons\On-NotInUse Light.png" />
<Image Include="Icons\On-Off Dark.png" />
<Image Include="Icons\On-Off Light.png" />
<Image Include="Icons\On-On Dark.png" />
<Image Include="Icons\On-On Light.png" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<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'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.200729.8\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SetttingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
</Project>

View file

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="Toolbar.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="VideoConferenceModule.cpp" />
<ClCompile Include="trace.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="VideoConferenceModule.h" />
<ClInclude Include="trace.h" />
</ItemGroup>
<ItemGroup>
<Image Include="black.bmp" />
<Image Include="Icons\Off-NotInUse Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-NotInUse Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-Off Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-Off Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-On Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\Off-On Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-NotInUse Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-NotInUse Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-Off Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-Off Light.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-On Dark.png">
<Filter>Icons</Filter>
</Image>
<Image Include="Icons\On-On Light.png">
<Filter>Icons</Filter>
</Image>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="Icons\Off-NotInUse Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-NotInUse Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-Off Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-Off Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-On Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\Off-On Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-NotInUse Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-NotInUse Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-Off Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-Off Light.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-On Dark.svg">
<Filter>Icons</Filter>
</None>
<None Include="Icons\On-On Light.svg">
<Filter>Icons</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Filter Include="Icons">
<UniqueIdentifier>{735361e2-82fa-4034-b9c9-cd6aa099eaa5}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View file

@ -0,0 +1,568 @@
#include "pch.h"
#include "VideoConferenceModule.h"
#include <WinUser.h>
#include <gdiplus.h>
#include <shellapi.h>
#include <filesystem>
#include <common/debug_control.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <CameraStateUpdateChannels.h>
#include "logging.h"
#include "trace.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
VideoConferenceModule* instance = nullptr;
VideoConferenceSettings VideoConferenceModule::settings;
Toolbar VideoConferenceModule::toolbar;
HHOOK VideoConferenceModule::hook_handle;
IAudioEndpointVolume* endpointVolume = NULL;
bool VideoConferenceModule::isKeyPressed(unsigned int keyCode)
{
return (GetKeyState(keyCode) & 0x8000);
}
namespace fs = std::filesystem;
bool VideoConferenceModule::isHotkeyPressed(DWORD code, PowerToysSettings::HotkeyObject& hotkey)
{
return code == hotkey.get_code() &&
isKeyPressed(VK_SHIFT) == hotkey.shift_pressed() &&
isKeyPressed(VK_CONTROL) == hotkey.ctrl_pressed() &&
isKeyPressed(VK_LWIN) == hotkey.win_pressed() &&
(isKeyPressed(VK_LMENU)) == hotkey.alt_pressed();
}
void VideoConferenceModule::reverseMicrophoneMute()
{
bool muted = false;
for (auto& controlledMic : instance->_controlledMicrophones)
{
const bool was_muted = controlledMic.muted();
controlledMic.toggle_muted();
muted = muted || !was_muted;
}
if (muted)
{
Trace::MicrophoneMuted();
}
toolbar.setMicrophoneMute(muted);
}
bool VideoConferenceModule::getMicrophoneMuteState()
{
return instance->_microphoneTrackedInUI ? instance->_microphoneTrackedInUI->muted() : false;
}
void VideoConferenceModule::reverseVirtualCameraMuteState()
{
bool muted = false;
if (!instance->_settingsUpdateChannel.has_value())
{
return;
}
instance->_settingsUpdateChannel->access([&muted](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
settings->useOverlayImage = !settings->useOverlayImage;
muted = settings->useOverlayImage;
});
if (muted)
{
Trace::CameraMuted();
}
toolbar.setCameraMute(muted);
}
bool VideoConferenceModule::getVirtualCameraMuteState()
{
bool disabled = false;
if (!instance->_settingsUpdateChannel.has_value())
{
return disabled;
}
instance->_settingsUpdateChannel->access([&disabled](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
disabled = settings->useOverlayImage;
});
return disabled;
}
bool VideoConferenceModule::getVirtualCameraInUse()
{
if (!instance->_settingsUpdateChannel.has_value())
{
return false;
}
bool inUse = false;
instance->_settingsUpdateChannel->access([&inUse](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
inUse = settings->cameraInUse;
});
return inUse;
}
LRESULT CALLBACK VideoConferenceModule::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
switch (wParam)
{
case WM_KEYDOWN:
KBDLLHOOKSTRUCT* kbd = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
if (isHotkeyPressed(kbd->vkCode, settings.cameraAndMicrophoneMuteHotkey))
{
const bool cameraInUse = getVirtualCameraInUse();
const bool microphoneIsMuted = getMicrophoneMuteState();
const bool cameraIsMuted = cameraInUse && getVirtualCameraMuteState();
if (cameraInUse)
{
// we're likely on a video call, so we must mute the unmuted cam/mic or reverse the mute state
// of everything, if cam and mic mute states are the same
if (microphoneIsMuted == cameraIsMuted)
{
reverseMicrophoneMute();
reverseVirtualCameraMuteState();
}
else if (cameraIsMuted)
{
reverseMicrophoneMute();
}
else if (microphoneIsMuted)
{
reverseVirtualCameraMuteState();
}
}
else
{
// if the camera is not in use, we just mute/unmute the mic
reverseMicrophoneMute();
}
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.microphoneMuteHotkey))
{
reverseMicrophoneMute();
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.cameraMuteHotkey))
{
reverseVirtualCameraMuteState();
return 1;
}
}
}
return CallNextHookEx(hook_handle, nCode, wParam, lParam);
}
void VideoConferenceModule::onGeneralSettingsChanged()
{
auto settings = PTSettingsHelper::load_general_settings();
bool enabled = false;
try
{
if (json::has(settings, L"enabled"))
{
for (const auto& mod : settings.GetNamedObject(L"enabled"))
{
const auto value = mod.Value();
if (value.ValueType() != json::JsonValueType::Boolean)
{
continue;
}
if (mod.Key() == get_key())
{
enabled = value.GetBoolean();
break;
}
}
}
}
catch (...)
{
LOG("Couldn't get enabled state");
}
if (enabled)
{
enable();
}
else
{
disable();
}
}
void VideoConferenceModule::onModuleSettingsChanged()
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
//Trace::SettingsChanged(pressTime.value, overlayOpacity.value, theme.value);
if (_enabled)
{
if (const auto val = values.get_json(L"mute_camera_and_microphone_hotkey"))
{
settings.cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(L"mute_microphone_hotkey"))
{
settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(L"mute_camera_hotkey"))
{
settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_string_value(L"toolbar_position"))
{
settings.toolbarPositionString = val.value();
}
if (const auto val = values.get_string_value(L"toolbar_monitor"))
{
settings.toolbarMonitorString = val.value();
}
if (const auto val = values.get_string_value(L"selected_camera"); val && val != settings.selectedCamera)
{
settings.selectedCamera = val.value();
sendSourceCameraNameUpdate();
}
if (const auto val = values.get_string_value(L"camera_overlay_image_path"); val && val != settings.imageOverlayPath)
{
settings.imageOverlayPath = val.value();
sendOverlayImageUpdate();
}
if (const auto val = values.get_bool_value(L"hide_toolbar_when_unmuted"))
{
toolbar.setHideToolbarWhenUnmuted(val.value());
}
const auto selectedMic = values.get_string_value(L"selected_mic");
if (selectedMic && selectedMic != settings.selectedMicrophone)
{
settings.selectedMicrophone = *selectedMic;
updateControlledMicrophones(settings.selectedMicrophone);
}
toolbar.show(settings.toolbarPositionString, settings.toolbarMonitorString);
}
}
catch (...)
{
LOG("onModuleSettingsChanged encountered an exception");
}
}
VideoConferenceModule::VideoConferenceModule() :
_generalSettingsWatcher{ PTSettingsHelper::get_powertoys_general_save_file_location(), [this] {
toolbar.scheduleGeneralSettingsUpdate();
} },
_moduleSettingsWatcher{ PTSettingsHelper::get_module_save_file_location(get_key()), [this] { toolbar.scheduleModuleSettingsUpdate(); } }
{
init_settings();
_settingsUpdateChannel =
SerializedSharedMemory::create(CameraSettingsUpdateChannel::endpoint(), sizeof(CameraSettingsUpdateChannel), false);
if (_settingsUpdateChannel)
{
_settingsUpdateChannel->access([](auto memory) {
auto updatesChannel = new (memory._data) CameraSettingsUpdateChannel{};
});
}
sendSourceCameraNameUpdate();
sendOverlayImageUpdate();
}
inline VideoConferenceModule::~VideoConferenceModule()
{
instance->unmuteAll();
toolbar.hide();
}
const wchar_t* VideoConferenceModule::get_name()
{
return L"Video Conference";
}
const wchar_t* VideoConferenceModule::get_key()
{
return L"Video Conference";
}
bool VideoConferenceModule::get_config(wchar_t* buffer, int* buffer_size)
{
return true;
}
void VideoConferenceModule::set_config(const wchar_t* config)
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
values.save_to_settings_file();
}
catch (...)
{
LOG("VideoConferenceModule::set_config: exception during saving new settings values");
}
}
void VideoConferenceModule::init_settings()
{
try
{
PowerToysSettings::PowerToyValues powerToysSettings = PowerToysSettings::PowerToyValues::load_from_settings_file(L"Video Conference");
if (const auto val = powerToysSettings.get_json(L"mute_camera_and_microphone_hotkey"))
{
settings.cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_json(L"mute_microphone_hotkey"))
{
settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_json(L"mute_camera_hotkey"))
{
settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_position"))
{
settings.toolbarPositionString = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_monitor"))
{
settings.toolbarMonitorString = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"selected_camera"))
{
settings.selectedCamera = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"camera_overlay_image_path"))
{
settings.imageOverlayPath = val.value();
}
if (const auto val = powerToysSettings.get_bool_value(L"hide_toolbar_when_unmuted"))
{
toolbar.setHideToolbarWhenUnmuted(val.value());
}
if (const auto val = powerToysSettings.get_string_value(L"selected_mic"); val && *val != settings.selectedMicrophone)
{
settings.selectedMicrophone = *val;
updateControlledMicrophones(settings.selectedMicrophone);
}
}
catch (std::exception&)
{
// Error while loading from the settings file. Just let default values stay as they are.
}
try
{
auto loaded = PTSettingsHelper::load_general_settings();
std::wstring settings_theme{ static_cast<std::wstring_view>(loaded.GetNamedString(L"theme", L"system")) };
if (settings_theme != L"dark" && settings_theme != L"light")
{
settings_theme = L"system";
}
toolbar.setTheme(settings_theme);
}
catch (...)
{
}
}
void VideoConferenceModule::updateControlledMicrophones(const std::wstring_view new_mic)
{
for (auto& controlledMic : _controlledMicrophones)
{
controlledMic.set_muted(false);
}
_controlledMicrophones.clear();
_microphoneTrackedInUI = nullptr;
auto allMics = MicrophoneDevice::getAllActive();
if (new_mic == L"[All]")
{
_controlledMicrophones = std::move(allMics);
if (auto defaultMic = MicrophoneDevice::getDefault())
{
for (auto& controlledMic : _controlledMicrophones)
{
if (controlledMic.id() == defaultMic->id())
{
_microphoneTrackedInUI = &controlledMic;
break;
}
}
}
}
else
{
for (auto& controlledMic : allMics)
{
if (controlledMic.name() == new_mic)
{
_controlledMicrophones.emplace_back(std::move(controlledMic));
_microphoneTrackedInUI = &_controlledMicrophones[0];
break;
}
}
}
if (_microphoneTrackedInUI)
{
_microphoneTrackedInUI->set_mute_changed_callback([&](const bool muted) {
toolbar.setMicrophoneMute(muted);
});
toolbar.setMicrophoneMute(_microphoneTrackedInUI->muted());
}
}
void toggleProxyCamRegistration(const bool enable)
{
if (!is_process_elevated())
{
return;
}
auto vcmRoot = fs::path{ get_module_folderpath() } / "modules";
vcmRoot /= "VideoConference";
std::array<fs::path, 2> proxyFilters = { vcmRoot / "VideoConferenceProxyFilter_x64.dll", vcmRoot / "VideoConferenceProxyFilter_x86.dll" };
for (const auto filter : proxyFilters)
{
std::wstring params{ L"/s " };
if (!enable)
{
params += L"/u ";
}
params += '"';
params += filter;
params += '"';
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
sei.lpFile = L"regsvr32";
sei.lpParameters = params.c_str();
sei.nShow = SW_SHOWNORMAL;
ShellExecuteExW(&sei);
}
}
void VideoConferenceModule::enable()
{
if (!_enabled)
{
toggleProxyCamRegistration(true);
toolbar.setMicrophoneMute(getMicrophoneMuteState());
toolbar.setCameraMute(getVirtualCameraMuteState());
toolbar.show(settings.toolbarPositionString, settings.toolbarMonitorString);
_enabled = true;
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
if (IsDebuggerPresent())
{
return;
}
#endif
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
}
}
void VideoConferenceModule::unmuteAll()
{
if (getVirtualCameraMuteState())
{
reverseVirtualCameraMuteState();
}
if (getMicrophoneMuteState())
{
reverseMicrophoneMute();
}
}
void VideoConferenceModule::disable()
{
if (_enabled)
{
toggleProxyCamRegistration(false);
if (hook_handle)
{
bool success = UnhookWindowsHookEx(hook_handle);
if (success)
{
hook_handle = nullptr;
}
}
instance->unmuteAll();
toolbar.hide();
_enabled = false;
}
}
bool VideoConferenceModule::is_enabled()
{
return _enabled;
}
void VideoConferenceModule::destroy()
{
delete this;
instance = nullptr;
}
void VideoConferenceModule::sendSourceCameraNameUpdate()
{
if (!_settingsUpdateChannel.has_value() || settings.selectedCamera.empty())
{
return;
}
_settingsUpdateChannel->access([](auto memory) {
auto updatesChannel = reinterpret_cast<CameraSettingsUpdateChannel*>(memory._data);
updatesChannel->sourceCameraName.emplace();
std::copy(begin(settings.selectedCamera), end(settings.selectedCamera), begin(*updatesChannel->sourceCameraName));
});
}
void VideoConferenceModule::sendOverlayImageUpdate()
{
if (!_settingsUpdateChannel.has_value())
{
return;
}
_imageOverlayChannel.reset();
wchar_t powertoysDirectory[MAX_PATH + 1];
DWORD length = GetModuleFileNameW(nullptr, powertoysDirectory, MAX_PATH);
PathRemoveFileSpecW(powertoysDirectory);
std::wstring blankImagePath(powertoysDirectory);
blankImagePath += L"\\modules\\VideoConference\\black.bmp";
_imageOverlayChannel = SerializedSharedMemory::create_readonly(CameraOverlayImageChannel::endpoint(),
settings.imageOverlayPath != L"" ? settings.imageOverlayPath : blankImagePath);
const auto imageSize = static_cast<uint32_t>(_imageOverlayChannel->size());
_settingsUpdateChannel->access([imageSize](auto memory) {
auto updatesChannel = reinterpret_cast<CameraSettingsUpdateChannel*>(memory._data);
updatesChannel->overlayImageSize.emplace(imageSize);
updatesChannel->newOverlayImagePosted = true;
});
}

View file

@ -0,0 +1,86 @@
#pragma once
#include <common/SettingsAPI/FileWatcher.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <interface/powertoy_module_interface.h>
#include <common/SettingsAPI/settings_objects.h>
#include <MicrophoneDevice.h>
#include "Toolbar.h"
#include <SerializedSharedMemory.h>
extern class VideoConferenceModule* instance;
struct VideoConferenceSettings
{
PowerToysSettings::HotkeyObject cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, false, 78);
PowerToysSettings::HotkeyObject microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 65);
PowerToysSettings::HotkeyObject cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_settings(true, false, false, true, 79);
std::wstring toolbarPositionString;
std::wstring toolbarMonitorString;
std::wstring selectedCamera;
std::wstring imageOverlayPath;
std::wstring selectedMicrophone;
};
class VideoConferenceModule : public PowertoyModuleIface
{
public:
VideoConferenceModule();
~VideoConferenceModule();
virtual const wchar_t* get_name() override;
virtual bool get_config(wchar_t* buffer, int* buffer_size) override;
virtual void set_config(const wchar_t* config) override;
virtual void enable() override;
virtual void disable() override;
virtual bool is_enabled() override;
virtual void destroy() override;
virtual const wchar_t * get_key() override;
void sendSourceCameraNameUpdate();
void sendOverlayImageUpdate();
static void unmuteAll();
static void reverseMicrophoneMute();
static bool getMicrophoneMuteState();
static void reverseVirtualCameraMuteState();
static bool getVirtualCameraMuteState();
static bool getVirtualCameraInUse();
void onGeneralSettingsChanged();
void onModuleSettingsChanged();
private:
void init_settings();
void updateControlledMicrophones(const std::wstring_view new_mic);
// all callback methods and used by callback have to be static
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
static bool isKeyPressed(unsigned int keyCode);
static bool isHotkeyPressed(DWORD code, PowerToysSettings::HotkeyObject& hotkey);
static HHOOK hook_handle;
bool _enabled = false;
std::vector<MicrophoneDevice> _controlledMicrophones;
MicrophoneDevice* _microphoneTrackedInUI = nullptr;
std::optional<SerializedSharedMemory> _imageOverlayChannel;
std::optional<SerializedSharedMemory> _settingsUpdateChannel;
FileWatcher _generalSettingsWatcher;
FileWatcher _moduleSettingsWatcher;
static VideoConferenceSettings settings;
static Toolbar toolbar;
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View file

@ -0,0 +1,35 @@
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <interface/powertoy_module_interface.h>
#include "trace.h"
#include "VideoConferenceModule.h"
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
Trace::RegisterProvider();
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
{
if (!instance)
{
instance = new VideoConferenceModule();
return instance;
}
else
{
return nullptr;
}
}

View file

@ -0,0 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>

View file

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

View file

@ -0,0 +1,5 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.

View file

@ -0,0 +1,24 @@
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <Unknwn.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <string_view>
#include <optional>
#include <iostream>
#include <fstream>
#include <vector>
#include <functional>
#include <algorithm>
#include <Shobjidl.h>
#include <Shlwapi.h>
#include <common/Telemetry/ProjectTelemetry.h>

View file

@ -0,0 +1,57 @@
#include "pch.h"
#include "trace.h"
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
void Trace::RegisterProvider() noexcept
{
TraceLoggingRegister(g_hProvider);
}
void Trace::UnregisterProvider() noexcept
{
TraceLoggingUnregister(g_hProvider);
}
void Trace::SettingsChanged(const struct VideoConferenceSettings& settings) noexcept
{
bool CustomOverlayImage = (settings.imageOverlayPath.length() > 0);
TraceLoggingWrite(
g_hProvider,
"VideoConference_SettingsChanged",
TraceLoggingWideString(settings.toolbarPositionString.c_str(), "ToolbarPosition"),
TraceLoggingWideString(settings.toolbarMonitorString.c_str(), "ToolbarMonitorSelection"),
TraceLoggingBool(CustomOverlayImage, "CustomImageOverlayUsed"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::MicrophoneMuted() noexcept
{
TraceLoggingWrite(
g_hProvider,
"VideoConference_MicrophoneMuted",
TraceLoggingBoolean(true, "MicrophoneMuted"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::CameraMuted() noexcept
{
TraceLoggingWrite(
g_hProvider,
"VideoConference_CameraMuted",
TraceLoggingBoolean(true, "CameraMuted"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View file

@ -0,0 +1,12 @@
#pragma once
#include "VideoConferenceModule.h"
class Trace
{
public:
static void RegisterProvider() noexcept;
static void UnregisterProvider() noexcept;
static void SettingsChanged(const struct VideoConferenceSettings &settings) noexcept;
static void MicrophoneMuted() noexcept;
static void CameraMuted() noexcept;
};

View file

@ -0,0 +1,118 @@
#include "DirectShowUtils.h"
#include <algorithm>
unique_media_type_ptr CopyMediaType(const AM_MEDIA_TYPE* source)
{
unique_media_type_ptr target{ static_cast<AM_MEDIA_TYPE*>(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))) };
*target = *source;
if (source->cbFormat)
{
target->pbFormat = static_cast<BYTE*>(CoTaskMemAlloc(source->cbFormat));
std::copy(source->pbFormat, source->pbFormat + source->cbFormat, target->pbFormat);
}
if (target->pUnk)
{
target->pUnk->AddRef();
}
return target;
}
wil::com_ptr_nothrow<IMemAllocator> GetPinAllocator(wil::com_ptr_nothrow<IPin>& inputPin)
{
if (!inputPin)
{
return nullptr;
}
wil::com_ptr_nothrow<IMemAllocator> allocator;
if (auto memInput = inputPin.try_query<IMemInputPin>(); memInput)
{
memInput->GetAllocator(&allocator);
return allocator;
}
return nullptr;
}
unique_media_type_ptr CopyMediaType(const unique_media_type_ptr& source)
{
return CopyMediaType(source.get());
}
void MyFreeMediaType(AM_MEDIA_TYPE& mt)
{
if (mt.cbFormat != 0)
{
CoTaskMemFree(mt.pbFormat);
mt.cbFormat = 0;
mt.pbFormat = nullptr;
}
if (mt.pUnk != nullptr)
{
mt.pUnk->Release();
mt.pUnk = nullptr;
}
}
void MyDeleteMediaType(AM_MEDIA_TYPE* pmt)
{
if (!pmt)
{
return;
}
MyFreeMediaType(*pmt);
CoTaskMemFree(const_cast<AM_MEDIA_TYPE*>(pmt));
}
HRESULT MediaTypeEnumerator::Next(ULONG cObjects, AM_MEDIA_TYPE** outObjects, ULONG* pcFetched)
{
if (!outObjects)
{
return E_POINTER;
}
ULONG fetched = 0;
ULONG toFetch = cObjects;
while (toFetch-- && _pos < _objects.size())
{
auto copy = CopyMediaType(_objects[_pos++]);
outObjects[fetched++] = copy.release();
}
if (pcFetched)
{
*pcFetched = fetched;
}
return fetched == cObjects ? S_OK : S_FALSE;
}
HRESULT MediaTypeEnumerator::Skip(ULONG cObjects)
{
_pos += cObjects;
return _pos < _objects.size() ? S_OK : S_FALSE;
}
HRESULT MediaTypeEnumerator::Reset()
{
_pos = 0;
return S_OK;
}
HRESULT MediaTypeEnumerator::Clone(IEnumMediaTypes** ppEnum)
{
auto cloned = winrt::make_self<MediaTypeEnumerator>();
cloned->_objects.resize(_objects.size());
for (size_t i = 0; i < _objects.size(); ++i)
{
cloned->_objects[i] = CopyMediaType(_objects[i]);
}
cloned->_pos = _pos;
cloned.as<IEnumMediaTypes>().copy_to(ppEnum);
return S_OK;
}

View file

@ -0,0 +1,88 @@
#pragma once
#include <initguid.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <dshow.h>
#include <wil/com.h>
#include <winrt/Windows.Foundation.h>
#include <vector>
#include "Logging.h"
void MyDeleteMediaType(AM_MEDIA_TYPE* pmt);
using unique_media_type_ptr =
wistd::unique_ptr<AM_MEDIA_TYPE, wil::function_deleter<decltype(&MyDeleteMediaType), MyDeleteMediaType>>;
unique_media_type_ptr CopyMediaType(const unique_media_type_ptr& source);
unique_media_type_ptr CopyMediaType(const AM_MEDIA_TYPE* source);
template<typename ObjectInterface, typename EnumeratorInterface>
struct ObjectEnumerator : public winrt::implements<ObjectEnumerator<ObjectInterface, EnumeratorInterface>, EnumeratorInterface>
{
std::vector<wil::com_ptr_nothrow<ObjectInterface>> _objects;
ULONG _pos = 0;
HRESULT STDMETHODCALLTYPE Next(ULONG cObjects, ObjectInterface** outObjects, ULONG* pcFetched) override
{
if (!outObjects)
{
return E_POINTER;
}
ULONG fetched = 0;
ULONG toFetch = cObjects;
while (toFetch-- && _pos < _objects.size())
{
_objects[_pos++].copy_to(&outObjects[fetched++]);
}
if (pcFetched)
{
*pcFetched = fetched;
}
return fetched == cObjects ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE Skip(ULONG cObjects) override
{
_pos += cObjects;
return _pos < _objects.size() ? S_OK : S_FALSE;
}
HRESULT STDMETHODCALLTYPE Reset() override
{
_pos = 0;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Clone(EnumeratorInterface** ppEnum) override
{
auto cloned = winrt::make_self<ObjectEnumerator>();
cloned->_objects = _objects;
cloned->_pos = _pos;
cloned.as<EnumeratorInterface>().copy_to(ppEnum);
return S_OK;
}
virtual ~ObjectEnumerator() = default;
};
struct MediaTypeEnumerator : public winrt::implements<MediaTypeEnumerator, IEnumMediaTypes>
{
std::vector<unique_media_type_ptr> _objects;
ULONG _pos = 0;
HRESULT STDMETHODCALLTYPE Next(ULONG cObjects, AM_MEDIA_TYPE** outObjects, ULONG* pcFetched) override;
HRESULT STDMETHODCALLTYPE Skip(ULONG cObjects) override;
HRESULT STDMETHODCALLTYPE Reset() override;
HRESULT STDMETHODCALLTYPE Clone(IEnumMediaTypes** ppEnum) override;
virtual ~MediaTypeEnumerator() = default;
};
wil::com_ptr_nothrow<IMemAllocator> GetPinAllocator(wil::com_ptr_nothrow<IPin>& inputPin);

View file

@ -0,0 +1,425 @@
#include <initguid.h>
#include <dxgiformat.h>
#include <assert.h>
#include <winrt/base.h>
#pragma warning(push)
#pragma warning(disable : 4005)
#include <wincodec.h>
#pragma warning(pop)
#include <memory>
#include <mfapi.h>
#include <shcore.h>
#include <algorithm>
#include <wil/resource.h>
#include <wil/com.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mftransform.h>
#include <dshow.h>
#include <Wincodecsdk.h>
#include <shlwapi.h>
#include "Logging.h"
IWICImagingFactory* _GetWIC() noexcept
{
static IWICImagingFactory* s_Factory = nullptr;
if (s_Factory)
{
return s_Factory;
}
OK_OR_BAIL(CoCreateInstance(
CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWICImagingFactory), (LPVOID*)&s_Factory));
return s_Factory;
}
bool ReencodeJPGImage(BYTE* imageBuf, const DWORD imageSize, DWORD& reencodedSize)
{
auto pWIC = _GetWIC();
wil::com_ptr_nothrow<IStream> imageStream = SHCreateMemStream(imageBuf, imageSize);
if (!imageStream)
{
return false;
}
// Decode jpg into bitmap
wil::com_ptr_nothrow<IWICBitmapDecoder> bitmapDecoder;
OK_OR_BAIL(pWIC->CreateDecoderFromStream(imageStream.get(), nullptr, WICDecodeMetadataCacheOnLoad, &bitmapDecoder));
wil::com_ptr_nothrow<IWICBitmapFrameDecode> decodedFrame;
OK_OR_BAIL(bitmapDecoder->GetFrame(0, &decodedFrame));
wil::com_ptr_nothrow<IWICBitmapSource> bitmap;
bitmap.attach(decodedFrame.detach());
UINT width = 0, height = 0;
OK_OR_BAIL(bitmap->GetSize(&width, &height));
// Initialize jpg encoder
wil::com_ptr_nothrow<IWICBitmapEncoder> encoder;
OK_OR_BAIL(pWIC->CreateEncoder(GUID_ContainerFormatJpeg, nullptr, &encoder));
wil::com_ptr_nothrow<IStream> outputStream;
OK_OR_BAIL(CreateStreamOnHGlobal(nullptr, true, &outputStream));
OK_OR_BAIL(encoder->Initialize(outputStream.get(), WICBitmapEncoderNoCache));
wil::com_ptr_nothrow<IWICBitmapFrameEncode> encodedFrame;
wil::com_ptr_nothrow<IPropertyBag2> encoderOptions;
OK_OR_BAIL(encoder->CreateNewFrame(&encodedFrame, &encoderOptions));
ULONG nProperties = 0;
OK_OR_BAIL(encoderOptions->CountProperties(&nProperties));
for (ULONG propIdx = 0; propIdx < nProperties; ++propIdx)
{
PROPBAG2 propBag{};
ULONG _;
OK_OR_BAIL(encoderOptions->GetPropertyInfo(propIdx, 1, &propBag, &_));
if (propBag.pstrName == std::wstring_view{ L"ImageQuality" })
{
wil::unique_variant variant;
variant.vt = VT_R4;
variant.fltVal = 0.1f;
OK_OR_BAIL(encoderOptions->Write(1, &propBag, &variant));
LOG("Successfully set jpg compression quality");
// skip the rest of the properties
propIdx = nProperties;
}
CoTaskMemFree(propBag.pstrName);
}
OK_OR_BAIL(encodedFrame->Initialize(encoderOptions.get()));
WICPixelFormatGUID intermediateFormat = GUID_WICPixelFormat24bppRGB;
OK_OR_BAIL(encodedFrame->SetPixelFormat(&intermediateFormat));
OK_OR_BAIL(encodedFrame->SetSize(width, height));
// Commit the image encoding
OK_OR_BAIL(encodedFrame->WriteSource(bitmap.get(), nullptr));
OK_OR_BAIL(encodedFrame->Commit());
OK_OR_BAIL(encoder->Commit());
STATSTG intermediateStreamStat{};
OK_OR_BAIL(outputStream->Stat(&intermediateStreamStat, STATFLAG_NONAME));
const ULONGLONG jpgStreamSize = intermediateStreamStat.cbSize.QuadPart;
HGLOBAL streamMemoryHandle{};
OK_OR_BAIL(GetHGlobalFromStream(outputStream.get(), &streamMemoryHandle));
auto jpgStreamMemory = static_cast<uint8_t*>(GlobalLock(streamMemoryHandle));
std::copy(jpgStreamMemory, jpgStreamMemory + jpgStreamSize, imageBuf);
auto unlockJpgStreamMemory = wil::scope_exit([jpgStreamMemory] { GlobalUnlock(jpgStreamMemory); });
reencodedSize = (DWORD)jpgStreamSize;
return true;
}
wil::com_ptr_nothrow<IWICBitmapSource> LoadAsRGB24BitmapWithSize(IWICImagingFactory* pWIC,
wil::com_ptr_nothrow<IStream> image,
const UINT targetWidth,
const UINT targetHeight)
{
wil::com_ptr_nothrow<IWICBitmapSource> bitmap;
// Initialize image bitmap decoder from filename and get the image frame
wil::com_ptr_nothrow<IWICBitmapDecoder> bitmapDecoder;
OK_OR_BAIL(pWIC->CreateDecoderFromStream(image.get(), nullptr, WICDecodeMetadataCacheOnLoad, &bitmapDecoder));
wil::com_ptr_nothrow<IWICBitmapFrameDecode> decodedFrame;
OK_OR_BAIL(bitmapDecoder->GetFrame(0, &decodedFrame));
UINT imageWidth = 0, imageHeight = 0;
OK_OR_BAIL(decodedFrame->GetSize(&imageWidth, &imageHeight));
// Scale the image if required
if (targetWidth != imageWidth || targetHeight != imageHeight)
{
wil::com_ptr_nothrow<IWICBitmapScaler> scaler;
OK_OR_BAIL(pWIC->CreateBitmapScaler(&scaler));
OK_OR_BAIL(
scaler->Initialize(decodedFrame.get(), targetWidth, targetHeight, WICBitmapInterpolationModeHighQualityCubic));
bitmap.attach(scaler.detach());
}
else
{
bitmap.attach(decodedFrame.detach());
}
WICPixelFormatGUID pixelFormat{};
OK_OR_BAIL(bitmap->GetPixelFormat(&pixelFormat));
const auto targetPixelFormat = GUID_WICPixelFormat24bppBGR;
if (pixelFormat != targetPixelFormat)
{
wil::com_ptr_nothrow<IWICBitmapSource> convertedBitmap;
if (SUCCEEDED(WICConvertBitmapSource(targetPixelFormat, bitmap.get(), &convertedBitmap)))
{
return convertedBitmap;
}
}
return bitmap;
}
wil::com_ptr_nothrow<IStream> EncodeBitmapToContainer(IWICImagingFactory* pWIC,
wil::com_ptr_nothrow<IWICBitmapSource> bitmap,
const GUID& containerGUID,
const UINT width,
const UINT height,
const float quality)
{
wil::com_ptr_nothrow<IWICBitmapEncoder> encoder;
pWIC->CreateEncoder(containerGUID, nullptr, &encoder);
if (!encoder)
{
return nullptr;
}
// Prepare the encoder output memory stream and encoding params
wil::com_ptr_nothrow<IStream> encodedBitmap;
OK_OR_BAIL(CreateStreamOnHGlobal(nullptr, true, &encodedBitmap));
OK_OR_BAIL(encoder->Initialize(encodedBitmap.get(), WICBitmapEncoderNoCache));
wil::com_ptr_nothrow<IWICBitmapFrameEncode> encodedFrame;
wil::com_ptr_nothrow<IPropertyBag2> encoderOptions;
OK_OR_BAIL(encoder->CreateNewFrame(&encodedFrame, &encoderOptions));
ULONG nProperties = 0;
OK_OR_BAIL(encoderOptions->CountProperties(&nProperties));
for (ULONG propIdx = 0; propIdx < nProperties; ++propIdx)
{
PROPBAG2 propBag{};
ULONG _;
OK_OR_BAIL(encoderOptions->GetPropertyInfo(propIdx, 1, &propBag, &_));
if (propBag.pstrName == std::wstring_view{ L"ImageQuality" })
{
wil::unique_variant variant;
variant.vt = VT_R4;
variant.fltVal = quality;
OK_OR_BAIL(encoderOptions->Write(1, &propBag, &variant));
LOG("Successfully set jpg compression quality");
// skip the rest of the properties
propIdx = nProperties;
}
CoTaskMemFree(propBag.pstrName);
}
OK_OR_BAIL(encodedFrame->Initialize(encoderOptions.get()));
WICPixelFormatGUID intermediateFormat = GUID_WICPixelFormat24bppRGB;
OK_OR_BAIL(encodedFrame->SetPixelFormat(&intermediateFormat));
OK_OR_BAIL(encodedFrame->SetSize(width, height));
// Commit the image encoding
OK_OR_BAIL(encodedFrame->WriteSource(bitmap.get(), nullptr));
OK_OR_BAIL(encodedFrame->Commit());
OK_OR_BAIL(encoder->Commit());
return encodedBitmap;
}
IMFSample* ConvertIMFVideoSample(const MFT_REGISTER_TYPE_INFO& inputType,
IMFMediaType* outputMediaType,
const wil::com_ptr_nothrow<IMFSample>& inputSample,
const UINT width,
const UINT height)
{
IMFActivate** ppVDActivate = nullptr;
UINT32 count = 0;
MFT_REGISTER_TYPE_INFO outputType = { MFMediaType_Video, {} };
outputMediaType->GetGUID(MF_MT_SUBTYPE, &outputType.guidSubtype);
const std::array<GUID, 3> transformerCategories = {
MFT_CATEGORY_VIDEO_PROCESSOR, MFT_CATEGORY_VIDEO_DECODER, MFT_CATEGORY_VIDEO_ENCODER
};
for (const auto& transformerCategory : transformerCategories)
{
OK_OR_BAIL(MFTEnumEx(transformerCategory, MFT_ENUM_FLAG_SYNCMFT, &inputType, &outputType, &ppVDActivate, &count));
if (count != 0)
{
break;
}
}
wil::com_ptr_nothrow<IMFTransform> videoTransformer;
bool videoDecoderActivated = false;
for (UINT32 i = 0; i < count; ++i)
{
if (!videoDecoderActivated && !FAILED(ppVDActivate[i]->ActivateObject(IID_PPV_ARGS(&videoTransformer))))
{
videoDecoderActivated = true;
}
ppVDActivate[i]->Release();
}
if (count)
{
CoTaskMemFree(ppVDActivate);
}
if (!videoDecoderActivated)
{
LOG("No converter avialable for the selected format");
return nullptr;
}
auto shutdownVideoDecoder = wil::scope_exit([&videoTransformer] { MFShutdownObject(videoTransformer.get()); });
// Set input/output types for the decoder
wil::com_ptr_nothrow<IMFMediaType> intermediateFrameMediaType;
OK_OR_BAIL(MFCreateMediaType(&intermediateFrameMediaType));
intermediateFrameMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
intermediateFrameMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB24);
intermediateFrameMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
intermediateFrameMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
OK_OR_BAIL(MFSetAttributeSize(intermediateFrameMediaType.get(), MF_MT_FRAME_SIZE, width, height));
OK_OR_BAIL(MFSetAttributeRatio(intermediateFrameMediaType.get(), MF_MT_PIXEL_ASPECT_RATIO, width, height));
OK_OR_BAIL(videoTransformer->SetInputType(0, intermediateFrameMediaType.get(), 0));
OK_OR_BAIL(videoTransformer->SetOutputType(0, outputMediaType, 0));
// Process the input sample
OK_OR_BAIL(videoTransformer->ProcessInput(0, inputSample.get(), 0));
// Check whether we need to allocate output sample and buffer ourselves
MFT_OUTPUT_STREAM_INFO outputStreamInfo{};
OK_OR_BAIL(videoTransformer->GetOutputStreamInfo(0, &outputStreamInfo));
const bool onlyProvidesSamples = outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_PROVIDES_SAMPLES;
const bool canProvideSamples = outputStreamInfo.dwFlags & MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES;
const bool mustAllocateSample =
(!onlyProvidesSamples && !canProvideSamples) ||
(!onlyProvidesSamples && (outputStreamInfo.dwFlags & MFT_PROCESS_OUTPUT_DISCARD_WHEN_NO_BUFFER));
MFT_OUTPUT_DATA_BUFFER outputSamples{};
IMFSample* outputSample = nullptr;
// If so, do the allocation
if (mustAllocateSample)
{
OK_OR_BAIL(MFCreateSample(&outputSample));
OK_OR_BAIL(outputSample->SetSampleDuration(333333));
OK_OR_BAIL(outputSample->SetSampleTime(1));
OK_OR_BAIL(outputSample->SetUINT32(MF_MT_VIDEO_ROTATION, MFVideoRotationFormat::MFVideoRotationFormat_0));
IMFMediaBuffer* outputMediaBuffer = nullptr;
OK_OR_BAIL(
MFCreateAlignedMemoryBuffer(outputStreamInfo.cbSize, outputStreamInfo.cbAlignment - 1, &outputMediaBuffer));
OK_OR_BAIL(outputMediaBuffer->SetCurrentLength(outputStreamInfo.cbSize));
OK_OR_BAIL(outputSample->AddBuffer(outputMediaBuffer));
outputSamples.pSample = outputSample;
}
// Finally, produce the output sample
DWORD processStatus = 0;
if (failed(videoTransformer->ProcessOutput(0, 1, &outputSamples, &processStatus)))
{
LOG("Failed to convert image frame");
}
if (outputSamples.pEvents)
{
outputSamples.pEvents->Release();
}
return outputSamples.pSample;
}
wil::com_ptr_nothrow<IMFSample> LoadImageAsSample(wil::com_ptr_nothrow<IStream> imageStream,
IMFMediaType* sampleMediaType,
const float quality) noexcept
{
UINT targetWidth = 0;
UINT targetHeight = 0;
OK_OR_BAIL(MFGetAttributeSize(sampleMediaType, MF_MT_FRAME_SIZE, &targetWidth, &targetHeight));
MFT_REGISTER_TYPE_INFO outputType = { MFMediaType_Video, {} };
OK_OR_BAIL(sampleMediaType->GetGUID(MF_MT_SUBTYPE, &outputType.guidSubtype));
IWICImagingFactory* pWIC = _GetWIC();
if (!pWIC)
{
LOG("Failed to create IWICImagingFactory");
return nullptr;
}
if (!imageStream)
{
return nullptr;
}
const auto srcImageBitmap = LoadAsRGB24BitmapWithSize(pWIC, imageStream, targetWidth, targetHeight);
if (!srcImageBitmap)
{
return nullptr;
}
// First, let's create a sample containing RGB24 bitmap
IMFSample* outputSample = nullptr;
OK_OR_BAIL(MFCreateSample(&outputSample));
OK_OR_BAIL(outputSample->SetUINT32(MF_MT_VIDEO_ROTATION, MFVideoRotationFormat::MFVideoRotationFormat_0));
OK_OR_BAIL(outputSample->SetSampleDuration(333333));
OK_OR_BAIL(outputSample->SetSampleTime(1));
IMFMediaBuffer* outputMediaBuffer = nullptr;
const DWORD nPixelBytes = targetWidth * targetHeight * 3;
OK_OR_BAIL(MFCreateAlignedMemoryBuffer(nPixelBytes, MF_64_BYTE_ALIGNMENT, &outputMediaBuffer));
const UINT stride = 3 * targetWidth;
DWORD max_length = 0, current_length = 0;
BYTE* sampleBufferMemory = nullptr;
OK_OR_BAIL(outputMediaBuffer->Lock(&sampleBufferMemory, &max_length, &current_length));
OK_OR_BAIL(srcImageBitmap->CopyPixels(nullptr, stride, nPixelBytes, sampleBufferMemory));
OK_OR_BAIL(outputMediaBuffer->Unlock());
OK_OR_BAIL(outputMediaBuffer->SetCurrentLength(nPixelBytes));
OK_OR_BAIL(outputSample->AddBuffer(outputMediaBuffer));
if (outputType.guidSubtype == MFVideoFormat_RGB24)
{
return outputSample;
}
// Special case for mjpg, since we need to use jpg container for it instead of supplying raw pixels
if (outputType.guidSubtype == MFVideoFormat_MJPG)
{
// Use an intermediate jpg container sample which will be transcoded to the target format
wil::com_ptr_nothrow<IStream> jpgStream =
EncodeBitmapToContainer(pWIC, srcImageBitmap, GUID_ContainerFormatJpeg, targetWidth, targetHeight, quality);
// Obtain stream size and lock its memory pointer
STATSTG intermediateStreamStat{};
OK_OR_BAIL(jpgStream->Stat(&intermediateStreamStat, STATFLAG_NONAME));
const ULONGLONG jpgStreamSize = intermediateStreamStat.cbSize.QuadPart;
HGLOBAL streamMemoryHandle{};
OK_OR_BAIL(GetHGlobalFromStream(jpgStream.get(), &streamMemoryHandle));
auto jpgStreamMemory = static_cast<uint8_t*>(GlobalLock(streamMemoryHandle));
auto unlockJpgStreamMemory = wil::scope_exit([jpgStreamMemory] { GlobalUnlock(jpgStreamMemory); });
// Create a sample from the input image buffer
wil::com_ptr_nothrow<IMFSample> jpgSample;
OK_OR_BAIL(MFCreateSample(&jpgSample));
OK_OR_BAIL(jpgSample->SetUINT32(MF_MT_VIDEO_ROTATION, MFVideoRotationFormat::MFVideoRotationFormat_0));
IMFMediaBuffer* inputMediaBuffer = nullptr;
OK_OR_BAIL(MFCreateAlignedMemoryBuffer(static_cast<DWORD>(jpgStreamSize), MF_64_BYTE_ALIGNMENT, &inputMediaBuffer));
BYTE* inputBuf = nullptr;
OK_OR_BAIL(inputMediaBuffer->Lock(&inputBuf, &max_length, &current_length));
if (max_length < jpgStreamSize)
{
return nullptr;
}
std::copy(jpgStreamMemory, jpgStreamMemory + jpgStreamSize, inputBuf);
unlockJpgStreamMemory.reset();
OK_OR_BAIL(inputMediaBuffer->Unlock());
OK_OR_BAIL(inputMediaBuffer->SetCurrentLength(static_cast<DWORD>(jpgStreamSize)));
OK_OR_BAIL(jpgSample->AddBuffer(inputMediaBuffer));
return jpgSample;
}
// Now we are ready to convert it to the requested media type
MFT_REGISTER_TYPE_INFO intermediateType = { MFMediaType_Video, MFVideoFormat_RGB24 };
// But if no conversion is needed, just return the input sample
return ConvertIMFVideoSample(intermediateType, sampleMediaType, outputSample, targetWidth, targetHeight);
}

View file

@ -0,0 +1,634 @@
#include "Logging.h"
#include "VideoCaptureDevice.h"
#include <wil/resource.h>
#include <cguid.h>
struct VideoCaptureReceiverFilter : winrt::implements<VideoCaptureReceiverFilter, IBaseFilter, IAMFilterMiscFlags>
{
FILTER_STATE _state = State_Stopped;
IFilterGraph* _graph = nullptr;
wil::com_ptr_nothrow<IPin> _videoReceiverPin;
ULONG STDMETHODCALLTYPE GetMiscFlags() override { return AM_FILTER_MISC_FLAGS_IS_RENDERER; }
HRESULT STDMETHODCALLTYPE GetClassID(CLSID*) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE Stop() override
{
_state = State_Stopped;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Pause() override
{
_state = State_Paused;
return S_OK;
}
HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME) override
{
_state = State_Running;
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetState(DWORD, FILTER_STATE* outState) override
{
*outState = _state;
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock** outRefClock) override
{
*outRefClock = nullptr;
return NOERROR;
}
HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock*) override { return S_OK; }
HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins** ppEnum) override
{
auto enumerator = winrt::make_self<ObjectEnumerator<IPin, IEnumPins>>();
enumerator->_objects.emplace_back(_videoReceiverPin);
*ppEnum = enumerator.detach();
return S_OK;
}
HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR, IPin**) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR) override
{
_graph = pGraph;
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO* pInfo) override
{
std::copy(std::begin(NAME), std::end(NAME), pInfo->achName);
if (_graph)
{
pInfo->pGraph = _graph;
_graph->AddRef();
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR* pVendorInfo) override
{
auto info = static_cast<LPWSTR>(CoTaskMemAlloc(sizeof(VENDOR)));
std::copy(std::begin(VENDOR), std::end(VENDOR), info);
*pVendorInfo = info;
return S_OK;
}
virtual ~VideoCaptureReceiverFilter() = default;
constexpr static inline wchar_t NAME[] = L"PowerToysVCMCaptureFilter";
constexpr static inline wchar_t VENDOR[] = L"Microsoft Corporation";
};
struct VideoCaptureReceiverPin : winrt::implements<VideoCaptureReceiverPin, IPin, IMemInputPin>
{
VideoCaptureReceiverFilter* _owningFilter = nullptr;
unique_media_type_ptr _expectedMediaType;
wil::com_ptr_nothrow<IPin> _captureInputPin;
unique_media_type_ptr _inputCaptureMediaType;
std::atomic_bool _flushing = false;
VideoCaptureDevice::callback_t _frameCallback;
wil::com_ptr_nothrow<IMemAllocator> _allocator;
VideoCaptureReceiverPin(unique_media_type_ptr mediaType, VideoCaptureReceiverFilter* filter) :
_expectedMediaType{ std::move(mediaType) }, _owningFilter{ filter }
{
}
HRESULT STDMETHODCALLTYPE Connect(IPin*, const AM_MEDIA_TYPE* pmt) override
{
if (_owningFilter->_state == State_Running)
{
return VFW_E_NOT_STOPPED;
}
if (_captureInputPin)
{
return VFW_E_ALREADY_CONNECTED;
}
if (!pmt || pmt->majortype == GUID_NULL)
{
return S_OK;
}
if (pmt->majortype != _expectedMediaType->majortype || pmt->subtype != _expectedMediaType->subtype)
{
return S_FALSE;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt) override
{
if (!pConnector || !pmt)
{
return E_POINTER;
}
if (_captureInputPin)
{
return VFW_E_ALREADY_CONNECTED;
}
if (_owningFilter->_state != State_Stopped)
{
return VFW_E_NOT_STOPPED;
}
if (QueryAccept(pmt) != S_OK)
{
return VFW_E_TYPE_NOT_ACCEPTED;
}
_captureInputPin = pConnector;
_inputCaptureMediaType = CopyMediaType(pmt);
return S_OK;
}
HRESULT STDMETHODCALLTYPE Disconnect() override
{
_allocator.reset();
_captureInputPin.reset();
_inputCaptureMediaType.reset();
return S_OK;
}
HRESULT STDMETHODCALLTYPE ConnectedTo(IPin** pPin) override
{
if (!_captureInputPin)
{
return VFW_E_NOT_CONNECTED;
}
_captureInputPin.copy_to(pPin);
return S_OK;
}
HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE* pmt) override
{
if (!pmt)
{
return E_POINTER;
}
if (!_inputCaptureMediaType)
{
return VFW_E_NOT_CONNECTED;
}
*pmt = *CopyMediaType(_inputCaptureMediaType).release();
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO* pInfo) override
{
if (!pInfo)
{
return E_POINTER;
}
pInfo->pFilter = _owningFilter;
if (_owningFilter)
{
_owningFilter->AddRef();
}
pInfo->dir = PINDIR_INPUT;
std::copy(std::begin(NAME), std::end(NAME), pInfo->achName);
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION* pPinDir) override
{
if (!pPinDir)
{
return E_POINTER;
}
*pPinDir = PINDIR_INPUT;
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryId(LPWSTR* lpId) override
{
if (!lpId)
{
return E_POINTER;
}
*lpId = static_cast<LPWSTR>(CoTaskMemAlloc(sizeof(NAME)));
std::copy(std::begin(NAME), std::end(NAME), *lpId);
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE* pmt) override
{
if (!pmt)
{
return E_POINTER;
}
if (pmt->majortype != _expectedMediaType->majortype || pmt->subtype != _expectedMediaType->subtype)
{
return S_FALSE;
}
if (_captureInputPin)
{
_inputCaptureMediaType.reset(const_cast<AM_MEDIA_TYPE*>(pmt));
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes** ppEnum) override
{
if (!ppEnum)
{
return E_POINTER;
}
auto enumerator = winrt::make_self<MediaTypeEnumerator>();
enumerator->_objects.emplace_back(CopyMediaType(_expectedMediaType));
*ppEnum = enumerator.detach();
return S_OK;
}
HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin**, ULONG*) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE EndOfStream() override { return S_OK; }
HRESULT STDMETHODCALLTYPE BeginFlush() override
{
_flushing = true;
return S_OK;
}
HRESULT STDMETHODCALLTYPE EndFlush() override
{
_flushing = false;
return S_OK;
}
HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME, REFERENCE_TIME, double) override { return S_OK; }
HRESULT STDMETHODCALLTYPE GetAllocator(IMemAllocator** allocator) override
{
VERBOSE_LOG;
if (!_allocator)
{
return VFW_E_NO_ALLOCATOR;
}
_allocator.copy_to(allocator);
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyAllocator(IMemAllocator* allocator, BOOL readOnly) override
{
VERBOSE_LOG;
LOG(readOnly ? "Allocator READONLY: true" : "Allocator READONLY: false");
_allocator = allocator;
return S_OK;
}
HRESULT STDMETHODCALLTYPE GetAllocatorRequirements(ALLOCATOR_PROPERTIES*) override { return E_NOTIMPL; }
HRESULT STDMETHODCALLTYPE Receive(IMediaSample* pSample) override
{
if (_flushing)
{
return S_FALSE;
}
if (!pSample)
{
return E_POINTER;
}
if (pSample && _frameCallback)
{
_frameCallback(pSample);
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE ReceiveMultiple(IMediaSample** pSamples, long nSamples, long* nSamplesProcessed) override
{
if (!pSamples && nSamples)
{
return E_POINTER;
}
if (_flushing)
{
return S_FALSE;
}
for (long i = 0; i < nSamples; i++)
{
Receive(pSamples[i]);
}
*nSamplesProcessed = nSamples;
return S_OK;
}
HRESULT STDMETHODCALLTYPE ReceiveCanBlock() override { return S_FALSE; }
virtual ~VideoCaptureReceiverPin() = default;
constexpr static inline wchar_t NAME[] = L"PowerToysVCMCapturePin";
};
constexpr long MINIMAL_FPS_ALLOWED = 29;
const char* GetMediaSubTypeString(const GUID& guid)
{
if (guid == MEDIASUBTYPE_RGB24)
{
return "MEDIASUBTYPE_RGB24";
}
if (guid == MEDIASUBTYPE_YUY2)
{
return "MEDIASUBTYPE_YUY2";
}
if (guid == MEDIASUBTYPE_MJPG)
{
return "MEDIASUBTYPE_MJPG";
}
if (guid == MEDIASUBTYPE_NV12)
{
return "MEDIASUBTYPE_NV12";
}
return "MEDIASUBTYPE_UNKNOWN";
}
std::optional<VideoStreamFormat> SelectBestMediaType(wil::com_ptr_nothrow<IPin>& pin)
{
VERBOSE_LOG;
wil::com_ptr_nothrow<IEnumMediaTypes> mediaTypeEnum;
if (pin->EnumMediaTypes(&mediaTypeEnum); !mediaTypeEnum)
{
return std::nullopt;
}
ULONG _ = 0;
VideoStreamFormat bestFormat;
unique_media_type_ptr mt;
while (mediaTypeEnum->Next(1, wil::out_param(mt), &_) == S_OK)
{
if (mt->majortype != MEDIATYPE_Video)
{
continue;
}
auto format = reinterpret_cast<VIDEOINFOHEADER*>(mt->pbFormat);
if (!format || !format->AvgTimePerFrame)
{
LOG("VideoInfoHeader not found");
continue;
}
const auto formatAvgFPS = 10000000LL / format->AvgTimePerFrame;
if (format->AvgTimePerFrame > bestFormat.avgFrameTime || formatAvgFPS < MINIMAL_FPS_ALLOWED)
{
continue;
}
if (format->bmiHeader.biWidth < bestFormat.width || format->bmiHeader.biHeight < bestFormat.height)
{
continue;
}
if (mt->subtype != MEDIASUBTYPE_YUY2 && mt->subtype != MEDIASUBTYPE_MJPG && mt->subtype != MEDIASUBTYPE_RGB24)
{
OLECHAR* guidString;
StringFromCLSID(mt->subtype, &guidString);
LOG("Skipping mediatype due to unsupported subtype: ");
LOG(guidString);
::CoTaskMemFree(guidString);
continue;
}
bestFormat.avgFrameTime = format->AvgTimePerFrame;
bestFormat.width = format->bmiHeader.biWidth;
bestFormat.height = format->bmiHeader.biHeight;
bestFormat.mediaType = std::move(mt);
}
if (!bestFormat.mediaType)
{
LOG(L"Couldn't select a suitable media format");
return std::nullopt;
}
char selectedFormat[512]{};
sprintf_s(selectedFormat, "Selected media format: %s %ldx%ld %lld fps", GetMediaSubTypeString(bestFormat.mediaType->subtype), bestFormat.width, bestFormat.height, 10000000LL / bestFormat.avgFrameTime);
LOG(selectedFormat);
return std::move(bestFormat);
}
std::vector<VideoCaptureDeviceInfo> VideoCaptureDevice::ListAll()
{
std::vector<VideoCaptureDeviceInfo> devices;
auto enumeratorFactory = wil::CoCreateInstanceNoThrow<ICreateDevEnum>(CLSID_SystemDeviceEnum);
if (!enumeratorFactory)
{
LOG("Couldn't create devenum factory");
return devices;
}
wil::com_ptr_nothrow<IEnumMoniker> enumMoniker;
enumeratorFactory->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &enumMoniker, CDEF_DEVMON_PNP_DEVICE);
if (!enumMoniker)
{
LOG("Couldn't create class enumerator");
return devices;
}
ULONG _ = 0;
wil::com_ptr_nothrow<IMoniker> moniker;
while (enumMoniker->Next(1, &moniker, &_) == S_OK)
{
LOG("Inspecting moniker");
VideoCaptureDeviceInfo deviceInfo;
wil::com_ptr_nothrow<IPropertyBag> propertyData;
moniker->BindToStorage(nullptr, nullptr, IID_IPropertyBag, reinterpret_cast<void**>(&propertyData));
if (!propertyData)
{
LOG("BindToStorage failed");
continue;
}
wil::unique_variant propVal;
propVal.vt = VT_BSTR;
if (FAILED(propertyData->Read(L"FriendlyName", &propVal, nullptr)))
{
LOG("Couldn't obtain FriendlyName property");
continue;
}
deviceInfo.friendlyName = { propVal.bstrVal, SysStringLen(propVal.bstrVal) };
LOG(deviceInfo.friendlyName);
propVal.reset();
propVal.vt = VT_BSTR;
if (FAILED(propertyData->Read(L"DevicePath", &propVal, nullptr)))
{
LOG("Couldn't obtain DevicePath property");
continue;
}
deviceInfo.devicePath = { propVal.bstrVal, SysStringLen(propVal.bstrVal) };
wil::com_ptr_nothrow<IBaseFilter> filter;
moniker->BindToObject(nullptr, nullptr, IID_IBaseFilter, reinterpret_cast<void**>(&filter));
if (!filter)
{
LOG("Couldn't BindToObject");
continue;
}
wil::com_ptr_nothrow<IEnumPins> pinsEnum;
if (FAILED(filter->EnumPins(&pinsEnum)))
{
LOG("BindToObject EnumPins");
continue;
}
wil::com_ptr_nothrow<IPin> pin;
while (pinsEnum->Next(1, &pin, &_) == S_OK)
{
LOG("Inspecting pin");
// Skip pins which do not belong to capture category
GUID category{};
DWORD __;
if (auto props = pin.try_copy<IKsPropertySet>();
!props ||
FAILED(props->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, nullptr, 0, &category, sizeof(GUID), &__)) ||
category != PIN_CATEGORY_CAPTURE)
{
continue;
}
// Skip non-output pins
if (PIN_DIRECTION direction = {}; FAILED(pin->QueryDirection(&direction)) || direction != PINDIR_OUTPUT)
{
continue;
}
LOG("Found a pin of suitable category and direction, selecting format");
auto bestFormat = SelectBestMediaType(pin);
if (!bestFormat)
{
continue;
}
deviceInfo.captureOutputPin = std::move(pin);
deviceInfo.bestFormat = std::move(bestFormat.value());
deviceInfo.captureOutputFilter = std::move(filter);
devices.emplace_back(std::move(deviceInfo));
}
}
return devices;
}
std::optional<VideoCaptureDevice> VideoCaptureDevice::Create(VideoCaptureDeviceInfo&& vdi, callback_t callback)
{
VERBOSE_LOG;
VideoCaptureDevice result;
result._graph = wil::CoCreateInstanceNoThrow<IGraphBuilder>(CLSID_FilterGraph);
result._builder = wil::CoCreateInstanceNoThrow<ICaptureGraphBuilder2>(CLSID_CaptureGraphBuilder2);
if (!result._graph || !result._builder)
{
return std::nullopt;
}
if (FAILED(result._builder->SetFiltergraph(result._graph.get())))
{
return std::nullopt;
}
result._control = result._graph.try_query<IMediaControl>();
if (!result._control)
{
return std::nullopt;
}
auto pinConfig = vdi.captureOutputPin.try_query<IAMStreamConfig>();
if (!pinConfig)
{
return std::nullopt;
}
if (FAILED(pinConfig->SetFormat(vdi.bestFormat.mediaType.get())))
{
return std::nullopt;
}
auto captureInputFilter = winrt::make_self<VideoCaptureReceiverFilter>();
auto receiverPin = winrt::make_self<VideoCaptureReceiverPin>(std::move(vdi.bestFormat.mediaType), captureInputFilter.get());
receiverPin->_frameCallback = std::move(callback);
captureInputFilter->_videoReceiverPin.attach(receiverPin.get());
auto detachReceiverPin = wil::scope_exit([&receiverPin]() { receiverPin.detach(); });
if (FAILED(result._graph->AddFilter(captureInputFilter.get(), nullptr)))
{
return std::nullopt;
}
if (FAILED(result._graph->AddFilter(vdi.captureOutputFilter.get(), nullptr)))
{
return std::nullopt;
}
if (FAILED(result._graph->ConnectDirect(vdi.captureOutputPin.get(), captureInputFilter->_videoReceiverPin.get(), nullptr)))
{
return std::nullopt;
}
result._allocator = receiverPin->_allocator;
return std::make_optional(std::move(result));
}
bool VideoCaptureDevice::StartCapture()
{
VERBOSE_LOG;
return SUCCEEDED(_control->Run());
}
bool VideoCaptureDevice::StopCapture()
{
VERBOSE_LOG;
return SUCCEEDED(_control->Stop());
}
VideoCaptureDevice::~VideoCaptureDevice()
{
StopCapture();
}

View file

@ -0,0 +1,62 @@
#pragma once
#include <initguid.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <dshow.h>
#include <wil/com.h>
#include <functional>
#include <vector>
#include <string>
#include <optional>
#include "DirectShowUtils.h"
struct VideoStreamFormat
{
long width = 0;
long height = 0;
REFERENCE_TIME avgFrameTime = std::numeric_limits<REFERENCE_TIME>::max();
unique_media_type_ptr mediaType;
VideoStreamFormat() = default;
VideoStreamFormat(const VideoStreamFormat&) = delete;
VideoStreamFormat& operator=(const VideoStreamFormat&) = delete;
VideoStreamFormat(VideoStreamFormat&&) = default;
VideoStreamFormat& operator=(VideoStreamFormat&&) = default;
};
struct VideoCaptureDeviceInfo
{
std::wstring friendlyName;
std::wstring devicePath;
wil::com_ptr_nothrow<IPin> captureOutputPin;
wil::com_ptr_nothrow<IBaseFilter> captureOutputFilter;
VideoStreamFormat bestFormat;
};
class VideoCaptureDevice final
{
public:
wil::com_ptr_nothrow<IMemAllocator> _allocator;
using callback_t = std::function<void(IMediaSample*)>;
static std::vector<VideoCaptureDeviceInfo> ListAll();
static std::optional<VideoCaptureDevice> Create(VideoCaptureDeviceInfo&& vdi, callback_t callback);
bool StartCapture();
bool StopCapture();
~VideoCaptureDevice();
private:
wil::com_ptr_nothrow<IGraphBuilder> _graph;
wil::com_ptr_nothrow<ICaptureGraphBuilder2> _builder;
wil::com_ptr_nothrow<IMediaControl> _control;
callback_t _callback;
};

View file

@ -0,0 +1,919 @@
#include "VideoCaptureProxyFilter.h"
#include "VideoCaptureDevice.h"
#include <mfidl.h>
#include <Shlwapi.h>
#include <mfapi.h>
#include <fstream>
constexpr static inline wchar_t FILTER_NAME[] = L"PowerToysVCMProxyFilter";
constexpr static inline wchar_t PIN_NAME[] = L"PowerToysVCMProxyPIN";
constexpr static inline wchar_t VENDOR[] = L"Microsoft Corporation";
namespace
{
constexpr float initialJpgQuality = 0.5f;
constexpr std::array<unsigned char, 3> overlayColor = { 0, 0, 0 };
// clang-format off
unsigned char bmpPixelData[58] = {
0x42, 0x4D, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00,
0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, overlayColor[0], overlayColor[1], overlayColor[2], 0x00
};
// clang-format on
}
wil::com_ptr_nothrow<IMemAllocator> VideoCaptureProxyPin::FindAllocator()
{
auto allocator = GetPinAllocator(_connectedInputPin);
if (!allocator && _owningFilter->_captureDevice)
{
allocator = _owningFilter->_captureDevice->_allocator;
}
return allocator;
}
wil::com_ptr_nothrow<IMFSample> LoadImageAsSample(wil::com_ptr_nothrow<IStream> imageStream,
IMFMediaType* sampleMediaType,
const float quality) noexcept;
bool ReencodeJPGImage(BYTE* imageBuf, const DWORD imageSize, DWORD& reencodedSize);
HRESULT VideoCaptureProxyPin::Connect(IPin* pReceivePin, const AM_MEDIA_TYPE*)
{
if (!pReceivePin)
{
LOG("VideoCaptureProxyPin::Connect FAILED pReceivePin");
return E_POINTER;
}
if (_owningFilter->_state == State_Running)
{
LOG("VideoCaptureProxyPin::Connect FAILED _owningFilter->_state");
return VFW_E_NOT_STOPPED;
}
if (_connectedInputPin)
{
LOG("VideoCaptureProxyPin::Connect FAILED _connectedInputPin");
return VFW_E_ALREADY_CONNECTED;
}
if (FAILED(pReceivePin->ReceiveConnection(this, _mediaFormat.get())))
{
LOG("VideoCaptureProxyPin::Connect FAILED pReceivePin->ReceiveConnection");
return E_POINTER;
}
_connectedInputPin = pReceivePin;
auto memInput = _connectedInputPin.try_query<IMemInputPin>();
if (!memInput)
{
LOG("VideoCaptureProxyPin::Connect FAILED _connectedInputPin.try_query");
return VFW_E_NO_TRANSPORT;
}
auto allocator = FindAllocator();
if (allocator == nullptr)
{
LOG("VideoCaptureProxyPin::Connect FAILED FindAllocator");
return VFW_E_NO_TRANSPORT;
}
if (FAILED(memInput->NotifyAllocator(allocator.get(), false)))
{
LOG("VideoCaptureProxyPin::Connect FAILED memInput->NotifyAllocator");
return VFW_E_NO_TRANSPORT;
}
return S_OK;
}
HRESULT VideoCaptureProxyPin::ReceiveConnection(IPin*, const AM_MEDIA_TYPE*)
{
return S_OK;
}
HRESULT VideoCaptureProxyPin::Disconnect(void)
{
if (!_connectedInputPin)
{
LOG("VideoCaptureProxyPin::Disconnect FAILED _connectedInputPin");
return S_FALSE;
}
_connectedInputPin.reset();
return S_OK;
}
HRESULT VideoCaptureProxyPin::ConnectedTo(IPin** pPin)
{
if (!_connectedInputPin)
{
*pPin = nullptr;
return VFW_E_NOT_CONNECTED;
}
_connectedInputPin.try_copy_to(pPin);
return S_OK;
}
HRESULT VideoCaptureProxyPin::ConnectionMediaType(AM_MEDIA_TYPE* pmt)
{
if (!_connectedInputPin)
{
LOG("VideoCaptureProxyPin::ConnectionMediaType FAILED _connectedInputPin");
return VFW_E_NOT_CONNECTED;
}
*pmt = *CopyMediaType(_mediaFormat).release();
return S_OK;
}
HRESULT VideoCaptureProxyPin::QueryPinInfo(PIN_INFO* pInfo)
{
if (!pInfo)
{
LOG("VideoCaptureProxyPin::QueryPinInfo FAILED pInfo");
return E_POINTER;
}
pInfo->pFilter = _owningFilter;
if (_owningFilter)
{
_owningFilter->AddRef();
}
if (_mediaFormat->majortype == MEDIATYPE_Video)
{
std::copy(std::begin(PIN_NAME), std::end(PIN_NAME), pInfo->achName);
}
pInfo->dir = PINDIR_OUTPUT;
return S_OK;
}
HRESULT VideoCaptureProxyPin::QueryDirection(PIN_DIRECTION* pPinDir)
{
if (!pPinDir)
{
LOG("VideoCaptureProxyPin::QueryDirection FAILED pPinDir");
return E_POINTER;
}
*pPinDir = PINDIR_OUTPUT;
return S_OK;
}
HRESULT VideoCaptureProxyPin::QueryId(LPWSTR* Id)
{
if (!Id)
{
LOG("VideoCaptureProxyPin::QueryId FAILED Id");
return E_POINTER;
}
*Id = static_cast<LPWSTR>(CoTaskMemAlloc(sizeof(PIN_NAME)));
std::copy(std::begin(PIN_NAME), std::end(PIN_NAME), *Id);
return S_OK;
}
HRESULT VideoCaptureProxyPin::QueryAccept(const AM_MEDIA_TYPE*)
{
return S_OK;
}
HRESULT VideoCaptureProxyPin::EnumMediaTypes(IEnumMediaTypes** ppEnum)
{
if (!ppEnum)
{
LOG("VideoCaptureProxyPin::EnumMediaTypes FAILED ppEnum");
return E_POINTER;
}
auto enumerator = winrt::make_self<MediaTypeEnumerator>();
enumerator->_objects.emplace_back(CopyMediaType(_mediaFormat));
*ppEnum = enumerator.detach();
return S_OK;
}
HRESULT VideoCaptureProxyPin::QueryInternalConnections(IPin**, ULONG*)
{
return E_NOTIMPL;
}
HRESULT VideoCaptureProxyPin::EndOfStream(void)
{
return S_OK;
}
HRESULT VideoCaptureProxyPin::BeginFlush(void)
{
_flushing = true;
return S_OK;
}
HRESULT VideoCaptureProxyPin::EndFlush(void)
{
_flushing = false;
return S_OK;
}
HRESULT VideoCaptureProxyPin::NewSegment(REFERENCE_TIME, REFERENCE_TIME, double)
{
return S_OK;
}
HRESULT VideoCaptureProxyPin::SetFormat(AM_MEDIA_TYPE* pmt)
{
if (pmt == nullptr)
{
return S_OK;
}
_mediaFormat = CopyMediaType(pmt);
return S_OK;
}
HRESULT VideoCaptureProxyPin::GetFormat(AM_MEDIA_TYPE** ppmt)
{
if (!ppmt)
{
LOG("VideoCaptureProxyPin::GetFormat FAILED ppmt");
return E_POINTER;
}
*ppmt = CopyMediaType(_mediaFormat).release();
return S_OK;
}
HRESULT VideoCaptureProxyPin::GetNumberOfCapabilities(int* piCount, int* piSize)
{
if (!piCount || !piSize)
{
LOG("VideoCaptureProxyPin::GetNumberOfCapabilities FAILED piCount || piSize");
return E_POINTER;
}
*piCount = 1;
*piSize = sizeof(VIDEO_STREAM_CONFIG_CAPS);
return S_OK;
}
HRESULT VideoCaptureProxyPin::GetStreamCaps(int iIndex, AM_MEDIA_TYPE** ppmt, BYTE* pSCC)
{
if (!ppmt || !pSCC)
{
LOG("VideoCaptureProxyPin::GetStreamCaps FAILED ppmt || pSCC");
return E_POINTER;
}
if (iIndex != 0)
{
LOG("VideoCaptureProxyPin::GetStreamCaps FAILED iIndex");
return S_FALSE;
}
VIDEOINFOHEADER* vih = reinterpret_cast<decltype(vih)>(_mediaFormat->pbFormat);
VIDEO_STREAM_CONFIG_CAPS caps{};
caps.guid = FORMAT_VideoInfo;
caps.MinFrameInterval = vih->AvgTimePerFrame;
caps.MaxFrameInterval = vih->AvgTimePerFrame;
caps.MinOutputSize.cx = vih->bmiHeader.biWidth;
caps.MinOutputSize.cy = vih->bmiHeader.biHeight;
caps.MaxOutputSize = caps.MinOutputSize;
caps.InputSize = caps.MinOutputSize;
caps.MinCroppingSize = caps.MinOutputSize;
caps.MaxCroppingSize = caps.MinOutputSize;
caps.CropGranularityX = vih->bmiHeader.biWidth;
caps.CropGranularityY = vih->bmiHeader.biHeight;
caps.MinBitsPerSecond = vih->dwBitRate;
caps.MaxBitsPerSecond = caps.MinBitsPerSecond;
*ppmt = CopyMediaType(_mediaFormat).release();
const auto caps_begin = reinterpret_cast<const char*>(&caps);
std::copy(caps_begin, caps_begin + sizeof(caps), pSCC);
return S_OK;
}
HRESULT VideoCaptureProxyPin::Set(REFGUID, DWORD, LPVOID, DWORD, LPVOID, DWORD)
{
return E_NOTIMPL;
}
HRESULT VideoCaptureProxyPin::Get(
REFGUID guidPropSet,
DWORD dwPropID,
LPVOID,
DWORD,
LPVOID pPropData,
DWORD cbPropData,
DWORD* pcbReturned)
{
if (guidPropSet != AMPROPSETID_Pin)
{
LOG("VideoCaptureProxyPin::Get FAILED guidPropSet");
return E_PROP_SET_UNSUPPORTED;
}
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
{
LOG("VideoCaptureProxyPin::Get FAILED dwPropID");
return E_PROP_ID_UNSUPPORTED;
}
if (!pPropData)
{
LOG("VideoCaptureProxyPin::Get FAILED pPropData || pcbReturned");
return E_POINTER;
}
if (pcbReturned)
{
*pcbReturned = sizeof(GUID);
}
if (cbPropData < sizeof(GUID))
{
LOG("VideoCaptureProxyPin::Get FAILED cbPropData");
return E_UNEXPECTED;
}
*(GUID*)pPropData = PIN_CATEGORY_CAPTURE;
LOG("VideoCaptureProxyPin::Get SUCCESS");
return S_OK;
}
HRESULT VideoCaptureProxyPin::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD* pTypeSupport)
{
if (guidPropSet != AMPROPSETID_Pin)
{
LOG("VideoCaptureProxyPin::QuerySupported FAILED guidPropSet");
return E_PROP_SET_UNSUPPORTED;
}
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
{
LOG("VideoCaptureProxyPin::QuerySupported FAILED dwPropID");
return E_PROP_ID_UNSUPPORTED;
}
if (pTypeSupport)
{
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
}
return S_OK;
}
long GetImageSize(wil::com_ptr_nothrow<IMFSample>& image)
{
if (!image)
{
return 0;
}
DWORD imageSize = 0;
wil::com_ptr_nothrow<IMFMediaBuffer> imageBuf;
OK_OR_BAIL(image->GetBufferByIndex(0, &imageBuf));
OK_OR_BAIL(imageBuf->GetCurrentLength(&imageSize));
return imageSize;
}
void ReencodeFrame(IMediaSample* frame)
{
BYTE* frameData = nullptr;
frame->GetPointer(&frameData);
if (!frameData)
{
LOG("VideoCaptureProxyPin::ReencodeFrame FAILED frameData");
return;
}
const DWORD frameSize = frame->GetSize();
DWORD reencodedSize = 0;
if (!ReencodeJPGImage(frameData, frameSize, reencodedSize))
{
LOG("VideoCaptureProxyPin::ReencodeJPGImage FAILED");
return;
}
frame->SetActualDataLength(reencodedSize);
}
bool OverwriteFrame(IMediaSample* frame, wil::com_ptr_nothrow<IMFSample>& image)
{
if (!image)
{
return false;
}
BYTE* frameData = nullptr;
frame->GetPointer(&frameData);
if (!frameData)
{
LOG("VideoCaptureProxyPin::OverwriteFrame FAILED frameData");
return false;
}
wil::com_ptr_nothrow<IMFMediaBuffer> imageBuf;
const DWORD frameSize = frame->GetSize();
image->GetBufferByIndex(0, &imageBuf);
if (!imageBuf)
{
LOG("VideoCaptureProxyPin::OverwriteFrame FAILED imageBuf");
return false;
}
BYTE* imageData = nullptr;
DWORD _ = 0, imageSize = 0;
imageBuf->Lock(&imageData, &_, &imageSize);
if (!imageData)
{
LOG("VideoCaptureProxyPin::OverwriteFrame FAILED imageData");
return false;
}
if (imageSize > frameSize && failed(frame->SetActualDataLength(imageSize)))
{
char buf[512]{};
sprintf_s(buf, "VideoCaptureProxyPin::OverwriteFrame FAILED overlay image size %lu is larger than frame size %lu", imageSize, frameSize);
LOG(buf);
imageBuf->Unlock();
return false;
}
std::copy(imageData, imageData + imageSize, frameData);
imageBuf->Unlock();
frame->SetActualDataLength(imageSize);
return true;
}
//#define DEBUG_FRAME_DATA
//#define DEBUG_OVERWRITE_FRAME
//#define DEBUG_REENCODE_JPG_DATA
#if defined(DEBUG_OVERWRITE_FRAME)
void DebugOverwriteFrame(IMediaSample* frame, std::string_view filepath)
{
std::ifstream file{ filepath.data(), std::ios::binary };
std::streampos fileSize = 0;
fileSize = file.tellg();
file.seekg(0, std::ios::end);
fileSize = file.tellg() - fileSize;
BYTE* frameData = nullptr;
if (!frame)
{
LOG("null frame provided");
return;
}
frame->GetPointer(&frameData);
const DWORD frameSize = frame->GetSize();
if (fileSize > frameSize || !frameData)
{
LOG("frame can't be filled with data");
return;
}
file.read((char*)frameData, fileSize);
frame->SetActualDataLength((long)fileSize);
LOG("DebugOverwriteFrame success");
}
#endif
#if defined(DEBUG_FRAME_DATA)
#include <filesystem>
namespace fs = std::filesystem;
void DumpSample(IMediaSample* frame, const std::string_view filename)
{
BYTE* data = nullptr;
frame->GetPointer(&data);
if (!data)
{
LOG("Couldn't get sample pointer");
return;
}
const long nBytes = frame->GetActualDataLength();
std::ofstream file{ fs::temp_directory_path() / filename, std::ios::binary };
file.write((const char*)data, nBytes);
}
#endif
VideoCaptureProxyFilter::VideoCaptureProxyFilter() :
_worker_thread{
std::thread{
[this]() {
using namespace std::chrono_literals;
const auto uninitializedSleepInterval = 15ms;
std::vector<float> lowerJpgQualityModes = { 0.1f, 0.25f };
while (!_shutdown_request)
{
std::unique_lock<std::mutex> lock{ _worker_mutex };
_worker_cv.wait(lock, [this] { return _pending_frame != nullptr || _shutdown_request; });
if (!_outPin || !_outPin->_connectedInputPin)
{
lock.unlock();
std::this_thread::sleep_for(uninitializedSleepInterval);
continue;
}
auto input = _outPin->_connectedInputPin.try_query<IMemInputPin>();
if (!input)
{
continue;
}
IMediaSample* sample = _pending_frame;
if (!sample)
{
continue;
}
#if defined(DEBUG_FRAME_DATA)
static bool realFrameSaved = false;
if (!realFrameSaved)
{
DumpSample(sample, "PowerToysVCMRealFrame.binary");
realFrameSaved = true;
}
#endif
auto newSettings = SyncCurrentSettings();
if (newSettings.webcamDisabled)
{
#if !defined(DEBUG_OVERWRITE_FRAME)
bool overwritten = OverwriteFrame(_pending_frame, _overlayImage ? _overlayImage : _blankImage);
while (!overwritten && _overlayImage)
{
_overlayImage.reset();
newSettings = SyncCurrentSettings();
if (!lowerJpgQualityModes.empty() && newSettings.overlayImage)
{
const float quality = lowerJpgQualityModes.back();
lowerJpgQualityModes.pop_back();
char buf[512]{};
sprintf_s(buf, "Reload overlay image with quality %f", quality);
LOG(buf);
_overlayImage = LoadImageAsSample(newSettings.overlayImage, _targetMediaType.get(), quality);
overwritten = OverwriteFrame(_pending_frame, _overlayImage);
}
else
{
LOG("Couldn't overwrite frame with image with all available quality modes.");
}
}
#if defined(DEBUG_FRAME_DATA)
static bool overlayFrameSaved = false;
if (!overlayFrameSaved && _overlayImage && overwritten)
{
DumpSample(sample, "PowerToysVCMOverlayImageFrame.binary");
overlayFrameSaved = true;
}
#endif
if (!overwritten && !_overlayImage)
{
OverwriteFrame(_pending_frame, _blankImage);
}
#else
DebugOverwriteFrame(_pending_frame, "R:\\frame.data");
#endif
}
#if defined(DEBUG_REENCODE_JPG_DATA)
else
{
GUID subtype{};
_targetMediaType->GetGUID(MF_MT_SUBTYPE, &subtype);
if (subtype == MFVideoFormat_MJPG)
{
ReencodeFrame(_pending_frame);
}
}
#endif
_pending_frame = nullptr;
input->Receive(sample);
sample->Release();
}
} }
}
{
}
HRESULT VideoCaptureProxyFilter::Stop(void)
{
if (_state != State_Stopped && _captureDevice)
{
_captureDevice->StopCapture();
}
_state = State_Stopped;
return S_OK;
}
HRESULT VideoCaptureProxyFilter::Pause(void)
{
if (_state == State_Stopped)
{
std::unique_lock<std::mutex> lock{ _worker_mutex };
if (!_outPin)
{
LOG("VideoCaptureProxyPin::Pause FAILED _outPin");
return VFW_E_NO_TRANSPORT;
}
auto allocator = _outPin->FindAllocator();
if (!allocator)
{
LOG("VideoCaptureProxyPin::Pause FAILED allocator");
return VFW_E_NO_TRANSPORT;
}
allocator->Commit();
}
_state = State_Paused;
return S_OK;
}
HRESULT VideoCaptureProxyFilter::Run(REFERENCE_TIME)
{
_state = State_Running;
if (_captureDevice)
{
_captureDevice->StartCapture();
}
return S_OK;
}
HRESULT VideoCaptureProxyFilter::GetState(DWORD, FILTER_STATE* State)
{
*State = _state;
return S_OK;
}
HRESULT VideoCaptureProxyFilter::SetSyncSource(IReferenceClock* pClock)
{
_clock = pClock;
return S_OK;
}
HRESULT VideoCaptureProxyFilter::GetSyncSource(IReferenceClock** pClock)
{
if (!pClock)
{
return E_POINTER;
}
_clock.try_copy_to(pClock);
return S_OK;
}
GUID MapDShowSubtypeToMFT(const GUID& dshowSubtype)
{
if (dshowSubtype == MEDIASUBTYPE_YUY2)
{
return MFVideoFormat_YUY2;
}
else if (dshowSubtype == MEDIASUBTYPE_MJPG)
{
return MFVideoFormat_MJPG;
}
else if (dshowSubtype == MEDIASUBTYPE_RGB24)
{
return MFVideoFormat_RGB24;
}
else
{
LOG("MapDShowSubtypeToMFT: Unsupported media type format provided!");
return MFVideoFormat_MJPG;
}
}
HRESULT VideoCaptureProxyFilter::EnumPins(IEnumPins** ppEnum)
{
if (!ppEnum)
{
LOG("VideoCaptureProxyFilter::EnumPins null arg provided");
return E_POINTER;
}
std::unique_lock<std::mutex> lock{ _worker_mutex };
// We cannot initialize capture device and outpin during VideoCaptureProxyFilter ctor
// since that results in a deadlock -> initializing now.
if (!_outPin)
{
LOG("VideoCaptureProxyFilter::EnumPins started pin initialization");
const auto newSettings = SyncCurrentSettings();
std::vector<VideoCaptureDeviceInfo> webcams;
webcams = VideoCaptureDevice::ListAll();
if (webcams.empty())
{
LOG("VideoCaptureProxyFilter::EnumPins no physical webcams found");
return E_FAIL;
}
std::optional<size_t> selectedCamIdx;
for (size_t i = 0; i < size(webcams); ++i)
{
if (newSettings.newCameraName == webcams[i].friendlyName)
{
selectedCamIdx = i;
LOG("VideoCaptureProxyFilter::EnumPins webcam selected using settings");
break;
}
}
if (!selectedCamIdx)
{
for (size_t i = 0; i < size(webcams); ++i)
{
if (newSettings.newCameraName != CAMERA_NAME)
{
LOG("VideoCaptureProxyFilter::EnumPins webcam selected using first fit");
selectedCamIdx = i;
break;
}
}
}
if (!selectedCamIdx)
{
LOG("VideoCaptureProxyFilter::EnumPins FAILED webcam couldn't be selected");
return E_FAIL;
}
auto& webcam = webcams[*selectedCamIdx];
auto pin = winrt::make_self<VideoCaptureProxyPin>();
pin->_mediaFormat = CopyMediaType(webcam.bestFormat.mediaType);
pin->_owningFilter = this;
_outPin.attach(pin.detach());
auto frameCallback = [this](IMediaSample* sample) {
std::unique_lock<std::mutex> lock{ _worker_mutex };
sample->AddRef();
_pending_frame = sample;
_worker_cv.notify_one();
};
_targetMediaType.reset();
MFCreateMediaType(&_targetMediaType);
_targetMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
_targetMediaType->SetGUID(MF_MT_SUBTYPE, MapDShowSubtypeToMFT(webcam.bestFormat.mediaType->subtype));
_targetMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
_targetMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
MFSetAttributeSize(
_targetMediaType.get(), MF_MT_FRAME_SIZE, webcam.bestFormat.width, webcam.bestFormat.height);
MFSetAttributeRatio(_targetMediaType.get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
_captureDevice = VideoCaptureDevice::Create(std::move(webcam), std::move(frameCallback));
if (_captureDevice)
{
if (!_blankImage)
{
wil::com_ptr_nothrow<IStream> blackBMPImage = SHCreateMemStream(bmpPixelData, sizeof(bmpPixelData));
_blankImage = LoadImageAsSample(blackBMPImage, _targetMediaType.get(), initialJpgQuality);
}
_overlayImage = LoadImageAsSample(newSettings.overlayImage, _targetMediaType.get(), initialJpgQuality);
LOG("VideoCaptureProxyFilter::EnumPins capture device created successfully");
}
else
{
LOG("VideoCaptureProxyFilter::EnumPins FAILED couldn't create capture device");
}
}
auto enumerator = winrt::make_self<ObjectEnumerator<IPin, IEnumPins>>();
enumerator->_objects.emplace_back(_outPin);
*ppEnum = enumerator.detach();
return S_OK;
}
HRESULT VideoCaptureProxyFilter::FindPin(LPCWSTR, IPin**)
{
return E_NOTIMPL;
}
HRESULT VideoCaptureProxyFilter::QueryFilterInfo(FILTER_INFO* pInfo)
{
if (!pInfo)
{
LOG("VideoCaptureProxyPin::QueryFilterInfo FAILED pInfo");
return E_POINTER;
}
VERBOSE_LOG;
std::copy(std::begin(FILTER_NAME), std::end(FILTER_NAME), pInfo->achName);
pInfo->pGraph = _graph;
if (_graph)
{
_graph->AddRef();
}
return S_OK;
}
HRESULT VideoCaptureProxyFilter::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR)
{
_graph = pGraph;
return S_OK;
}
HRESULT VideoCaptureProxyFilter::QueryVendorInfo(LPWSTR* pVendorInfo)
{
auto info = static_cast<LPWSTR>(CoTaskMemAlloc(sizeof(VENDOR)));
std::copy(std::begin(VENDOR), std::end(VENDOR), info);
*pVendorInfo = info;
return S_OK;
}
HRESULT VideoCaptureProxyFilter::GetClassID(CLSID*)
{
return E_NOTIMPL;
}
ULONG VideoCaptureProxyFilter::GetMiscFlags(void)
{
return AM_FILTER_MISC_FLAGS_IS_SOURCE;
}
VideoCaptureProxyFilter::~VideoCaptureProxyFilter()
{
VERBOSE_LOG;
_shutdown_request = true;
_worker_cv.notify_one();
_worker_thread.join();
}
VideoCaptureProxyFilter::SyncedSettings VideoCaptureProxyFilter::SyncCurrentSettings()
{
SyncedSettings result;
if (!_settingsUpdateChannel.has_value())
{
_settingsUpdateChannel = SerializedSharedMemory::open(CameraSettingsUpdateChannel::endpoint(), sizeof(CameraSettingsUpdateChannel), false);
}
if (!_settingsUpdateChannel)
{
return result;
}
_settingsUpdateChannel->access([this, &result](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
bool cameraNameUpdated = false;
result.webcamDisabled = settings->useOverlayImage;
settings->cameraInUse = true;
if (settings->sourceCameraName.has_value())
{
std::wstring_view newCameraNameView{ settings->sourceCameraName->data() };
if (!_currentSourceCameraName.has_value() || *_currentSourceCameraName != newCameraNameView)
{
cameraNameUpdated = true;
result.newCameraName = newCameraNameView;
}
}
if (!settings->overlayImageSize.has_value())
{
return;
}
if (settings->newOverlayImagePosted || !_overlayImage)
{
auto imageChannel =
SerializedSharedMemory::open(CameraOverlayImageChannel::endpoint(), *settings->overlayImageSize, true);
if (!imageChannel)
{
return;
}
imageChannel->access([this, settings, &result](auto imageMemory) {
result.overlayImage = SHCreateMemStream(imageMemory._data, static_cast<UINT>(imageMemory._size));
if (!result.overlayImage)
{
return;
}
settings->newOverlayImagePosted = false;
});
}
});
return result;
}

View file

@ -0,0 +1,120 @@
#pragma once
#include <initguid.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <dshow.h>
#include <winrt/base.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <CameraStateUpdateChannels.h>
#include <SerializedSharedMemory.h>
#include "VideoCaptureDevice.h"
#include <mutex>
#include <condition_variable>
struct VideoCaptureProxyPin;
struct IMFSample;
struct IMFMediaType;
inline const wchar_t CAMERA_NAME[] = L"PowerToys VideoConference Mute";
struct VideoCaptureProxyFilter : winrt::implements<VideoCaptureProxyFilter, IBaseFilter, IAMFilterMiscFlags>
{
// BLOCK START: member accessed concurrently
wil::com_ptr_nothrow<VideoCaptureProxyPin> _outPin;
IMediaSample* _pending_frame = nullptr;
std::atomic_bool _shutdown_request = false;
std::optional<SerializedSharedMemory> _settingsUpdateChannel;
std::optional<std::wstring> _currentSourceCameraName;
wil::com_ptr_nothrow<IMFSample> _blankImage;
wil::com_ptr_nothrow<IMFSample> _overlayImage;
wil::com_ptr_nothrow<IMFMediaType> _targetMediaType;
// BLOCK END: member accessed concurrently
std::mutex _worker_mutex;
std::condition_variable _worker_cv;
FILTER_STATE _state = State_Stopped;
wil::com_ptr_nothrow<IReferenceClock> _clock;
IFilterGraph* _graph = nullptr;
std::optional<VideoCaptureDevice> _captureDevice;
std::thread _worker_thread;
VideoCaptureProxyFilter();
~VideoCaptureProxyFilter();
struct SyncedSettings
{
bool webcamDisabled = false;
std::wstring newCameraName;
wil::com_ptr_nothrow<IStream> overlayImage;
};
SyncedSettings SyncCurrentSettings();
HRESULT STDMETHODCALLTYPE Stop(void) override;
HRESULT STDMETHODCALLTYPE Pause(void) override;
HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart) override;
HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE* State) override;
HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock* pClock) override;
HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock** pClock) override;
HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins** ppEnum) override;
HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin** ppPin) override;
HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO* pInfo) override;
HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) override;
HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR* pVendorInfo) override;
HRESULT STDMETHODCALLTYPE GetClassID(CLSID* pClassID) override;
ULONG STDMETHODCALLTYPE GetMiscFlags(void) override;
};
struct VideoCaptureProxyPin : winrt::implements<VideoCaptureProxyPin, IPin, IAMStreamConfig, IKsPropertySet>
{
VideoCaptureProxyFilter* _owningFilter = nullptr;
wil::com_ptr_nothrow<IPin> _connectedInputPin;
unique_media_type_ptr _mediaFormat;
std::atomic_bool _flushing = false;
HRESULT STDMETHODCALLTYPE Connect(IPin* pReceivePin, const AM_MEDIA_TYPE* pmt) override;
HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin* pConnector, const AM_MEDIA_TYPE* pmt) override;
HRESULT STDMETHODCALLTYPE Disconnect(void) override;
HRESULT STDMETHODCALLTYPE ConnectedTo(IPin** pPin) override;
HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE* pmt) override;
HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO* pInfo) override;
HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION* pPinDir) override;
HRESULT STDMETHODCALLTYPE QueryId(LPWSTR* Id) override;
HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE* pmt) override;
HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes** ppEnum) override;
HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin** apPin, ULONG* nPin) override;
HRESULT STDMETHODCALLTYPE EndOfStream(void) override;
HRESULT STDMETHODCALLTYPE BeginFlush(void) override;
HRESULT STDMETHODCALLTYPE EndFlush(void) override;
HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) override;
HRESULT STDMETHODCALLTYPE SetFormat(AM_MEDIA_TYPE* pmt) override;
HRESULT STDMETHODCALLTYPE GetFormat(AM_MEDIA_TYPE** ppmt) override;
HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities(int* piCount, int* piSize) override;
HRESULT STDMETHODCALLTYPE GetStreamCaps(int iIndex, AM_MEDIA_TYPE** ppmt, BYTE* pSCC) override;
HRESULT STDMETHODCALLTYPE Set(REFGUID guidPropSet,
DWORD dwPropID,
LPVOID pInstanceData,
DWORD cbInstanceData,
LPVOID pPropData,
DWORD cbPropData) override;
HRESULT STDMETHODCALLTYPE Get(REFGUID guidPropSet,
DWORD dwPropID,
LPVOID pInstanceData,
DWORD cbInstanceData,
LPVOID pPropData,
DWORD cbPropData,
DWORD* pcbReturned) override;
HRESULT STDMETHODCALLTYPE QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD* pTypeSupport) override;
wil::com_ptr_nothrow<IMemAllocator> FindAllocator();
};

View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{AC2857B4-103D-4D6D-9740-926EBF785042}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>VideoConferenceProxyFilter</RootNamespace>
<ProjectName>VideoConferenceProxyFilter</ProjectName>
<OverrideWindowsTargetPlatformVersion>true</OverrideWindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;$(ProjectDir)..\..\..\</AdditionalIncludeDirectories>
<WarningLevel>Level4</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
<PreBuildEvent Condition="'$(Platform)'=='x64'">
<Command>call "$(ProjectDir)build_vcm_x86.cmd"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<PropertyGroup Condition="'$(Platform)'!='Win32'">
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<TargetName>VideoConferenceProxyFilter_x64</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='Win32'">
<OutDir>..\..\..\..\x86\$(Configuration)\modules\VideoConference\</OutDir>
<TargetName>VideoConferenceProxyFilter_x86</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_LIB;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MultiThreaded</RuntimeLibrary>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<AdditionalDependencies>$(OutDir)VideoConferenceShared.lib;mfplat.lib;Mfsensorgroup.lib;OneCoreUAP.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies);</AdditionalDependencies>
<ModuleDefinitionFile>module.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="DirectShowUtils.h" />
<ClInclude Include="VideoCaptureDevice.h" />
<ClInclude Include="VideoCaptureProxyFilter.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DirectShowUtils.cpp" />
<ClCompile Include="VideoCaptureDevice.cpp" />
<ClCompile Include="VideoCaptureProxyFilter.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="ImageLoading.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="module.def" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VideoConferenceShared\VideoConferenceShared.vcxproj">
<Project>{459e0768-7ebd-4c41-bba1-6db3b3815e0a}</Project>
</ProjectReference>
</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')" />
</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,41 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceProxyFilter", "VideoConferenceProxyFilter.vcxproj", "{AC2857B4-103D-4D6D-9740-926EBF785042}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceShared", "..\VideoConferenceShared\VideoConferenceShared.vcxproj", "{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|Win32.ActiveCfg = Debug|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|Win32.Build.0 = Debug|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x64.ActiveCfg = Debug|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x64.Build.0 = Debug|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|Win32.ActiveCfg = Release|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|Win32.Build.0 = Release|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.ActiveCfg = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.Build.0 = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|Win32.ActiveCfg = Debug|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|Win32.Build.0 = Debug|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.ActiveCfg = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.Build.0 = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|Win32.ActiveCfg = Release|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|Win32.Build.0 = Release|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0E41348C-22CB-45A4-8A16-8D7BEA070BB2}
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,3 @@
msbuild VideoConferenceProxyFilterx86.sln -p:Configuration="Release" -p:Platform="Win32"
exit 0

View file

@ -0,0 +1,242 @@
#include "VideoCaptureProxyFilter.h"
#include <winrt/base.h>
#include <wil/registry.h>
#include <cguid.h>
namespace
{
#if defined(_WIN64)
class __declspec(uuid("{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}")) GUID_DECL_POWERTOYS_VCM;
#elif defined(_WIN32)
class __declspec(uuid("{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}")) GUID_DECL_POWERTOYS_VCM;
#endif
const GUID CLSID_POWERTOYS_VCM = __uuidof(GUID_DECL_POWERTOYS_VCM);
const REGPINTYPES MEDIA_TYPES = { &MEDIATYPE_Video, &MEDIASUBTYPE_MJPG };
const wchar_t FILTER_NAME[] = L"Output";
const REGFILTERPINS PINS_REGISTRATION = {
(wchar_t*)FILTER_NAME,
false,
true,
false,
false,
&CLSID_NULL,
nullptr,
1,
&MEDIA_TYPES
};
HINSTANCE DLLInstance{};
}
struct __declspec(uuid("9DCAF869-9C13-4BDF-BD0D-3592C5579DD6")) VideoCaptureProxyFilterFactory : winrt::implements<VideoCaptureProxyFilterFactory, IClassFactory>
{
HRESULT STDMETHODCALLTYPE CreateInstance(IUnknown*, REFIID riid, void** ppvObject) noexcept override
{
try
{
return winrt::make<VideoCaptureProxyFilter>()->QueryInterface(riid, ppvObject);
}
catch (...)
{
return winrt::to_hresult();
}
}
HRESULT STDMETHODCALLTYPE LockServer(BOOL fLock) noexcept override
{
if (fLock)
{
++winrt::get_module_lock();
}
else
{
--winrt::get_module_lock();
}
return S_OK;
}
};
HRESULT STDMETHODCALLTYPE DllCanUnloadNow()
{
if (winrt::get_module_lock())
{
return S_FALSE;
}
winrt::clear_factory_cache();
return S_OK;
}
HRESULT STDMETHODCALLTYPE DllGetClassObject(GUID const& clsid, GUID const& iid, void** result)
{
if (!result)
{
return E_POINTER;
}
if (iid != IID_IClassFactory && iid != IID_IUnknown)
{
return E_NOINTERFACE;
}
if (clsid != CLSID_POWERTOYS_VCM)
{
return E_INVALIDARG;
}
try
{
*result = nullptr;
auto factory = winrt::make<VideoCaptureProxyFilterFactory>();
factory->AddRef();
*result = static_cast<void*>(factory.get());
return S_OK;
}
catch (...)
{
return winrt::to_hresult();
}
}
std::wstring RegistryPath()
{
std::wstring registryPath;
registryPath.resize(CHARS_IN_GUID, L'\0');
StringFromGUID2(CLSID_POWERTOYS_VCM, registryPath.data(), CHARS_IN_GUID);
registryPath.resize(registryPath.size() - 1);
registryPath = L"CLSID\\" + registryPath;
return registryPath;
}
bool RegisterServer()
{
std::wstring dllPath;
dllPath.resize(MAX_PATH, L'\0');
if (auto length = GetModuleFileNameW(DLLInstance, dllPath.data(), MAX_PATH); length != 0)
{
dllPath.resize(length);
}
else
{
return false;
}
wil::unique_hkey key;
wil::unique_hkey subkey;
const auto registryPath = RegistryPath();
if (RegCreateKeyW(HKEY_CLASSES_ROOT, registryPath.c_str(), &key))
{
return false;
}
if (RegSetValueW(key.get(), nullptr, REG_SZ, CAMERA_NAME, sizeof(CAMERA_NAME)))
{
return false;
}
if (RegCreateKeyW(key.get(), L"InprocServer32", &subkey))
{
return false;
}
if (RegSetValueW(subkey.get(), nullptr, REG_SZ, dllPath.c_str(), static_cast<DWORD>((dllPath.length() + 1) * sizeof(wchar_t))))
{
return false;
}
const wchar_t THREADING_MODEL[] = L"Both";
RegSetValueExW(subkey.get(), L"ThreadingModel", 0, REG_SZ, (const BYTE*)THREADING_MODEL, sizeof(THREADING_MODEL));
return true;
}
bool UnregisterServer()
{
const auto registryPath = RegistryPath();
return !RegDeleteTreeW(HKEY_CLASSES_ROOT, registryPath.c_str());
}
bool RegisterFilter()
{
auto filterMapper = wil::CoCreateInstanceNoThrow<IFilterMapper2>(CLSID_FilterMapper2);
if (!filterMapper)
{
return false;
}
REGFILTER2 regFilter{ .dwVersion = 1, .dwMerit = MERIT_DO_NOT_USE, .cPins = 1, .rgPins = &PINS_REGISTRATION };
wil::com_ptr_nothrow<IMoniker> moniker;
return SUCCEEDED(filterMapper->RegisterFilter(
CLSID_POWERTOYS_VCM, CAMERA_NAME, &moniker, &CLSID_VideoInputDeviceCategory, nullptr, &regFilter));
}
bool UnregisterFilter()
{
auto filterMapper = wil::CoCreateInstanceNoThrow<IFilterMapper2>(CLSID_FilterMapper2);
if (!filterMapper)
{
return false;
}
return SUCCEEDED(filterMapper->UnregisterFilter(&CLSID_VideoInputDeviceCategory, nullptr, CLSID_POWERTOYS_VCM));
}
HRESULT STDMETHODCALLTYPE DllRegisterServer()
{
if (!RegisterServer())
{
UnregisterServer();
return E_FAIL;
}
auto COMContext = wil::CoInitializeEx(COINIT_APARTMENTTHREADED);
if (!RegisterFilter())
{
UnregisterFilter();
UnregisterServer();
return E_FAIL;
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE DllUnregisterServer()
{
auto COMContext = wil::CoInitializeEx(COINIT_APARTMENTTHREADED);
UnregisterFilter();
UnregisterServer();
return S_OK;
}
HRESULT STDMETHODCALLTYPE DllInstall(BOOL install, LPCWSTR)
{
if (install)
{
return DllRegisterServer();
}
else
{
return DllUnregisterServer();
}
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
DLLInstance = hinstDLL;
}
return true;
}

View file

@ -0,0 +1,7 @@
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
DllInstall PRIVATE

View file

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

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);

View file

@ -0,0 +1,20 @@
; Disable default limits
.option EXPLICIT
.set CabinetFileCountThreshold=0
.set FolderFileCountThreshold=0
.set FolderSizeThreshold=0
.set MaxCabinetSize=0
.set MaxDiskFileCount=0
.set MaxDiskSize=0
.set GenerateInf=ON
.set Compress=OFF
.set Cabinet=ON
.set CabinetNameTemplate=driver.cab
.set DestinationDir=cab_output
.set DiskDirectoryTemplate=driver
VideoConferenceCustomMediaSource.dll
videoconferencevirtualdriver.cat
VideoConferenceVirtualDriver.dll
VideoConferenceVirtualDriver.inf

View file

@ -20,6 +20,7 @@
#include <common/updating/updateState.h>
#include <common/utils/appMutex.h>
#include <common/utils/elevation.h>
#include <common/utils/os-detect.h>
#include <common/utils/processApi.h>
#include <common/utils/resources.h>
@ -43,6 +44,7 @@
#include <common/utils/winapi_error.h>
#include <common/utils/window.h>
#include <common/version/version.h>
#include <gdiplus.h>
namespace
{
@ -130,7 +132,7 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe)
chdir_current_executable();
// Load Powertoys DLLs
const std::array<std::wstring_view, 9> knownModules = {
std::vector<std::wstring_view> knownModules = {
L"modules/FancyZones/FancyZonesModuleInterface.dll",
L"modules/FileExplorerPreview/powerpreview.dll",
L"modules/ImageResizer/ImageResizerExt.dll",
@ -140,6 +142,8 @@ int runner(bool isProcessElevated, bool openSettings, bool openOobe)
L"modules/ShortcutGuide/ShortcutGuideModuleInterface/ShortcutGuideModuleInterface.dll",
L"modules/ColorPicker/ColorPicker.dll",
L"modules/Awake/AwakeModuleInterface.dll",
// TODO(yuyoyuppe): uncomment when VCM should be enabled
//L"modules/VideoConference/VideoConferenceModule.dll"
};
for (const auto& moduleSubdir : knownModules)
@ -264,6 +268,10 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
Gdiplus::GdiplusStartupInput gpStartupInput;
ULONG_PTR gpToken;
GdiplusStartup(&gpToken, &gpStartupInput, NULL);
winrt::init_apartment();
const wchar_t* securityDescriptor =
L"O:BA" // Owner: Builtin (local) administrator

View file

@ -38,7 +38,7 @@
<Link>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>Shcore.lib;Msi.lib;WindowsApp.lib;taskschd.lib;Rstrtmgr.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Shcore.lib;gdiplus.lib;Msi.lib;WindowsApp.lib;taskschd.lib;Rstrtmgr.lib;Shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Manifest>
<EnableDpiAwareness>false</EnableDpiAwareness>

View file

@ -80,6 +80,22 @@ namespace Microsoft.PowerToys.Settings.UI.Library
}
}
private bool videoConference = true;
[JsonPropertyName("Video Conference")]
public bool VideoConference
{
get => this.videoConference;
set
{
if (this.videoConference != value)
{
LogTelemetryEvent(value);
this.videoConference = value;
}
}
}
private bool powerRename = true;
public bool PowerRename

View file

@ -58,4 +58,11 @@
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\ManagedTelemetry.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Windows.Forms">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\System.Windows.Forms.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

Some files were not shown because too many files have changed in this diff Show more