Compare commits

...

2 Commits

Author SHA1 Message Date
yuyoyuppe
0a272be436 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>
2020-12-03 15:24:12 +03:00
Seraphima Zykova
9d39952670 handle key events (#8281) 2020-11-30 18:42:47 +01:00
150 changed files with 7729 additions and 432 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
cd /D "%~dp0"
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" -arch=amd64 -host_arch=amd64 -winsdk=10.0.18362.0
call msbuild -m ../PowerToys.sln /t:modules\VideoConference\VideoConferenceCustomMediaSource,modules\VideoConference\VideoConferenceVirtualDriver /p:Configuration=Release /p:Platform=x64 /p:CIBuild=true || exit /b 1
SET PTRoot=..

View File

@@ -0,0 +1,100 @@
environment:
host:
os: 'windows'
flavor: 'server'
version: '2019'
runtime:
provider: 'appcontainer'
image: 'cdpxwin1809.azurecr.io/global/vse2019:latest'
source_mode: 'map'
version:
name: 'PowerToys'
major: 1
minor: 0
signing_options:
profile: 'external_distribution'
package_sources:
nuget:
feeds:
'Nuget': 'https://api.nuget.org/v3/index.json'
'Toolset': 'https://msazure.pkgs.visualstudio.com/_packaging/Toolset/nuget/v3/index.json'
'PipelineBuildSupplement': 'https://msazure.pkgs.visualstudio.com/_packaging/PipelineBuildSupplement/nuget/v3/index.json'
restore:
commands:
- !!defaultcommand
name: 'Restore Power Toys Telemetry'
command: '.pipelines\restore-telemetry.cmd'
- !!defaultcommand
name: 'Restore Power Toys'
command: '.pipelines\restore.cmd'
- !!defaultcommand
name: 'Restore Localization packages'
command: '.pipelines\restore-localization.cmd'
build:
commands:
- !!buildcommand
name: 'Build Power Toys'
command: '.pipelines\build-videoconference.cmd'
artifacts:
- to: 'Symbols'
include:
- 'x64/**/*.pdb'
exclude:
- 'x64/Release/obj/**/*.pdb'
- from: 'x64/Release'
to: 'Build_Output'
include:
- 'modules\VideoConference\VideoConferenceModule.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceCustomMediaSource.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\videoconferencevirtualdriver.cat'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.inf'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.cab'
signing_options:
sign_inline: true # This does signing a soon as this command completes
- !!buildcommand
name: 'Localize Power Toys'
command: '.pipelines\build-localization.cmd'
artifacts:
- from: 'out\loc'
to: 'loc'
include:
- '**/*'
#package:
# commands:
# - !!buildcommand
# name: 'Build MSIX package'
# command: 'installer\msix\build_msix_cdpx.cmd'
# artifacts:
# - from: 'installer\msix\bin'
# to: 'Build_MSIX_Package_Output'
# include:
# - '*.msix'
# - '*.msixbundle'
# signing_options:
# profile: '400'
static_analysis_options:
binskim_options:
files_to_scan:
- from: 'installer/packages'
exclude:
- 'WiX.*/**/*.dll'
- 'Wix.*/**/*.exe'
moderncop_options:
files_to_scan:
- from: 'src'
exclude:
- '**/just.config.js'
- '**/webpack.config.js'
- '**/webpack.serve.config.js'
- '**/dist/bundle.js'

View File

@@ -127,6 +127,12 @@ build:
- 'modules\Microsoft.Launcher.dll'
- 'modules\PowerRename\PowerRenameExt.dll'
- 'modules\ShortcutGuide\ShortcutGuide.dll'
- 'modules\VideoConference\VideoConferenceModule.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceCustomMediaSource.dll'
- 'modules\VideoConference\VideoConferenceVirtualDriver\videoconferencevirtualdriver.cat'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.inf'
- 'modules\VideoConference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.cab'
- 'Notifications.dll'
- 'os-detection.dll'
- 'PowerToys.exe'

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

@@ -229,7 +229,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Settings.UI.Library", "src\core\Microsoft.PowerToys.Settings.UI.Library\Microsoft.PowerToys.Settings.UI.Library.csproj", "{B1BCC8C6-46B5-4BFA-8F22-20F32D99EC6A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "interop", "src\common\interop\interop.vcxproj", "{F055103B-F80B-4D0C-BF48-057C55620033}"
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToysInterop", "src\common\interop\interop.vcxproj", "{F055103B-F80B-4D0C-BF48-057C55620033}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common-md-flag", "src\common\common-md-flag\common-md-flag.vcxproj", "{985B3F2F-CEED-4C0A-A249-69257E719145}"
EndProject
@@ -265,6 +265,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Setting
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Calculator.UnitTest", "src\modules\launcher\Plugins\Microsoft.Plugin.Calculator.UnitTest\Microsoft.Plugin.Calculator.UnitTest.csproj", "{632BBE62-5421-49EA-835A-7FFA4F499BD6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VideoConference", "VideoConference", "{05BE6150-D5B3-48F0-AEB9-C44096950C6D}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceCustomMediaSource", "src\modules\videoconference\VideoConferenceCustomMediaSource\VideoConferenceCustomMediaSource.vcxproj", "{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}"
ProjectSection(ProjectDependencies) = postProject
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceModule", "src\modules\videoconference\VideoConferenceModule\Video Conference.vcxproj", "{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}"
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}") = "VideoConferenceVirtualDriver", "src\modules\videoconference\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.vcxproj", "{3098C6BF-E96E-4793-A70E-FB09B741580A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Plugin.Folder.UnitTests", "src\modules\launcher\Plugins\Microsoft.Plugin.Folder.UnitTests\Microsoft.Plugin.Folder.UnitTests.csproj", "{4FA206A5-F69F-4193-BF8F-F6EEB496734C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest-ColorPickerUI", "src\modules\colorPicker\UnitTest-ColorPickerUI\UnitTest-ColorPickerUI.csproj", "{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1}"
@@ -535,6 +548,24 @@ Global
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Debug|x64.Build.0 = Debug|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x64.ActiveCfg = Release|x64
{0F85E674-34AE-443D-954C-8321EB8B93B1}.Release|x64.Build.0 = Release|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Debug|x64.ActiveCfg = Debug|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Debug|x64.Build.0 = Debug|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Release|x64.ActiveCfg = Release|x64
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}.Release|x64.Build.0 = Release|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Debug|x64.ActiveCfg = Debug|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Debug|x64.Build.0 = Debug|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Release|x64.ActiveCfg = Release|x64
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193}.Release|x64.Build.0 = 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}.Release|x64.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.Build.0 = Release|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Debug|x64.ActiveCfg = Debug|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Debug|x64.Build.0 = Debug|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Debug|x64.Deploy.0 = Debug|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Release|x64.ActiveCfg = Release|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Release|x64.Build.0 = Release|x64
{3098C6BF-E96E-4793-A70E-FB09B741580A}.Release|x64.Deploy.0 = 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}.Release|x64.ActiveCfg = Release|x64
@@ -630,6 +661,11 @@ Global
{03276A39-D4E9-417C-8FFD-200B0EE5E871} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{B81FB7B6-D30E-428F-908A-41422EFC1172} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{0F85E674-34AE-443D-954C-8321EB8B93B1} = {C3081D9A-1586-441A-B5F4-ED815B3719C1}
{05BE6150-D5B3-48F0-AEB9-C44096950C6D} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{43AD9BF7-E765-48FE-9826-71A8F2CB12DD} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{FD2CAFFC-D682-4ED9-A06B-5FC88AE0A193} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{3098C6BF-E96E-4793-A70E-FB09B741580A} = {05BE6150-D5B3-48F0-AEB9-C44096950C6D}
{632BBE62-5421-49EA-835A-7FFA4F499BD6} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{4FA206A5-F69F-4193-BF8F-F6EEB496734C} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{090CD7B7-3B0C-4D1D-BC98-83EB5D799BC1} = {1D78B84B-CA39-406C-98F4-71F7EC266CC0}

View File

@@ -75,6 +75,10 @@ modify --installpath "%ProgramFiles(x86)%\Microsoft Visual Studio\2019\%targetFo
--add Microsoft.VisualStudio.Component.VC.ATL.Spectre
```
**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`.

View File

@@ -2,8 +2,9 @@
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureNuGetPackageBuildImports" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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" />
<Import Project="..\..\src\hasWDK.props" />
<PropertyGroup>
<DefineConstants>Version=$(Version);</DefineConstants>
<DefineConstants>Version=$(Version);HasWDK=$(HasWDK)</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>

View File

@@ -9,6 +9,7 @@
<?define PowerRenameProjectName="PowerRename"?>
<?define ShortcutGuideProjectName="ShortcutGuide"?>
<?define ColorPickerProjectName="ColorPicker"?>
<?define VideoConferenceProjectName="VideoConference"?>
<?define RepoDir="$(var.ProjectDir)..\..\" ?>
<?define BinX64Dir="$(var.RepoDir)x64\$(var.Configuration)\" ?>
@@ -87,6 +88,26 @@
<Custom Action="RegisterPowerToysSchTask" After="InstallFiles">
NOT Installed and CREATESCHEDULEDTASK = 1
</Custom>
<?if $(var.HasWDK)="true" ?>
<Custom Action="SetCertifyVirtualCameraDriverParam" After="RegisterPowerToysSchTask" >
WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="CertifyVirtualCameraDriver" After="SetCertifyVirtualCameraDriverParam" >
NOT Installed and WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="SetInstallVirtualCameraDriverParam" After="CertifyVirtualCameraDriver" >
WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="InstallVirtualCameraDriver" After="SetInstallVirtualCameraDriverParam" >
NOT Installed and WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="SetUninstallVirtualCameraDriverParam" Before="UninstallVirtualCameraDriver" >
WINDOWSBUILDNUMBER >= 18362
</Custom>
<Custom Action="UninstallVirtualCameraDriver" Before="RemoveFiles">
Installed and WINDOWSBUILDNUMBER >= 18362
</Custom>
<?endif?>
<Custom Action="WixCloseApplications" Before="RemoveFiles" />
<Custom Action="RemovePowerToysSchTasks" After="RemoveFiles" />
<Custom Action="TelemetryLogInstallSuccess" After="InstallFinalize">
@@ -204,6 +225,44 @@
DllEntry="DetectPrevInstallPathCA"
/>
<?if $(var.HasWDK)="true" ?>
<CustomAction Id="SetCertifyVirtualCameraDriverParam"
Property="CertifyVirtualCameraDriver"
Value="[#VideoConferenceVirtualDriver.cer]" />
<CustomAction Id="CertifyVirtualCameraDriver"
Return="ignore"
Impersonate="no"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CertifyVirtualCameraDriverCA"
/>
<CustomAction Id="SetInstallVirtualCameraDriverParam"
Property="InstallVirtualCameraDriver"
Value="[#VideoConferenceVirtualDriver.inf]" />
<CustomAction Id="InstallVirtualCameraDriver"
Return="ignore"
Impersonate="no"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="InstallVirtualCameraDriverCA"
/>
<CustomAction Id="SetUninstallVirtualCameraDriverParam"
Property="UninstallVirtualCameraDriver"
Value="[#VideoConferenceVirtualDriver.inf]" />
<CustomAction Id="UninstallVirtualCameraDriver"
Return="ignore"
Impersonate="no"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="UninstallVirtualCameraDriverCA"
/>
<?endif?>
<!-- Close 'PowerToys.exe' before uninstall-->
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
<util:CloseApplication CloseMessage="yes" Target="PowerToys.exe" ElevatedCloseMessage="yes" RebootPrompt="no" TerminateProcess="0" />
@@ -222,6 +281,11 @@
<Directory Id="ImageResizerInstallFolder" Name="$(var.ImageResizerProjectName)" />
<Directory Id="PowerRenameInstallFolder" Name="$(var.PowerRenameProjectName)"/>
<Directory Id="ShortcutGuideInstallFolder" Name="$(var.ShortcutGuideProjectName)"/>
<?if $(var.HasWDK)="true" ?>
<Directory Id="VideoConferenceInstallFolder" Name="$(var.VideoConferenceProjectName)">
<Directory Id="VideoConferenceIconsFolder" Name="Icons" />
</Directory>
<?endif?>
<Directory Id="FileExplorerPreviewInstallFolder" Name="FileExplorerPreview" />
<Directory Id="FancyZonesInstallFolder" Name="$(var.FancyZonesProjectName)" />
<Directory Id="KeyboardManagerInstallFolder" Name="$(var.KeyboardManagerProjectName)" />
@@ -589,6 +653,38 @@
<File Source="$(var.BinX64Dir)modules\$(var.ShortcutGuideProjectName)\$(var.ShortcutGuideProjectName).dll" KeyPath="yes" />
</Component>
</DirectoryRef>
<?if $(var.HasWDK)="true" ?>
<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 Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceModule.dll" KeyPath="yes" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\black.bmp" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\videoconferencevirtualdriver.cat" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\VideoConferenceCustomMediaSource.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.dll" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver\VideoConferenceVirtualDriver.inf" />
<File Source="$(var.BinX64Dir)modules\$(var.VideoConferenceProjectName)\VideoConferenceVirtualDriver.cer" />
</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>
<?endif?>
<DirectoryRef Id="KeyboardManagerInstallFolder" FileSource="$(var.BinX64Dir)modules\$(var.KeyboardManagerProjectName)\">
<Component Id="Module_KeyboardManager" Guid="9279BD82-786F-4F0B-8E49-DB484EE34C9B" Win64="yes">
@@ -644,7 +740,7 @@
<File Source="$(var.BinX64Dir)SettingsUIRunner\Microsoft.PowerToys.Settings.UI.Runner.exe"/>
<File Source="$(var.BinX64Dir)SettingsUIRunner\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;Microsoft.PowerToys.Settings.UI.Runner.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;Telemetry.dll;ManagedCommon.dll?>
<?foreach File in concrt140_app.dll;Microsoft.Bcl.AsyncInterfaces.dll;System.IO.Abstractions.dll;Microsoft.PowerToys.Settings.UI.Lib.dll;Microsoft.PowerToys.Settings.UI.Runner.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;Telemetry.dll;ManagedCommon.dll;System.Deployment.dll;System.Windows.Forms.dll;System.Runtime.Serialization.Formatters.Soap.dll?>
<File Id="SettingsV2_$(var.File)" Source="$(var.BinX64Dir)SettingsUIRunner\$(var.File)" />
<?endforeach?>
<!-- json -->
@@ -684,7 +780,7 @@
</DirectoryRef>
<DirectoryRef Id="SettingsV2AssetsModulesInstallFolder" FileSource="$(var.BinX64Dir)SettingsUIRunner\Assets\Modules">
<Component Id="SettingsV2AssetsModules" Guid="A0B961A9-77D0-4223-88A9-E3B41BD9C329" Win64="yes">
<?foreach File in ColorPicker.png;FancyZones.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png?>
<?foreach File in ColorPicker.png;FancyZones.png;ImageResizer.png;KBM.png;PowerLauncher.png;PowerPreview.png;PowerRename.png;PT.png;ShortcutGuide.png;VideoConference.png?>
<File Source="$(var.BinX64Dir)SettingsUIRunner\Assets\Modules\$(var.File)" />
<?endforeach?>
</Component>
@@ -771,6 +867,8 @@
<ComponentRef Id="vcredist_dlls" />
<ComponentRef Id="PowerToysSvgs" />
<ComponentRef Id="Module_ShortcutGuide" />
<ComponentRef Id="Module_VideoConference" />
<ComponentRef Id="Module_VideoConferenceIcons" />
<ComponentRef Id="Module_FancyZones" />
<ComponentRef Id="DesktopShortcut" />
<ComponentRef Id="Module_PowerRename" />

View File

@@ -31,7 +31,6 @@ echo ^</Project^> >> !settingsPublishProfile!
rem In case of Release we should not use Debug CRT in VCRT forwarders
msbuild !PTRoot!\src\core\Microsoft.PowerToys.Settings.UI.Runner\Microsoft.PowerToys.Settings.UI.Runner.csproj -t:Publish -p:Configuration="Release" -p:Platform="x64" -p:AppxBundle=Never -p:VCRTForwarders-IncludeDebugCRT=false -p:PublishProfile=!settingsProfileFileName!
rem Publish Launcher
SET launcherProfileFolderName=!PTRoot!\src\modules\launcher\PowerLauncher\Properties\PublishProfiles\

View File

@@ -16,6 +16,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.
@@ -565,6 +566,8 @@ LExit:
return WcaFinalize(er);
}
UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
@@ -663,6 +666,166 @@ UINT __stdcall TerminateProcessesCA(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);
}
// DllMain - Initialize and cleanup WiX custom action utils.
extern "C" BOOL WINAPI DllMain(__in HINSTANCE hInst, __in ULONG ulReason, __in LPVOID)

View File

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

View File

@@ -60,7 +60,7 @@
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Crypt32.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;Newdev.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>
@@ -81,8 +81,11 @@
<LanguageStandard>stdcpplatest</LanguageStandard>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<ClCompile Condition="'$(CIBuild)'=='true'">
<AdditionalOptions>/DCIBuild</AdditionalOptions>
</ClCompile>
<Link>
<AdditionalDependencies>Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>Crypt32.lib;Psapi.lib;Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;Newdev.lib;..\..\$(PlatformShortName)\$(Configuration)\common.lib;..\..\$(PlatformShortName)\$(Configuration)\updating.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
<GenerateDebugInformation>true</GenerateDebugInformation>

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

@@ -31,6 +31,7 @@
<ProjectName>action_runner</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="..\..\deps\expected.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>

View File

@@ -0,0 +1,142 @@
#include "pch.h"
#include "MicrophoneDevice.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);
}
}
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)
{
return std::nullopt;
}
wil::com_ptr_nothrow<IMMDevice> captureDevice;
deviceEnumerator->GetDefaultAudioEndpoint(eCapture, eCommunications, &captureDevice);
if (!captureDevice)
{
return std::nullopt;
}
wil::com_ptr_nothrow<IAudioEndpointVolume> microphoneEndpoint;
captureDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, nullptr, reinterpret_cast<LPVOID*>(&microphoneEndpoint));
if (!microphoneEndpoint)
{
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)
{
return microphoneDevices;
}
wil::com_ptr_nothrow<IMMDeviceCollection> captureDevices;
deviceEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &captureDevices);
if (!captureDevices)
{
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,60 @@
#pragma once
#include <winrt/base.h>
#include <wil/resource.h>
#include <wil/com.h>
#include <Windows.h>
#include <Unknwn.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,92 @@
#include "pch.h"
#include "VideoCaptureDeviceList.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);
}
// Enumerate devices.
if (SUCCEEDED(hr))
{
hr = MFEnumDeviceSources(pAttributes.get(), &m_ppDevices, &m_numberDevices);
}
if (FAILED(hr))
{
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

@@ -97,19 +97,23 @@
<ClInclude Include="..\common.h" />
<ClInclude Include="..\keyboard_layout.h" />
<ClInclude Include="..\keyboard_layout_impl.h" />
<ClInclude Include="..\MicrophoneDevice.h" />
<ClInclude Include="..\os-detect.h" />
<ClInclude Include="..\pch.h" />
<ClInclude Include="..\two_way_pipe_message_ipc.h" />
<ClInclude Include="..\two_way_pipe_message_ipc_impl.h" />
<ClInclude Include="..\VideoCaptureDeviceList.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\common.cpp" />
<ClCompile Include="..\keyboard_layout.cpp" />
<ClCompile Include="..\MicrophoneDevice.cpp" />
<ClCompile Include="..\os-detect.cpp" />
<ClCompile Include="..\pch.cpp">
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\two_way_pipe_message_ipc.cpp" />
<ClCompile Include="..\VideoCaptureDeviceList.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -36,6 +36,12 @@
<ClInclude Include="..\os-detect.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\MicrophoneDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\VideoCaptureDeviceList.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\keyboard_layout.cpp">
@@ -53,6 +59,12 @@
<ClCompile Include="..\os-detect.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\MicrophoneDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@@ -49,7 +49,7 @@ bool is_system_window(HWND hwnd, const char* class_name)
int run_message_loop(const bool until_idle, const std::optional<uint32_t> timeout_seconds)
{
MSG msg;
MSG msg{};
bool stop = false;
UINT_PTR timerId = 0;
if (timeout_seconds.has_value())

View File

@@ -129,6 +129,8 @@
<ClInclude Include="keyboard_layout.h" />
<ClInclude Include="keyboard_layout_impl.h" />
<ClInclude Include="LowlevelKeyboardEvent.h" />
<ClInclude Include="MicrophoneDevice.h" />
<ClInclude Include="naming.h" />
<ClInclude Include="notifications.h" />
<ClInclude Include="processApi.h" />
<ClInclude Include="RcResource.h" />
@@ -139,7 +141,9 @@
<ClInclude Include="timeutil.h" />
<ClInclude Include="toast_dont_show_again.h" />
<ClInclude Include="two_way_pipe_message_ipc.h" />
<ClInclude Include="user.h" />
<ClInclude Include="VersionHelper.h" />
<ClInclude Include="VideoCaptureDeviceList.h" />
<ClInclude Include="window_helpers.h" />
<ClInclude Include="icon_helpers.h" />
<ClInclude Include="json.h" />
@@ -165,7 +169,9 @@
<ClCompile Include="dpi_aware.cpp" />
<ClCompile Include="json.cpp" />
<ClCompile Include="keyboard_layout.cpp" />
<ClCompile Include="MicrophoneDevice.cpp" />
<ClCompile Include="monitors.cpp" />
<ClCompile Include="naming.cpp" />
<ClCompile Include="notifications.cpp" />
<ClCompile Include="on_thread_executor.cpp" />
<ClCompile Include="os-detect.cpp" />
@@ -180,10 +186,12 @@
<ClCompile Include="start_visible.cpp" />
<ClCompile Include="tasklist_positions.cpp" />
<ClCompile Include="common.cpp" />
<ClCompile Include="user.cpp" />
<ClCompile Include="toast_dont_show_again.cpp" />
<ClCompile Include="version.cpp" />
<ClCompile Include="two_way_pipe_message_ipc.cpp" />
<ClCompile Include="VersionHelper.cpp" />
<ClCompile Include="VideoCaptureDeviceList.cpp" />
<ClCompile Include="windows_colors.cpp" />
<ClCompile Include="window_helpers.cpp" />
<ClCompile Include="winstore.cpp" />
@@ -204,4 +212,4 @@
<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'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.200902.2\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>
</Project>

View File

@@ -129,6 +129,18 @@
<ClInclude Include="string_utils.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="naming.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="user.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="MicrophoneDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="VideoCaptureDeviceList.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="toast_dont_show_again.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -204,6 +216,18 @@
<ClCompile Include="comUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="naming.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="user.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="MicrophoneDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="toast_dont_show_again.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@@ -211,4 +235,4 @@
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1,4 +1,167 @@
#include "pch.h"
#include "interop.h"
#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
#include <functional>
#include "..\keyboard_layout.h"
#include "..\two_way_pipe_message_ipc.h"
#include "..\common.h"
#include "..\shared_constants.h"
#include "..\os-detect.h"
#pragma warning(push)
#pragma warning(disable : 4793) // some functions must be compiled as native
#include "..\MicrophoneDevice.h"
#include "..\VideoCaptureDeviceList.h"
#pragma warning(pop)
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
{
public
ref class LayoutMapManaged
{
public:
LayoutMapManaged() :
_map(new LayoutMap) {}
~LayoutMapManaged()
{
delete _map;
}
String ^ GetKeyName(DWORD key) {
return gcnew String(_map->GetKeyName(key).c_str());
}
void Updatelayout()
{
_map->UpdateLayout();
}
protected:
!LayoutMapManaged()
{
delete _map;
}
private:
LayoutMap* _map;
};
public
ref class TwoWayPipeMessageIPCManaged
{
public:
delegate void ReadCallback(String ^ message);
TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
{
_wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
_callback = callback;
TwoWayPipeMessageIPC::callback_function cb = nullptr;
if (callback != nullptr)
{
cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
}
_pipe = new TwoWayPipeMessageIPC(
msclr::interop::marshal_as<std::wstring>(inputPipeName),
msclr::interop::marshal_as<std::wstring>(outputPipeName),
cb);
}
~TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
void Send(String ^ msg)
{
_pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
}
void Start()
{
_pipe->start(nullptr);
}
void End()
{
_pipe->end();
}
protected:
!TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
private:
delegate void InternalReadCallback(const std::wstring& msg);
TwoWayPipeMessageIPC* _pipe;
ReadCallback ^ _callback;
InternalReadCallback ^ _wrapperCallback;
void ReadCallbackHelper(const std::wstring& msg)
{
_callback(gcnew String(msg.c_str()));
}
};
public
ref class CommonManaged
{
public:
static String ^ GetProductVersion() {
return gcnew String(get_product_version().c_str());
}
static bool ShouldNewSettingsBeUsed()
{
return UseNewSettings();
}
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")
{
names->Add(name);
}
}
return names;
}
};
public
ref class Constants
{
public:
literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;
static String ^ PowerLauncherSharedEvent() {
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
};
}

View File

@@ -1,136 +0,0 @@
#pragma once
#include <msclr\marshal.h>
#include <msclr\marshal_cppstd.h>
#include <functional>
#include "..\keyboard_layout.h"
#include "..\two_way_pipe_message_ipc.h"
#include "..\common.h"
#include "..\shared_constants.h"
#include "..\os-detect.h"
using namespace System;
using namespace System::Runtime::InteropServices;
//https://docs.microsoft.com/en-us/cpp/dotnet/how-to-wrap-native-class-for-use-by-csharp?view=vs-2019
namespace interop
{
public
ref class LayoutMapManaged
{
public:
LayoutMapManaged() :
_map(new LayoutMap) {}
~LayoutMapManaged()
{
delete _map;
}
String ^ GetKeyName(DWORD key) {
return gcnew String(_map->GetKeyName(key).c_str());
}
void Updatelayout()
{
_map->UpdateLayout();
}
protected:
!LayoutMapManaged()
{
delete _map;
}
private:
LayoutMap* _map;
};
public
ref class TwoWayPipeMessageIPCManaged
{
public:
delegate void ReadCallback(String ^ message);
TwoWayPipeMessageIPCManaged(String ^ inputPipeName, String ^ outputPipeName, ReadCallback ^ callback)
{
_wrapperCallback = gcnew InternalReadCallback(this, &TwoWayPipeMessageIPCManaged::ReadCallbackHelper);
_callback = callback;
TwoWayPipeMessageIPC::callback_function cb = nullptr;
if (callback != nullptr)
{
cb = (TwoWayPipeMessageIPC::callback_function)(void*)Marshal::GetFunctionPointerForDelegate(_wrapperCallback);
}
_pipe = new TwoWayPipeMessageIPC(
msclr::interop::marshal_as<std::wstring>(inputPipeName),
msclr::interop::marshal_as<std::wstring>(outputPipeName),
cb);
}
~TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
void Send(String ^ msg)
{
_pipe->send(msclr::interop::marshal_as<std::wstring>(msg));
}
void Start()
{
_pipe->start(nullptr);
}
void End()
{
_pipe->end();
}
protected:
!TwoWayPipeMessageIPCManaged()
{
delete _pipe;
}
private:
delegate void InternalReadCallback(const std::wstring& msg);
TwoWayPipeMessageIPC* _pipe;
ReadCallback ^ _callback;
InternalReadCallback ^ _wrapperCallback;
void ReadCallbackHelper(const std::wstring& msg)
{
_callback(gcnew String(msg.c_str()));
}
};
public
ref class CommonManaged
{
public:
static String^ GetProductVersion()
{
return gcnew String(get_product_version().c_str());
}
static bool ShouldNewSettingsBeUsed()
{
return UseNewSettings();
}
};
public
ref class Constants
{
public:
literal int VK_WIN_BOTH = CommonSharedConstants::VK_WIN_BOTH;
static String^ PowerLauncherSharedEvent()
{
return gcnew String(CommonSharedConstants::POWER_LAUNCHER_SHARED_EVENT);
}
};
}

View File

@@ -23,6 +23,7 @@
<Keyword>ManagedCProj</Keyword>
<RootNamespace>interop</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectName>PowerToysInterop</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
@@ -73,7 +74,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>WindowsApp.lib;Mf.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>
</DelayLoadDLLs>
</Link>
@@ -87,7 +88,7 @@
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<AdditionalDependencies>WindowsApp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>WindowsApp.lib;Mf.lib;%(AdditionalDependencies)</AdditionalDependencies>
<DelayLoadDLLs>
</DelayLoadDLLs>
</Link>
@@ -129,7 +130,6 @@
</Target>
<ItemGroup>
<ClInclude Include="HotkeyManager.h" />
<ClInclude Include="interop.h" />
<ClInclude Include="KeyboardHook.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
@@ -151,6 +151,9 @@
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\common-md-flag\common-md-flag.vcxproj">
<Project>{985b3f2f-ceed-4c0a-a249-69257e719145}</Project>
@@ -158,5 +161,12 @@
</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

@@ -15,9 +15,6 @@
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="interop.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>

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

@@ -72,3 +72,23 @@ MonitorInfo MonitorInfo::GetPrimaryMonitor()
EnumDisplayMonitors(NULL, NULL, GetPrimaryDisplayEnumCb, reinterpret_cast<LPARAM>(&primary));
return primary;
}
MonitorInfo MonitorInfo::GetFromWindow(HWND hwnd)
{
auto monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
return GetFromHandle(monitor);
}
MonitorInfo MonitorInfo::GetFromPoint(POINT p)
{
auto monitor = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
return GetFromHandle(monitor);
}
MonitorInfo MonitorInfo::GetFromHandle(HMONITOR monitor)
{
MONITORINFOEX monitor_info;
monitor_info.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(monitor, &monitor_info);
return MonitorInfo(monitor, monitor_info.rcWork);
}

View File

@@ -33,6 +33,9 @@ struct MonitorInfo : ScreenSize
// Returns monitor rects ordered from left to right
static std::vector<MonitorInfo> GetMonitors(bool includeNonWorkingArea);
static MonitorInfo GetPrimaryMonitor();
static MonitorInfo GetFromWindow(HWND hwnd);
static MonitorInfo GetFromPoint(POINT p);
static MonitorInfo GetFromHandle(HMONITOR monitor);
};
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs);

20
src/common/naming.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "pch.h"
#include "naming.h"
#include "user.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;
}

5
src/common/naming.h Normal file
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

@@ -1,8 +1,16 @@
#define NOMINMAX
#include <Windows.h>
#include <Unknwn.h>
#include <winrt/base.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Windows.h>
#include <dxgi1_3.h>
#include <d3d11_2.h>
#include <d2d1_3.h>
#include <d2d1_3helper.h>
#include <d2d1helper.h>
#include <dwrite.h>
#include <dcomp.h>
#include <dwmapi.h>
#include <Shobjidl.h>

View File

@@ -7,6 +7,7 @@
namespace PTSettingsHelper
{
std::wstring get_module_save_folder_location(std::wstring_view powertoy_name);
std::wstring get_module_save_file_location(std::wstring_view powertoy_name);
std::wstring get_root_save_folder_location();
void save_module_settings(std::wstring_view powertoy_name, json::JsonObject& settings);

View File

@@ -203,7 +203,7 @@ namespace PowerToysSettings
if (output_bytes == 1 && output[0] >= 'a' && output[0] <= 'z')
{
// Make Latin letters keys capital, as it looks better
output[0] = toupper(output[0]);
output[0] = static_cast<wchar_t>(toupper(output[0]));
}
return output.data();
}

View File

@@ -26,6 +26,13 @@ namespace timeutil
return std::nullopt;
}
}
inline time_t from_filetime(const FILETIME& ft)
{
ULARGE_INTEGER ull;
ull.LowPart = ft.dwLowDateTime;
ull.HighPart = ft.dwHighDateTime;
return ull.QuadPart / 10000000ULL - 11644473600ULL;
}
inline std::time_t now()
{

View File

@@ -18,7 +18,7 @@ namespace updating
{
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
current_version_to_next_version += L" -> ";
current_version_to_next_version += info.version_string;
current_version_to_next_version += info.version.toWstring();
return current_version_to_next_version;
}
@@ -54,7 +54,7 @@ namespace updating
remove_toasts(UPDATING_PROCESS_TOAST_TAG);
progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
std::wstring progress_title{ info.version.toWstring() };
progress_title += L' ';
progress_title += strings.DOWNLOAD_IN_PROGRESS;
@@ -134,7 +134,7 @@ namespace updating
{
progress_bar_params progress_bar_params;
std::wstring progress_title{ info.version_string };
std::wstring progress_title{ info.version.toWstring() };
progress_title += L' ';
progress_title += progress < 1 ? strings.DOWNLOAD_IN_PROGRESS : strings.DOWNLOAD_COMPLETE;
progress_bar_params.progress_title = progress_title;
@@ -142,4 +142,4 @@ namespace updating
update_toast_progress_bar(UPDATING_PROCESS_TOAST_TAG, progress_bar_params);
}
}
}
}

View File

@@ -22,13 +22,54 @@
namespace // Strings in this namespace should not be localized
{
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
const wchar_t ALL_RELEASES_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases";
const size_t MAX_DOWNLOAD_ATTEMPTS = 3;
}
namespace updating
{
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings)
std::optional<VersionHelper> extract_version_from_release_object(const json::JsonObject& release_object)
{
try
{
return VersionHelper{ winrt::to_string(release_object.GetNamedString(L"tag_name")) };
}
catch (...)
{
}
return std::nullopt;
}
std::pair<Uri, std::wstring> extract_installer_asset_download_info(const json::JsonObject& release_object)
{
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
// Desc-sorted by its priority
const std::array<std::wstring_view, 2> asset_extensions = { L".exe", L".msi" };
for (const auto asset_extension : asset_extensions)
{
for (auto asset_elem : release_object.GetNamedArray(L"assets"))
{
auto asset{ asset_elem.GetObjectW() };
std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str();
std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower);
const bool extension_matched = filename_lower.ends_with(asset_extension);
const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos;
const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos;
const bool asset_matched = extension_matched && architecture_matched && filename_matched;
if (extension_matched && architecture_matched && filename_matched)
{
return std::make_pair(Uri{ asset.GetNamedString(L"browser_download_url") }, std::move(filename_lower));
}
}
}
throw std::runtime_error("Release object doesn't have the required asset");
}
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease)
{
// If the current version starts with 0.0.*, it means we're on a local build from a farm and shouldn't check for updates.
if (VERSION_MAJOR == 0 && VERSION_MINOR == 0)
@@ -38,46 +79,53 @@ namespace updating
try
{
http::HttpClient client;
const auto body = co_await client.request(winrt::Windows::Foundation::Uri{ LATEST_RELEASE_ENDPOINT });
auto json_body = json::JsonValue::Parse(body).GetObjectW();
auto new_version = json_body.GetNamedString(L"tag_name");
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
VersionHelper github_version(winrt::to_string(new_version));
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
json::JsonObject release_object;
const VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
VersionHelper github_version = current_version;
if (prerelease)
{
const auto body = co_await client.request(Uri{ ALL_RELEASES_ENDPOINT });
for (const auto& json : json::JsonValue::Parse(body).GetArray())
{
auto potential_release_object = json.GetObjectW();
const bool is_prerelease = potential_release_object.GetNamedBoolean(L"prerelease", false);
auto extracted_version = extract_version_from_release_object(potential_release_object);
if (!is_prerelease || !extracted_version || *extracted_version <= github_version)
{
continue;
}
// Do not break, since https://developer.github.com/v3/repos/releases/#list-releases
// doesn't specify the order in which release object appear
github_version = std::move(*extracted_version);
release_object = std::move(potential_release_object);
}
}
else
{
const auto body = co_await client.request(Uri{ LATEST_RELEASE_ENDPOINT });
release_object = json::JsonValue::Parse(body).GetObjectW();
if (auto extracted_version = extract_version_from_release_object(release_object))
{
github_version = *extracted_version;
}
}
if (github_version <= current_version)
{
co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_UP_TO_DATE);
}
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
constexpr const std::wstring_view required_filename_pattern = updating::INSTALLER_FILENAME_PATTERN;
// Desc-sorted by its priority
const std::array<std::wstring_view, 2> asset_extensions = { L".exe", L".msi" };
for (const auto asset_extension : asset_extensions)
{
for (auto asset_elem : json_body.GetNamedArray(L"assets"))
{
auto asset{ asset_elem.GetObjectW() };
std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str();
std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower);
const bool extension_matched = filename_lower.ends_with(asset_extension);
const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos;
const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos;
if (extension_matched && architecture_matched && filename_matched)
{
winrt::Windows::Foundation::Uri msi_download_url{ asset.GetNamedString(L"browser_download_url") };
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str(), std::move(msi_download_url), std::move(filename_lower) };
}
}
}
Uri release_page_url{ release_object.GetNamedString(L"html_url") };
auto installer_download_url = extract_installer_asset_download_info(release_object);
co_return new_version_download_info{ std::move(release_page_url),
std::move(github_version),
std::move(installer_download_url.first),
std::move(installer_download_url.second) };
}
catch (...)
{
co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_CHECK_ERROR);
}
co_return nonstd::make_unexpected(strings.GITHUB_NEW_VERSION_CHECK_ERROR);
}
bool could_be_costly_connection()
@@ -142,19 +190,6 @@ namespace updating
}
}
std::future<std::wstring> check_new_version_available(const notifications::strings& strings)
{
auto new_version = co_await get_new_github_version_info_async(strings);
if (!new_version)
{
updating::notifications::show_unavailable(strings, std::move(new_version.error()));
co_return VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
}
updating::notifications::show_available(new_version.value(), strings);
co_return new_version->version_string;
}
std::future<std::wstring> download_update(const notifications::strings& strings)
{
const auto new_version = co_await get_new_github_version_info_async(strings);

View File

@@ -5,26 +5,27 @@
#include <future>
#include <filesystem>
#include <winrt/Windows.Foundation.h>
#include <expected.hpp>
#include "notifications.h"
#include "../VersionHelper.h"
namespace updating
{
using winrt::Windows::Foundation::Uri;
struct new_version_download_info
{
winrt::Windows::Foundation::Uri release_page_uri = nullptr;
std::wstring version_string;
winrt::Windows::Foundation::Uri installer_download_url = nullptr;
Uri release_page_uri = nullptr;
VersionHelper version{ 0, 0, 0 };
Uri installer_download_url = nullptr;
std::wstring installer_filename;
};
std::future<void> try_autoupdate(const bool download_updates_automatically, const notifications::strings&);
std::filesystem::path get_pending_updates_path();
std::future<std::wstring> check_new_version_available(const notifications::strings&);
std::future<std::wstring> download_update(const notifications::strings&);
std::future<nonstd::expected<new_version_download_info, std::wstring>> get_new_github_version_info_async(const notifications::strings& strings, const bool prerelease = true);
// non-localized
constexpr inline std::wstring_view INSTALLER_FILENAME_PATTERN = L"powertoyssetup";
}
}

20
src/common/user.cpp Normal file
View File

@@ -0,0 +1,20 @@
#include "pch.h"
#include "user.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;
}

9
src/common/user.h Normal file
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

@@ -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

@@ -55,4 +55,11 @@
<ProjectReference Include="..\..\common\ManagedCommon\ManagedCommon.csproj" />
<ProjectReference Include="..\..\common\ManagedTelemetry\Telemetry\Telemetry.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>

View File

@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class SndVideoConferenceSettings
{
[JsonPropertyName("Video Conference")]
public VideoConferenceSettings VideoConference { get; set; }
public SndVideoConferenceSettings(VideoConferenceSettings settings)
{
VideoConference = settings;
}
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -29,5 +30,15 @@ namespace Microsoft.PowerToys.Settings.UI.Library
{
return JsonSerializer.Serialize(this);
}
public static StringProperty ToStringProperty(string v)
{
return new StringProperty(v);
}
public static implicit operator StringProperty(string v)
{
return new StringProperty(v);
}
}
}

View File

@@ -0,0 +1,86 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class VideoConferenceConfigProperties
{
public VideoConferenceConfigProperties()
{
this.MuteCameraAndMicrophoneHotkey = new KeyboardKeysProperty(
new HotkeySettings()
{
Win = true,
Ctrl = false,
Alt = false,
Shift = false,
Key = "N",
Code = 78,
});
this.MuteMicrophoneHotkey = new KeyboardKeysProperty(
new HotkeySettings()
{
Win = true,
Ctrl = false,
Alt = false,
Shift = true,
Key = "A",
Code = 65,
});
this.MuteCameraHotkey = new KeyboardKeysProperty(
new HotkeySettings()
{
Win = true,
Ctrl = false,
Alt = false,
Shift = true,
Key = "O",
Code = 79,
});
this.HideToolbarWhenUnmuted = new BoolProperty(true);
}
[JsonPropertyName("mute_camera_and_microphone_hotkey")]
public KeyboardKeysProperty MuteCameraAndMicrophoneHotkey { get; set; }
[JsonPropertyName("mute_microphone_hotkey")]
public KeyboardKeysProperty MuteMicrophoneHotkey { get; set; }
[JsonPropertyName("mute_camera_hotkey")]
public KeyboardKeysProperty MuteCameraHotkey { get; set; }
[JsonPropertyName("selected_camera")]
public StringProperty SelectedCamera { get; set; } = string.Empty;
[JsonPropertyName("selected_mic")]
public StringProperty SelectedMicrophone { get; set; } = string.Empty;
[JsonPropertyName("toolbar_position")]
public StringProperty ToolbarPosition { get; set; } = "Top right corner";
[JsonPropertyName("toolbar_monitor")]
public StringProperty ToolbarMonitor { get; set; } = "Main monitor";
[JsonPropertyName("camera_overlay_image_path")]
public StringProperty CameraOverlayImagePath { get; set; } = string.Empty;
[JsonPropertyName("theme")]
public StringProperty Theme { get; set; }
[JsonPropertyName("hide_toolbar_when_unmuted")]
public BoolProperty HideToolbarWhenUnmuted { get; set; }
// converts the current to a json string.
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class VideoConferenceSettings : BasePTModuleSettings, ISettingsConfig
{
public VideoConferenceSettings()
{
Version = "1";
Name = "Video Conference";
Properties = new VideoConferenceConfigProperties();
}
[JsonPropertyName("properties")]
public VideoConferenceConfigProperties Properties { get; set; }
public string GetModuleName()
{
return Name;
}
public bool UpgradeSettingsConfiguration()
{
return false;
}
}
}

View File

@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Microsoft.PowerToys.Settings.UI.Library
{
public class VideoConferenceSettingsIPCMessage
{
[JsonPropertyName("powertoys")]
public SndVideoConferenceSettings Powertoys { get; set; }
public VideoConferenceSettingsIPCMessage()
{
}
public VideoConferenceSettingsIPCMessage(SndVideoConferenceSettings settings)
{
this.Powertoys = settings;
}
public string ToJsonString()
{
return JsonSerializer.Serialize(this);
}
}
}

View File

@@ -0,0 +1,424 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Helpers;
using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
namespace Microsoft.PowerToys.Settings.UI.ViewModels
{
public class VideoConferenceViewModel : Observable
{
private readonly ISettingsUtils _settingsUtils;
private VideoConferenceSettings Settings { get; set; }
private GeneralSettings GeneralSettingsConfig { get; set; }
private const string ModuleName = "Video Conference";
private Func<string, int> SendConfigMSG { get; }
private string _settingsConfigFileFolder = string.Empty;
public VideoConferenceViewModel(ISettingsUtils settingsUtils, ISettingsRepository<GeneralSettings> settingsRepository, Func<string, int> ipcMSGCallBackFunc, string configFileSubfolder = "")
{
if (settingsRepository == null)
{
throw new ArgumentNullException(nameof(settingsRepository));
}
GeneralSettingsConfig = settingsRepository.SettingsConfig;
_settingsUtils = settingsUtils ?? throw new ArgumentNullException(nameof(settingsUtils));
SendConfigMSG = ipcMSGCallBackFunc;
_settingsConfigFileFolder = configFileSubfolder;
try
{
Settings = _settingsUtils.GetSettings<VideoConferenceSettings>(GetSettingsSubPath());
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
Settings = new VideoConferenceSettings();
_settingsUtils.SaveSettings(Settings.ToJsonString(), GetSettingsSubPath());
}
CameraNames = interop.CommonManaged.GetAllVideoCaptureDeviceNames();
MicrophoneNames = interop.CommonManaged.GetAllActiveMicrophoneDeviceNames();
MicrophoneNames.Insert(0, "[All]");
var shouldSaveSettings = false;
if (string.IsNullOrEmpty(Settings.Properties.SelectedCamera.Value) && CameraNames.Count != 0)
{
_selectedCameraIndex = 0;
Settings.Properties.SelectedCamera.Value = CameraNames[0];
shouldSaveSettings = true;
}
else
{
_selectedCameraIndex = CameraNames.FindIndex(name => name == Settings.Properties.SelectedCamera.Value);
}
if (string.IsNullOrEmpty(Settings.Properties.SelectedMicrophone.Value))
{
_selectedMicrophoneIndex = 0;
Settings.Properties.SelectedMicrophone.Value = MicrophoneNames[0];
shouldSaveSettings = true;
}
else
{
_selectedMicrophoneIndex = MicrophoneNames.FindIndex(name => name == Settings.Properties.SelectedMicrophone.Value);
}
_isEnabled = GeneralSettingsConfig.Enabled.VideoConference;
_cameraAndMicrophoneMuteHotkey = Settings.Properties.MuteCameraAndMicrophoneHotkey.Value;
_mirophoneMuteHotkey = Settings.Properties.MuteMicrophoneHotkey.Value;
_cameraMuteHotkey = Settings.Properties.MuteCameraHotkey.Value;
CameraImageOverlayPath = Settings.Properties.CameraOverlayImagePath.Value;
SelectOverlayImage = new ButtonClickCommand(SelectOverlayImageAction);
ClearOverlayImage = new ButtonClickCommand(ClearOverlayImageAction);
_hideToolbarWhenUnmuted = Settings.Properties.HideToolbarWhenUnmuted.Value;
switch (Settings.Properties.ToolbarPosition.Value)
{
case "Top left corner":
_toolbarPositionIndex = 0;
break;
case "Top center":
_toolbarPositionIndex = 1;
break;
case "Top right corner":
_toolbarPositionIndex = 2;
break;
case "Bottom left corner":
_toolbarPositionIndex = 3;
break;
case "Bottom center":
_toolbarPositionIndex = 4;
break;
case "Bottom right corner":
_toolbarPositionIndex = 5;
break;
}
switch (Settings.Properties.ToolbarMonitor.Value)
{
case "Main monitor":
_toolbarMonitorIndex = 0;
break;
case "All monitors":
_toolbarMonitorIndex = 1;
break;
}
if (shouldSaveSettings)
{
_settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName);
}
}
private bool _isEnabled;
private int _toolbarPositionIndex;
private int _toolbarMonitorIndex;
private HotkeySettings _cameraAndMicrophoneMuteHotkey;
private HotkeySettings _mirophoneMuteHotkey;
private HotkeySettings _cameraMuteHotkey;
private int _selectedCameraIndex = -1;
private int _selectedMicrophoneIndex;
private bool _hideToolbarWhenUnmuted;
public List<string> CameraNames { get; }
public List<string> MicrophoneNames { get; }
public string CameraImageOverlayPath { get; set; }
public ButtonClickCommand SelectOverlayImage { get; set; }
public ButtonClickCommand ClearOverlayImage { get; set; }
private void ClearOverlayImageAction()
{
CameraImageOverlayPath = string.Empty;
Settings.Properties.CameraOverlayImagePath = string.Empty;
RaisePropertyChanged(nameof(CameraImageOverlayPath));
}
private void SelectOverlayImageAction()
{
try
{
string pickedImage = null;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Image Files (*.jpeg;*.jpg;*.png)|*.jpeg;*.jpg;*.png";
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
pickedImage = openFileDialog.FileName;
}
}
if (pickedImage != null)
{
CameraImageOverlayPath = pickedImage;
Settings.Properties.CameraOverlayImagePath = pickedImage;
RaisePropertyChanged(nameof(CameraImageOverlayPath));
}
}
#pragma warning disable CA1031 // Do not catch general exception types
catch
#pragma warning restore CA1031 // Do not catch general exception types
{
}
}
public int SelectedCameraIndex
{
get
{
return _selectedCameraIndex;
}
set
{
if (_selectedCameraIndex != value)
{
_selectedCameraIndex = value;
if (_selectedCameraIndex >= 0 && _selectedCameraIndex < CameraNames.Count)
{
Settings.Properties.SelectedCamera.Value = CameraNames[_selectedCameraIndex];
RaisePropertyChanged();
}
}
}
}
public int SelectedMicrophoneIndex
{
get
{
return _selectedMicrophoneIndex;
}
set
{
if (_selectedMicrophoneIndex != value)
{
_selectedMicrophoneIndex = value;
if (_selectedMicrophoneIndex >= 0 && _selectedMicrophoneIndex < MicrophoneNames.Count)
{
Settings.Properties.SelectedMicrophone.Value = MicrophoneNames[_selectedMicrophoneIndex];
RaisePropertyChanged();
}
}
}
}
public bool IsEnabled
{
get
{
return _isEnabled;
}
set
{
if (value != _isEnabled)
{
_isEnabled = value;
GeneralSettingsConfig.Enabled.VideoConference = value;
OutGoingGeneralSettings snd = new OutGoingGeneralSettings(GeneralSettingsConfig);
SendConfigMSG(snd.ToString());
OnPropertyChanged(nameof(IsEnabled));
}
}
}
public HotkeySettings CameraAndMicrophoneMuteHotkey
{
get
{
return _cameraAndMicrophoneMuteHotkey;
}
set
{
if (value != _cameraAndMicrophoneMuteHotkey)
{
_cameraAndMicrophoneMuteHotkey = value;
Settings.Properties.MuteCameraAndMicrophoneHotkey.Value = value;
RaisePropertyChanged(nameof(CameraAndMicrophoneMuteHotkey));
}
}
}
public HotkeySettings MicrophoneMuteHotkey
{
get
{
return _mirophoneMuteHotkey;
}
set
{
if (value != _mirophoneMuteHotkey)
{
_mirophoneMuteHotkey = value;
Settings.Properties.MuteMicrophoneHotkey.Value = value;
RaisePropertyChanged(nameof(MicrophoneMuteHotkey));
}
}
}
public HotkeySettings CameraMuteHotkey
{
get
{
return _cameraMuteHotkey;
}
set
{
if (value != _cameraMuteHotkey)
{
_cameraMuteHotkey = value;
Settings.Properties.MuteCameraHotkey.Value = value;
RaisePropertyChanged(nameof(CameraMuteHotkey));
}
}
}
public int ToolbarPostionIndex
{
get
{
return _toolbarPositionIndex;
}
set
{
if (_toolbarPositionIndex != value)
{
_toolbarPositionIndex = value;
switch (_toolbarPositionIndex)
{
case 0:
Settings.Properties.ToolbarPosition.Value = "Top left corner";
break;
case 1:
Settings.Properties.ToolbarPosition.Value = "Top center";
break;
case 2:
Settings.Properties.ToolbarPosition.Value = "Top right corner";
break;
case 3:
Settings.Properties.ToolbarPosition.Value = "Bottom left corner";
break;
case 4:
Settings.Properties.ToolbarPosition.Value = "Bottom center";
break;
case 5:
Settings.Properties.ToolbarPosition.Value = "Bottom right corner";
break;
}
RaisePropertyChanged(nameof(ToolbarPostionIndex));
}
}
}
public int ToolbarMonitorIndex
{
get
{
return _toolbarMonitorIndex;
}
set
{
if (_toolbarMonitorIndex != value)
{
_toolbarMonitorIndex = value;
switch (_toolbarMonitorIndex)
{
case 0:
Settings.Properties.ToolbarMonitor.Value = "Main monitor";
break;
case 1:
Settings.Properties.ToolbarMonitor.Value = "All monitors";
break;
}
RaisePropertyChanged(nameof(ToolbarMonitorIndex));
}
}
}
public bool HideToolbarWhenUnmuted
{
get
{
return _hideToolbarWhenUnmuted;
}
set
{
if (value != _hideToolbarWhenUnmuted)
{
_hideToolbarWhenUnmuted = value;
Settings.Properties.HideToolbarWhenUnmuted.Value = value;
RaisePropertyChanged(nameof(HideToolbarWhenUnmuted));
}
}
}
public string GetSettingsSubPath()
{
return _settingsConfigFileFolder + "\\" + ModuleName;
}
#pragma warning disable CA1030 // Use events where appropriate
public void RaisePropertyChanged([CallerMemberName] string propertyName = null)
#pragma warning restore CA1030 // Use events where appropriate
{
OnPropertyChanged(propertyName);
SndVideoConferenceSettings outsettings = new SndVideoConferenceSettings(Settings);
SndModuleSettings<SndVideoConferenceSettings> ipcMessage = new SndModuleSettings<SndVideoConferenceSettings>(outsettings);
SendConfigMSG(ipcMessage.ToJsonString());
}
}
[ComImport]
[Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -144,6 +144,9 @@
<Compile Include="Views\ShortcutGuidePage.xaml.cs">
<DependentUpon>ShortcutGuidePage.xaml</DependentUpon>
</Compile>
<Compile Include="Views\VideoConference.xaml.cs">
<DependentUpon>VideoConference.xaml</DependentUpon>
</Compile>
<Compile Include="Views\VisibleIfNotEmpty.cs" />
</ItemGroup>
<ItemGroup>
@@ -162,6 +165,7 @@
<Content Include="Assets\Modules\PowerRename.png" />
<Content Include="Assets\Modules\PT.png" />
<Content Include="Assets\Modules\ShortcutGuide.png" />
<Content Include="Assets\Modules\VideoConference.png" />
<Content Include="Assets\SplashScreen.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
@@ -283,6 +287,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Views\VideoConference.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\codeAnalysis\StyleCop.json">
@@ -306,12 +314,6 @@
<EnableTypeInfoReflection>false</EnableTypeInfoReflection>
<EnableXBindDiagnostics>false</EnableXBindDiagnostics>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|Win32'">
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|Win32'">
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@@ -117,6 +117,98 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Shell_VideoConference.Content" xml:space="preserve">
<value>Video Conference Mute</value>
<comment>Navigation view item name for Video Conference</comment>
</data>
<data name="VideoConference_Enable.Header" xml:space="preserve">
<value>Enable Video Conference</value>
</data>
<data name="VideoConference_Description.Text" xml:space="preserve">
<value>Video Conference Mute is a quick and easy way to do an global "mute" of both your microphone and webcam.
Disabling this module or closing PowerToys will unmute the microphone and camera.</value>
</data>
<data name="VideoConference_CameraAndMicrophoneMuteHotkeyControl_Header.Header" xml:space="preserve">
<value>Mute camera and microphone</value>
</data>
<data name="VideoConference_MicrophoneMuteHotkeyControl_Header.Header" xml:space="preserve">
<value>Mute microphone</value>
</data>
<data name="VideoConference_CameraMuteHotkeyControl_Header.Header" xml:space="preserve">
<value>Mute camera</value>
</data>
<data name="VideoConference_SelectedCamera.Header" xml:space="preserve">
<value>Selected camera</value>
</data>
<data name="VideoConference_SelectedMicrophone.Header" xml:space="preserve">
<value>Selected microphone</value>
</data>
<data name="VideoConference_CameraOverlayImagePathHeader.Text" xml:space="preserve">
<value>Camera overlay image</value>
</data>
<data name="VideoConference_ToolbarPosition.Header" xml:space="preserve">
<value>Toolbar postion</value>
</data>
<data name="VideoConference_ToolbarPosition_TopCenter.Content" xml:space="preserve">
<value>Top center</value>
</data>
<data name="VideoConference_ToolbarPosition_TopLeftCorner.Content" xml:space="preserve">
<value>Top left corner</value>
</data>
<data name="VideoConference_ToolbarPosition_TopRightCorner.Content" xml:space="preserve">
<value>Top right corner</value>
</data>
<data name="VideoConference_ToolbarPosition_BottomLeftCorner.Content" xml:space="preserve">
<value>Bottom left corner</value>
</data>
<data name="VideoConference_ToolbarPosition_BottomCenter.Content" xml:space="preserve">
<value>Bottom center</value>
</data>
<data name="VideoConference_ToolbarPosition_BottomRightCorner.Content" xml:space="preserve">
<value>Bottom right corner</value>
</data>
<data name="VideoConference_ToolbarMonitor.Header" xml:space="preserve">
<value>Show toolbar on</value>
</data>
<data name="VideoConference_ToolbarMonitor_Main.Content" xml:space="preserve">
<value>Main monitor</value>
</data>
<data name="VideoConference_ToolbarMonitor_UnderCursor.Content" xml:space="preserve">
<value>Monitor under cursor</value>
</data>
<data name="VideoConference_ToolbarMonitor_ActiveWindow.Content" xml:space="preserve">
<value>Active window monitor</value>
</data>
<data name="VideoConference_ToolbarMonitor_All.Content" xml:space="preserve">
<value>All monitors</value>
</data>
<data name="VideoConference_HideToolbarWhenUnmuted.Content" xml:space="preserve">
<value>Hide toolbar when both camera and microphone are unmuted</value>
</data>
<data name="About_VideoConference.Text" xml:space="preserve">
<value>About Video Conference</value>
</data>
<data name="VideoConference_Camera.Text" xml:space="preserve">
<value>Camera</value>
</data>
<data name="VideoConference_Microphone.Text" xml:space="preserve">
<value>Microphone</value>
</data>
<data name="VideoConference_Toolbar.Text" xml:space="preserve">
<value>Toolbar</value>
</data>
<data name="VideoConference_Shortcuts.Text" xml:space="preserve">
<value>Shortcuts</value>
</data>
<data name="VideoConference_CameraOverlayImageAlt.AutomationProperties.Name" xml:space="preserve">
<value>Camera overlay image preview</value>
</data>
<data name="VideoConference_CameraOverlayImageBrowse.Content" xml:space="preserve">
<value>Browse</value>
</data>
<data name="VideoConference_CameraOverlayImageClear.Content" xml:space="preserve">
<value>Clear</value>
</data>
<data name="Shell_General.Content" xml:space="preserve">
<value>General</value>
<comment>Navigation view item name for General</comment>
@@ -870,4 +962,4 @@
<data name="ColorPicker_ColorFormatsDescription.Text" xml:space="preserve">
<value>Editor color formats (Change the order by dragging)</value>
</data>
</root>
</root>

View File

@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
@@ -36,6 +37,14 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
set { Set(ref isBackEnabled, value); }
}
public bool IsVideoConferenceBuild
{
get
{
return this != null && File.Exists("modules/VideoConference/VideoConferenceModule.dll");
}
}
public WinUI.NavigationViewItem Selected
{
get { return selected; }

View File

@@ -82,6 +82,13 @@
<FontIcon Glyph="&#xEDA7;"/>
</winui:NavigationViewItem.Icon>
</winui:NavigationViewItem>
<winui:NavigationViewItem x:Uid="Shell_VideoConference" helpers:NavHelper.NavigateTo="views:VideoConferencePage" IsEnabled="{x:Bind ViewModel.IsVideoConferenceBuild, Mode=OneWay}">
<winui:NavigationViewItem.Icon>
<FontIcon Glyph="&#xE714;"/>
</winui:NavigationViewItem.Icon>
</winui:NavigationViewItem>
</winui:NavigationView.MenuItems>
<i:Interaction.Behaviors>
<behaviors:NavigationViewHeaderBehavior
@@ -110,4 +117,4 @@
</ScrollViewer>
</winui:NavigationView>
</Grid>
</UserControl>
</UserControl>

View File

@@ -0,0 +1,207 @@
<Page
x:Class="Microsoft.PowerToys.Settings.UI.Views.VideoConferencePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid RowSpacing="{StaticResource DefaultRowSpacing}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="LayoutVisualStates">
<VisualState x:Name="WideLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource WideLayoutMinWidth}" />
</VisualState.StateTriggers>
</VisualState>
<VisualState x:Name="SmallLayout">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="{StaticResource SmallLayoutMinWidth}" />
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="SidePanel.(Grid.Column)" Value="0"/>
<Setter Target="SidePanel.Width" Value="Auto"/>
<Setter Target="VideoConferenceView.(Grid.Row)" Value="1" />
<Setter Target="LinksPanel.(RelativePanel.RightOf)" Value="AboutImage"/>
<Setter Target="LinksPanel.(RelativePanel.AlignTopWith)" Value="AboutImage"/>
<Setter Target="AboutImage.Margin" Value="0,12,12,0"/>
<Setter Target="AboutTitle.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical" x:Name="VideoConferenceView">
<ToggleSwitch x:Uid="VideoConference_Enable"
IsOn="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<TextBlock x:Uid="VideoConference_Shortcuts"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<CustomControls:HotkeySettingsControl
x:Uid="VideoConference_CameraAndMicrophoneMuteHotkeyControl_Header"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
Keys="Win, Ctrl, Alt, Shift"
HotkeySettings="{x:Bind Path=ViewModel.CameraAndMicrophoneMuteHotkey, Mode=TwoWay}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<CustomControls:HotkeySettingsControl
x:Uid="VideoConference_MicrophoneMuteHotkeyControl_Header"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
Keys="Win, Ctrl, Alt, Shift"
HotkeySettings="{x:Bind Path=ViewModel.MicrophoneMuteHotkey, Mode=TwoWay}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<CustomControls:HotkeySettingsControl
x:Uid="VideoConference_CameraMuteHotkeyControl_Header"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
Keys="Win, Ctrl, Alt, Shift"
HotkeySettings="{x:Bind Path=ViewModel.CameraMuteHotkey, Mode=TwoWay}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
/>
<TextBlock x:Uid="VideoConference_Microphone"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<ComboBox x:Uid="VideoConference_SelectedMicrophone"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{Binding Path=SelectedMicrophoneIndex, Mode=TwoWay}"
ItemsSource="{Binding MicrophoneNames, Mode=OneTime}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"/>
<TextBlock x:Uid="VideoConference_Camera"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<ComboBox x:Uid="VideoConference_SelectedCamera"
Width="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{Binding Path=SelectedCameraIndex, Mode=TwoWay}"
ItemsSource="{Binding CameraNames, Mode=OneTime}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"/>
<TextBlock x:Uid="VideoConference_CameraOverlayImagePathHeader"
Margin="{StaticResource SmallTopMargin}"/>
<Border CornerRadius="4"
HorizontalAlignment="Left"
Margin="{StaticResource XXSmallTopMargin}">
<Image Width="240"
x:Uid="VideoConference_CameraOverlayImageAlt"
ToolTipService.ToolTip="{Binding Mode=OneWay, Path=CameraImageOverlayPath}"
Source="{Binding Mode=OneWay, Path=CameraImageOverlayPath}"/>
</Border>
<StackPanel Orientation="Horizontal"
Padding="0"
Spacing="8"
Margin="{StaticResource SmallTopMargin}">
<Button Height="32"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}"
x:Uid="VideoConference_CameraOverlayImageBrowse"
Command="{Binding Mode=OneWay, Path=SelectOverlayImage}"
HorizontalContentAlignment="Left"
HorizontalAlignment="Left" />
<Button IsEnabled="{Binding Mode=TwoWay, Path=IsEnabled}"
Height="32"
x:Uid="VideoConference_CameraOverlayImageClear"
Command="{Binding Mode=OneWay, Path=ClearOverlayImage}"
HorizontalAlignment="Left" />
</StackPanel>
<TextBlock x:Uid="VideoConference_Toolbar"
Style="{StaticResource SettingsGroupTitleStyle}"
Foreground="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled, Converter={StaticResource ModuleEnabledToForegroundConverter}}"/>
<ComboBox x:Uid="VideoConference_ToolbarPosition"
MinWidth="240"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{ Binding Mode=TwoWay, Path=ToolbarPostionIndex}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}">
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_TopLeftCorner"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_TopCenter"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_TopRightCorner"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_BottomLeftCorner"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_BottomCenter"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarPosition_BottomRightCorner"/>
</ComboBox>
<ComboBox x:Uid="VideoConference_ToolbarMonitor"
MinWidth="240"
Margin="{StaticResource SmallTopMargin}"
SelectedIndex="{ Binding Mode=TwoWay, Path=ToolbarMonitorIndex}"
IsEnabled="{ Binding Mode=TwoWay, Path=IsEnabled}">
<ComboBoxItem x:Uid="VideoConference_ToolbarMonitor_Main"/>
<ComboBoxItem x:Uid="VideoConference_ToolbarMonitor_All"/>
</ComboBox>
<CheckBox x:Uid="VideoConference_HideToolbarWhenUnmuted"
IsChecked="{x:Bind Mode=TwoWay, Path=ViewModel.HideToolbarWhenUnmuted}"
Margin="{StaticResource SmallTopMargin}"
IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.IsEnabled}"
/>
</StackPanel>
<RelativePanel x:Name="SidePanel"
HorizontalAlignment="Left"
Width="{StaticResource SidePanelWidth}"
Grid.Column="1">
<StackPanel x:Name="DescriptionPanel">
<TextBlock x:Uid="About_VideoConference"
x:Name="AboutTitle"
Grid.ColumnSpan="2"
Style="{StaticResource SettingsGroupTitleStyle}"
Margin="{StaticResource XSmallBottomMargin}"/>
<TextBlock x:Uid="VideoConference_Description"
TextWrapping="Wrap"
Grid.Row="1" />
</StackPanel>
<Border x:Name="AboutImage"
CornerRadius="4"
Grid.Row="2"
MaxWidth="240"
HorizontalAlignment="Left"
Margin="{StaticResource SmallTopBottomMargin}"
RelativePanel.Below="DescriptionPanel">
<Image Source="ms-appx:///Assets/Modules/VideoConference.png" />
</Border>
<StackPanel x:Name="LinksPanel"
Margin="0,1,0,0"
RelativePanel.Below="AboutImage"
Orientation="Vertical" >
<HyperlinkButton NavigateUri="https://aka.ms/PowerToysOverview_VideoConference">
<TextBlock x:Uid="Module_overview" />
</HyperlinkButton>
<HyperlinkButton NavigateUri="https://aka.ms/powerToysGiveFeedback">
<TextBlock x:Uid="Give_Feedback" />
</HyperlinkButton>
</StackPanel>
</RelativePanel>
</Grid>
</Page>

View File

@@ -0,0 +1,24 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.Library;
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
using Microsoft.PowerToys.Settings.UI.ViewModels;
using Windows.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Views
{
public sealed partial class VideoConferencePage : Page
{
private VideoConferenceViewModel ViewModel { get; set; }
public VideoConferencePage()
{
var settingsUtils = new SettingsUtils();
ViewModel = new VideoConferenceViewModel(settingsUtils, SettingsRepository<GeneralSettings>.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage);
DataContext = ViewModel;
InitializeComponent();
}
}
}

15
src/hasWDK.props Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Choose>
<When Condition="Exists('$(VCTargetsPath)\Microsoft.Cpp.WDK.props')">
<PropertyGroup>
<HasWDK>true</HasWDK>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<HasWDK>false</HasWDK>
</PropertyGroup>
</Otherwise>
</Choose>
</Project>

View File

@@ -91,6 +91,22 @@ namespace FancyZonesEditor
Overlay.Show();
}
public void App_KeyUp(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.LeftShift || e.Key == System.Windows.Input.Key.RightShift)
{
MainWindowSettings.IsShiftKeyPressed = false;
}
}
public void App_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == System.Windows.Input.Key.LeftShift || e.Key == System.Windows.Input.Key.RightShift)
{
MainWindowSettings.IsShiftKeyPressed = true;
}
}
public static void ShowExceptionMessageBox(string message, Exception exception = null)
{
string fullMessage = FancyZonesEditor.Properties.Resources.Error_Report + PowerToysIssuesURL + " \n" + message;

View File

@@ -19,6 +19,7 @@ namespace FancyZonesEditor
InitializeComponent();
KeyUp += GridEditorWindow_KeyUp;
KeyDown += ((App)Application.Current).App_KeyDown;
_stashedModel = (GridLayoutModel)(App.Overlay.CurrentDataContext as GridLayoutModel).Clone();
}
@@ -36,6 +37,8 @@ namespace FancyZonesEditor
{
OnCancel(sender, null);
}
((App)Application.Current).App_KeyUp(sender, e);
}
private GridLayoutModel _stashedModel;

View File

@@ -5,6 +5,7 @@
using System;
using System.Reflection;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using FancyZonesEditor.Utils;
@@ -32,6 +33,9 @@ namespace FancyZonesEditor.Models
Window.Background = (Brush)properties[milliseconds % properties.Length].GetValue(null, null);
}
Window.KeyUp += ((App)Application.Current).App_KeyUp;
Window.KeyDown += ((App)Application.Current).App_KeyDown;
Window.Left = workArea.X;
Window.Top = workArea.Y;
Window.Width = workArea.Width;

View File

@@ -4,4 +4,5 @@
#include <winrt/Windows.Foundation.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <Shlwapi.h>
#include <ProjectTelemetry.h>
#include <ProjectTelemetry.h>
#include <functional>

View File

@@ -1,4 +1,4 @@
#include <pch.h>
#include "pch.h"
#include "registry_wrapper.h"
namespace PowerPreviewSettings

View File

@@ -1,5 +1,4 @@
#pragma once
#include <pch.h>
#include <string>
#include "Generated Files/resource.h"
#include <settings_objects.h>

View File

@@ -0,0 +1,220 @@
#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 "ImageLoader.h"
#include "Logging.h"
IWICImagingFactory* _GetWIC() noexcept
{
static IWICImagingFactory* s_Factory = nullptr;
if (s_Factory)
return s_Factory;
HRESULT hr = CoCreateInstance(
CLSID_WICImagingFactory,
nullptr,
CLSCTX_INPROC_SERVER,
__uuidof(IWICImagingFactory),
(LPVOID*)&s_Factory);
if (FAILED(hr))
{
LogToFile(std::string("_GetWIC() failed with code: " + hr));
s_Factory = nullptr;
return nullptr;
}
return s_Factory;
}
using Microsoft::WRL::ComPtr;
ComPtr<IMFSample> LoadImageAsSample(ComPtr<IStream> imageStream, IMFMediaType* sampleMediaType) noexcept
{
HRESULT hr = S_OK;
// Get target sample frame dimensions
UINT targetWidth = 0;
UINT targetHeight = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFGetAttributeSize(sampleMediaType, MF_MT_FRAME_SIZE, &targetWidth, &targetHeight));
IWICImagingFactory* pWIC = _GetWIC();
if (!pWIC)
{
LogToFile("Failed to create IWICImagingFactory");
return nullptr;
}
// Initialize image bitmap decoder from filename and get the image frame
ComPtr<IWICBitmapDecoder> bitmapDecoder;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(pWIC->CreateDecoderFromStream(imageStream.Get(), nullptr, WICDecodeMetadataCacheOnLoad, &bitmapDecoder));
ComPtr<IWICBitmapFrameDecode> decodedFrame;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(bitmapDecoder->GetFrame(0, &decodedFrame));
UINT imageWidth = 0, imageHeight = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(decodedFrame->GetSize(&imageWidth, &imageHeight));
// Scale the image if required
ComPtr<IWICBitmapSource> sourceImageFrame;
if (targetWidth != imageWidth || targetHeight != imageHeight)
{
ComPtr<IWICBitmapScaler> scaler;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(pWIC->CreateBitmapScaler(&scaler));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(scaler->Initialize(decodedFrame.Get(), targetWidth, targetHeight, WICBitmapInterpolationModeHighQualityCubic));
sourceImageFrame.Attach(scaler.Detach());
}
else
{
sourceImageFrame.Attach(decodedFrame.Detach());
}
MFT_REGISTER_TYPE_INFO outputFilter = { MFMediaType_Video, {} };
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(sampleMediaType->GetGUID(MF_MT_SUBTYPE, &outputFilter.guidSubtype));
ComPtr<IWICBitmapEncoder> jpgEncoder;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(pWIC->CreateEncoder(
outputFilter.guidSubtype == MFVideoFormat_RGB24 ? GUID_ContainerFormatBmp : GUID_ContainerFormatJpeg, nullptr, &jpgEncoder));
// Prepare the encoder output memory stream and encoding params
ComPtr<IStream> jpgStream;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(CreateStreamOnHGlobal(nullptr, true, &jpgStream));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgEncoder->Initialize(jpgStream.Get(), WICBitmapEncoderNoCache));
ComPtr<IWICBitmapFrameEncode> jpgFrame;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgEncoder->CreateNewFrame(&jpgFrame, nullptr));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->Initialize(nullptr));
WICPixelFormatGUID jpgFormat = GUID_WICPixelFormat24bppBGR;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->SetPixelFormat(&jpgFormat));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->SetSize(targetWidth, targetHeight));
// Commit the image encoding
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->WriteSource(sourceImageFrame.Get(), nullptr));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrame->Commit());
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgEncoder->Commit());
// Obtain stream size and lock its memory pointer
STATSTG jpgStreamStat{};
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgStream->Stat(&jpgStreamStat, STATFLAG_NONAME));
const size_t jpgStreamSize = jpgStreamStat.cbSize.QuadPart;
HGLOBAL streamMemoryHandle{};
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(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
ComPtr<IMFSample> inputSample;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateSample(&inputSample));
IMFMediaBuffer* inputMediaBuffer = nullptr;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateAlignedMemoryBuffer(static_cast<DWORD>(jpgStreamSize), MF_64_BYTE_ALIGNMENT, &inputMediaBuffer));
BYTE* inputBuf = nullptr;
DWORD max_length = 0, current_length = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputMediaBuffer->Lock(&inputBuf, &max_length, &current_length));
if (max_length < jpgStreamSize)
{
LogToFile("max_length < jpgStreamSize");
return nullptr;
}
std::copy(jpgStreamMemory, jpgStreamMemory + jpgStreamSize, inputBuf);
unlockJpgStreamMemory.reset();
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputMediaBuffer->Unlock());
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputMediaBuffer->SetCurrentLength(static_cast<DWORD>(jpgStreamSize)));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(inputSample->AddBuffer(inputMediaBuffer));
// Now we are ready to convert it to the requested media type, so we need to find a suitable jpg encoder
MFT_REGISTER_TYPE_INFO inputFilter = { MFMediaType_Video, outputFilter.guidSubtype == MFVideoFormat_RGB24 ? MFVideoFormat_RGB24 : MFVideoFormat_MJPG };
// But if no conversion is needed, just return the input sample
if (!memcmp(&inputFilter.guidSubtype, &outputFilter.guidSubtype, sizeof(GUID)))
{
return inputSample;
}
IMFActivate** ppVDActivate = nullptr;
UINT32 count = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFTEnumEx(MFT_CATEGORY_VIDEO_DECODER, MFT_ENUM_FLAG_SYNCMFT, &inputFilter, &outputFilter, &ppVDActivate, &count));
ComPtr<IMFTransform> videoDecoder;
bool videoDecoderActivated = false;
for (UINT32 i = 0; i < count; ++i)
{
if (!videoDecoderActivated && !FAILED(ppVDActivate[i]->ActivateObject(IID_PPV_ARGS(&videoDecoder))))
{
videoDecoderActivated = true;
}
ppVDActivate[i]->Release();
}
if (count)
{
CoTaskMemFree(ppVDActivate);
}
if (!videoDecoderActivated)
{
LogToFile("No converter avialable for the selected format");
return nullptr;
}
auto shutdownVideoDecoder = wil::scope_exit([&videoDecoder] { MFShutdownObject(videoDecoder.Get()); });
// Set input/output types for the decoder
ComPtr<IMFMediaType> jpgFrameMediaType;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateMediaType(&jpgFrameMediaType));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_MJPG));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(jpgFrameMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFSetAttributeSize(jpgFrameMediaType.Get(), MF_MT_FRAME_SIZE, targetWidth, targetHeight));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFSetAttributeRatio(jpgFrameMediaType.Get(), MF_MT_PIXEL_ASPECT_RATIO, 1, 1));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->SetInputType(0, jpgFrameMediaType.Get(), 0));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->SetOutputType(0, sampleMediaType, 0));
// Process the input sample
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->ProcessInput(0, inputSample.Get(), 0));
// Check whether we need to allocate output sample and buffer ourselves
MFT_OUTPUT_STREAM_INFO outputStreamInfo{};
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->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)
{
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateSample(&outputSample));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(outputSample->SetSampleDuration(333333));
IMFMediaBuffer* outputMediaBuffer = nullptr;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(MFCreateAlignedMemoryBuffer(outputStreamInfo.cbSize, outputStreamInfo.cbAlignment - 1, &outputMediaBuffer));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(outputMediaBuffer->SetCurrentLength(outputStreamInfo.cbSize));
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(outputSample->AddBuffer(outputMediaBuffer));
outputSamples.pSample = outputSample;
}
// Finally, produce the output sample
DWORD processStatus = 0;
RETURN_NULLPTR_IF_FAILED_WITH_LOGGING(videoDecoder->ProcessOutput(0, 1, &outputSamples, &processStatus));
if (outputSamples.pEvents)
{
outputSamples.pEvents->Release();
}
return outputSamples.pSample;
}

View File

@@ -0,0 +1,14 @@
#pragma once
#include <memory>
#include <optional>
#include <wrl/client.h>
#include <Mfidl.h>
#pragma warning(push)
#pragma warning(disable : 4005)
#include <stdint.h>
#pragma warning(pop)
Microsoft::WRL::ComPtr<IMFSample> LoadImageAsSample(Microsoft::WRL::ComPtr<IStream> imageStream, IMFMediaType* outputSampleMediaType) noexcept;

View File

@@ -0,0 +1,524 @@
#include "stdafx.h"
#include "SimpleMediaSource.h"
#include "SimpleMediaStream.h"
#include "Logging.h"
HRESULT
SimpleMediaSource::RuntimeClassInitialize()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
RETURN_IF_FAILED_WITH_LOGGING(MFCreateAttributes(&_spAttributes, 10));
RETURN_IF_FAILED_WITH_LOGGING(MFCreateEventQueue(&_spEventQueue));
RETURN_IF_FAILED_WITH_LOGGING(MakeAndInitialize<SimpleMediaStream>(&_stream, this));
{
ComPtr<IMFStreamDescriptor> streamDescriptor(_stream.Get()->_spStreamDesc.Get());
RETURN_IF_FAILED_WITH_LOGGING(MFCreatePresentationDescriptor(1, streamDescriptor.GetAddressOf(), &_spPresentationDescriptor));
}
_wasStreamPreviouslySelected = false;
_sourceState = SourceState::Stopped;
return hr;
}
// IMFMediaEventGenerator methods.
IFACEMETHODIMP
SimpleMediaSource::BeginGetEvent(
_In_ IMFAsyncCallback* pCallback,
_In_ IUnknown* punkState)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->BeginGetEvent(pCallback, punkState));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::EndGetEvent(
_In_ IMFAsyncResult* pResult,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->EndGetEvent(pResult, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::GetEvent(
DWORD dwFlags,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
// NOTE:
// GetEvent can block indefinitely, so we don't hold the lock.
// This requires some juggling with the event queue pointer.
HRESULT hr = S_OK;
ComPtr<IMFMediaEventQueue> spQueue;
{
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
spQueue = _spEventQueue;
}
// Now get the event.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->GetEvent(dwFlags, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::QueueEvent(
MediaEventType eventType,
REFGUID guidExtendedType,
HRESULT hrStatus,
_In_opt_ PROPVARIANT const* pvValue)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(eventType, guidExtendedType, hrStatus, pvValue));
return hr;
}
// IMFMediaSource methods
IFACEMETHODIMP
SimpleMediaSource::CreatePresentationDescriptor(
_COM_Outptr_ IMFPresentationDescriptor** ppPresentationDescriptor)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppPresentationDescriptor == nullptr)
{
return E_POINTER;
}
*ppPresentationDescriptor = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spPresentationDescriptor->Clone(ppPresentationDescriptor));
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::GetCharacteristics(
_Out_ DWORD* pdwCharacteristics)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (nullptr == pdwCharacteristics)
{
return E_POINTER;
}
*pdwCharacteristics = 0;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*pdwCharacteristics = MFMEDIASOURCE_IS_LIVE;
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::Pause()
{
// Pause() not required/needed
return MF_E_INVALID_STATE_TRANSITION;
}
IFACEMETHODIMP
SimpleMediaSource::Shutdown()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
_sourceState = SourceState::Shutdown;
_spAttributes.Reset();
_spPresentationDescriptor.Reset();
if (_spEventQueue != nullptr)
{
_spEventQueue->Shutdown();
_spEventQueue.Reset();
}
if (_stream != nullptr)
{
_stream.Get()->Shutdown();
_stream.Reset();
}
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::Start(
_In_ IMFPresentationDescriptor* pPresentationDescriptor,
_In_opt_ const GUID* pguidTimeFormat,
_In_ const PROPVARIANT* pvarStartPos)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
DWORD count = 0;
PROPVARIANT startTime;
BOOL selected = false;
ComPtr<IMFStreamDescriptor> streamDesc;
DWORD streamIndex = 0;
if (pPresentationDescriptor == nullptr || pvarStartPos == nullptr)
{
return E_INVALIDARG;
}
else if (pguidTimeFormat != nullptr && *pguidTimeFormat != GUID_NULL)
{
return MF_E_UNSUPPORTED_TIME_FORMAT;
}
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (!(_sourceState != SourceState::Stopped || _sourceState != SourceState::Shutdown))
{
return MF_E_INVALID_STATE_TRANSITION;
}
_sourceState = SourceState::Started;
// This checks the passed in PresentationDescriptor matches the member of streams we
// have defined internally and that at least one stream is selected
RETURN_IF_FAILED_WITH_LOGGING(_ValidatePresentationDescriptor(pPresentationDescriptor));
RETURN_IF_FAILED_WITH_LOGGING(pPresentationDescriptor->GetStreamDescriptorCount(&count));
RETURN_IF_FAILED_WITH_LOGGING(InitPropVariantFromInt64(MFGetSystemTime(), &startTime));
// Send event that the source started. Include error code in case it failed.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(MESourceStarted,
GUID_NULL,
hr,
&startTime));
// We're hardcoding this to the first descriptor
// since this sample is a single stream sample. For
// multiple streams, we need to walk the list of streams
// and for each selected stream, send the MEUpdatedStream
// or MENewStream event along with the MEStreamStarted
// event.
RETURN_IF_FAILED_WITH_LOGGING(pPresentationDescriptor->GetStreamDescriptorByIndex(0,
&selected,
&streamDesc));
RETURN_IF_FAILED_WITH_LOGGING(streamDesc->GetStreamIdentifier(&streamIndex));
if (streamIndex >= 1)
{
return MF_E_INVALIDSTREAMNUMBER;
}
if (selected)
{
ComPtr<IUnknown> spunkStream;
MediaEventType met = (_wasStreamPreviouslySelected ? MEUpdatedStream : MENewStream);
// Update our internal PresentationDescriptor
RETURN_IF_FAILED_WITH_LOGGING(_spPresentationDescriptor->SelectStream(streamIndex));
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->SetStreamState(MF_STREAM_STATE_RUNNING));
RETURN_IF_FAILED_WITH_LOGGING(_stream.As(&spunkStream));
// Send the MEUpdatedStream/MENewStream to our source event
// queue.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamUnk(met,
GUID_NULL,
S_OK,
spunkStream.Get()));
// But for our stream started (MEStreamStarted), we post to our
// stream event queue.
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->QueueEvent(MEStreamStarted,
GUID_NULL,
S_OK,
&startTime));
}
_wasStreamPreviouslySelected = selected;
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::Stop()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
PROPVARIANT stopTime;
DWORD count = 0;
MF_STREAM_STATE state;
if (_sourceState != SourceState::Started)
{
return MF_E_INVALID_STATE_TRANSITION;
}
_sourceState = SourceState::Stopped;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(InitPropVariantFromInt64(MFGetSystemTime(), &stopTime));
RETURN_IF_FAILED_WITH_LOGGING(_spPresentationDescriptor->GetStreamDescriptorCount(&count));
// Deselect the streams and send the stream stopped events.
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->GetStreamState(&state));
_wasStreamPreviouslySelected = (state == MF_STREAM_STATE_RUNNING);
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->SetStreamState(MF_STREAM_STATE_STOPPED));
_spPresentationDescriptor->DeselectStream(0);
RETURN_IF_FAILED_WITH_LOGGING(_stream.Get()->QueueEvent(MEStreamStopped, GUID_NULL, hr, &stopTime));
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(MESourceStopped, GUID_NULL, hr, &stopTime));
return hr;
}
// IMFMediaSourceEx
IFACEMETHODIMP
SimpleMediaSource::GetSourceAttributes(
_COM_Outptr_ IMFAttributes** sourceAttributes)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (nullptr == sourceAttributes)
{
return E_POINTER;
}
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*sourceAttributes = nullptr;
if (_spAttributes.Get() == nullptr)
{
ComPtr<IMFSensorProfileCollection> profileCollection;
ComPtr<IMFSensorProfile> profile;
// Create our source attribute store.
RETURN_IF_FAILED_WITH_LOGGING(MFCreateAttributes(_spAttributes.GetAddressOf(), 1));
// Create an empty profile collection...
RETURN_IF_FAILED_WITH_LOGGING(MFCreateSensorProfileCollection(&profileCollection));
// In this example since we have just one stream, we only have one
// pin to add: Pin0.
// Legacy profile is mandatory. This is to ensure non-profile
// aware applications can still function, but with degraded
// feature sets.
RETURN_IF_FAILED_WITH_LOGGING(MFCreateSensorProfile(KSCAMERAPROFILE_Legacy, 0, nullptr, profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED_WITH_LOGGING(profile->AddProfileFilter(0, L"((RES==;FRT<=30,1;SUT==))"));
RETURN_IF_FAILED_WITH_LOGGING(profileCollection->AddProfile(profile.Get()));
// High Frame Rate profile will only allow >=60fps.
RETURN_IF_FAILED_WITH_LOGGING(MFCreateSensorProfile(KSCAMERAPROFILE_HighFrameRate, 0, nullptr, profile.ReleaseAndGetAddressOf()));
RETURN_IF_FAILED_WITH_LOGGING(profile->AddProfileFilter(0, L"((RES==;FRT>=60,1;SUT==))"));
RETURN_IF_FAILED_WITH_LOGGING(profileCollection->AddProfile(profile.Get()));
// Se the profile collection to the attribute store of the IMFTransform.
RETURN_IF_FAILED_WITH_LOGGING(_spAttributes->SetUnknown(MF_DEVICEMFT_SENSORPROFILE_COLLECTION,
profileCollection.Get()));
}
return _spAttributes.CopyTo(sourceAttributes);
}
IFACEMETHODIMP
SimpleMediaSource::GetStreamAttributes(
DWORD dwStreamIdentifier,
_COM_Outptr_ IMFAttributes** ppAttributes)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppAttributes == nullptr)
{
return E_POINTER;
}
*ppAttributes = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (dwStreamIdentifier >= 1)
{
return MF_E_INVALIDSTREAMNUMBER;
}
else
{
*ppAttributes = _stream.Get()->_spAttributes.Get();
(*ppAttributes)->AddRef();
}
return hr;
}
IFACEMETHODIMP
SimpleMediaSource::SetD3DManager(
_In_opt_ IUnknown* /*pManager*/
)
{
// Return code is ignored by the frame work, this is a
// best effort attempt to inform the media source of the
// DXGI manager to use if DX surface support is available.
return E_NOTIMPL;
}
struct __declspec(uuid("a1f58958-a5aa-412f-af20-1b7f1242dba0")) IMFDeviceController;
// IMFGetService methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::GetService(
_In_ REFGUID,
_In_ REFIID iid,
_Out_ LPVOID* ppvObject)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
const bool wantsIMFDeviceController = IsEqualIID(iid, __uuidof(IMFDeviceController));
auto stream = _stream.Get();
const bool goingToRestart = wantsIMFDeviceController && _wasStreamPreviouslySelected && stream && stream->_isSelected && _sourceState == SourceState::Started;
if (goingToRestart)
{
// GetService w /IMFDeviceController is called when we're already started -> stopping to prepare for restart
stream->SetStreamState(MF_STREAM_STATE_STOPPED);
return E_POINTER;
}
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (!ppvObject)
{
return E_POINTER;
}
*ppvObject = nullptr;
// We have no supported service, just return
// MF_E_UNSUPPORTED_SERVICE for all calls.
return MF_E_UNSUPPORTED_SERVICE;
}
// IKsControl methods
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsProperty(
_In_reads_bytes_(ulPropertyLength) PKSPROPERTY,
_In_ ULONG,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID,
_In_ ULONG,
_Out_ ULONG*)
{
// ERROR_SET_NOT_FOUND is the standard error code returned
// by the AV Stream driver framework when a miniport
// driver does not register a handler for a KS operation.
// We want to mimic the driver behavior here if we don't
// support controls.
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsMethod(
_In_reads_bytes_(ulMethodLength) PKSMETHOD,
_In_ ULONG,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID,
_In_ ULONG,
_Out_ ULONG*)
{
Shutdown();
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
_Use_decl_annotations_
IFACEMETHODIMP
SimpleMediaSource::KsEvent(
_In_reads_bytes_opt_(ulEventLength) PKSEVENT,
_In_ ULONG,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID,
_In_ ULONG,
_Out_opt_ ULONG*)
{
return HRESULT_FROM_WIN32(ERROR_SET_NOT_FOUND);
}
/// Internal methods.
HRESULT
SimpleMediaSource::_CheckShutdownRequiresLock()
{
if (_sourceState == SourceState::Shutdown)
{
return MF_E_SHUTDOWN;
}
if (_spEventQueue == nullptr || _stream == nullptr)
{
return E_UNEXPECTED;
}
return S_OK;
}
HRESULT
SimpleMediaSource::_ValidatePresentationDescriptor(
_In_ IMFPresentationDescriptor* pPD)
{
HRESULT hr = S_OK;
DWORD cStreams = 0;
bool anySelected = false;
if (pPD == nullptr)
{
return E_INVALIDARG;
}
// The caller's PD must have the same number of streams as ours.
RETURN_IF_FAILED_WITH_LOGGING(pPD->GetStreamDescriptorCount(&cStreams));
if (SUCCEEDED(hr) && (cStreams != 1))
{
return E_INVALIDARG;
}
// The caller must select at least one stream.
for (UINT32 i = 0; i < cStreams; ++i)
{
ComPtr<IMFStreamDescriptor> spSD;
BOOL fSelected = FALSE;
DWORD dwId = 0;
RETURN_IF_FAILED_WITH_LOGGING(pPD->GetStreamDescriptorByIndex(i, &fSelected, &spSD));
anySelected |= !!fSelected;
RETURN_IF_FAILED_WITH_LOGGING(spSD->GetStreamIdentifier(&dwId));
if (dwId >= 1)
{
return E_INVALIDARG;
}
}
if (!anySelected)
{
return E_INVALIDARG;
}
return hr;
}

View File

@@ -0,0 +1,92 @@
#pragma once
#include "stdafx.h"
class SimpleMediaStream;
class __declspec(uuid("{8a6954dc-7baa-486b-a7a3-a3cc09246487}"))
SimpleMediaSource : public RuntimeClass<RuntimeClassFlags<WinRtClassicComMix>, IMFMediaEventGenerator, IMFMediaSource, IMFMediaSourceEx, IMFGetService, IKsControl>
{
enum class SourceState
{
Invalid,
Stopped,
Started,
Shutdown
};
public:
// IMFMediaEventGenerator
IFACEMETHOD(BeginGetEvent)
(_In_ IMFAsyncCallback* pCallback, _In_ IUnknown* punkState);
IFACEMETHOD(EndGetEvent)
(_In_ IMFAsyncResult* pResult, _Out_ IMFMediaEvent** ppEvent);
IFACEMETHOD(GetEvent)
(DWORD dwFlags, _Out_ IMFMediaEvent** ppEvent);
IFACEMETHOD(QueueEvent)
(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, _In_ const PROPVARIANT* pvValue);
// IMFMediaSource
IFACEMETHOD(CreatePresentationDescriptor)
(_Out_ IMFPresentationDescriptor** ppPresentationDescriptor);
IFACEMETHOD(GetCharacteristics)
(_Out_ DWORD* pdwCharacteristics);
IFACEMETHOD(Pause)
();
IFACEMETHOD(Shutdown)
();
IFACEMETHOD(Start)
(_In_ IMFPresentationDescriptor* pPresentationDescriptor, _In_ const GUID* pguidTimeFormat, _In_ const PROPVARIANT* pvarStartPosition);
IFACEMETHOD(Stop)
();
// IMFMediaSourceEx
IFACEMETHOD(GetSourceAttributes)
(_COM_Outptr_ IMFAttributes** ppAttributes);
IFACEMETHOD(GetStreamAttributes)
(DWORD dwStreamIdentifier, _COM_Outptr_ IMFAttributes** ppAttributes);
IFACEMETHOD(SetD3DManager)
(_In_opt_ IUnknown* pManager);
// IMFGetService
IFACEMETHOD(GetService)
(_In_ REFGUID guidService, _In_ REFIID riid, _Out_ LPVOID* ppvObject);
// IKsControl
IFACEMETHOD(KsProperty)
(_In_reads_bytes_(ulPropertyLength) PKSPROPERTY pProperty,
_In_ ULONG ulPropertyLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pPropertyData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned);
IFACEMETHOD(KsMethod)
(_In_reads_bytes_(ulMethodLength) PKSMETHOD pMethod,
_In_ ULONG ulMethodLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pMethodData,
_In_ ULONG ulDataLength,
_Out_ ULONG* pBytesReturned);
IFACEMETHOD(KsEvent)
(_In_reads_bytes_opt_(ulEventLength) PKSEVENT pEvent,
_In_ ULONG ulEventLength,
_Inout_updates_to_(ulDataLength, *pBytesReturned) LPVOID pEventData,
_In_ ULONG ulDataLength,
_Out_opt_ ULONG* pBytesReturned);
public:
HRESULT RuntimeClassInitialize();
private:
HRESULT _CheckShutdownRequiresLock();
HRESULT _ValidatePresentationDescriptor(_In_ IMFPresentationDescriptor* pPresentationDescriptor);
CriticalSection _critSec;
SourceState _sourceState{ SourceState::Invalid };
ComPtr<IMFMediaEventQueue> _spEventQueue;
ComPtr<IMFPresentationDescriptor> _spPresentationDescriptor;
ComPtr<IMFAttributes> _spAttributes;
bool _wasStreamPreviouslySelected; // maybe makes more sense as a property of the stream
ComPtr<SimpleMediaStream> _stream;
};
CoCreatableClass(SimpleMediaSource);

View File

@@ -0,0 +1,670 @@
#include "stdafx.h"
#include "ImageLoader.h"
#include <vector>
#include <algorithm>
#include <thread>
#include "SimpleMediaSource.h"
#include "SimpleMediaStream.h"
#include <common\user.h>
#include "Logging.h"
HRESULT CopyAttribute(IMFAttributes* pSrc, IMFAttributes* pDest, const GUID& key);
const static std::wstring_view MODULE_NAME = L"Video Conference";
const static std::wstring_view VIRTUAL_CAMERA_NAME = L"PowerToys VideoConference";
namespace
{
constexpr std::array<unsigned char, 3> blackColor = { 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, blackColor[0], blackColor[1], blackColor[2], 0x00
};
// clang-format on
}
//-------------------------------------------------------------------
// CopyAttribute
//
// Copy an attribute value from one attribute store to another.
//-------------------------------------------------------------------
HRESULT CopyAttribute(IMFAttributes* pSrc, IMFAttributes* pDest, const GUID& key)
{
PROPVARIANT var;
PropVariantInit(&var);
HRESULT hr = S_OK;
hr = pSrc->GetItem(key, &var);
if (SUCCEEDED(hr))
{
hr = pDest->SetItem(key, var);
}
PropVariantClear(&var);
return hr;
}
bool areSame(double lhs, double rhs)
{
const double EPSILON = 0.00000001;
return (fabs(lhs - rhs) < EPSILON);
}
ComPtr<IMFMediaType> SelectBestMediaType(IMFSourceReader* reader)
{
VERBOSE_LOG;
std::vector<ComPtr<IMFMediaType>> supportedMediaTypes;
auto typeFramerate = [](IMFMediaType* type) {
UINT32 framerateNum = 0, framerateDenum = 1;
MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &framerateNum, &framerateDenum);
const float framerate = static_cast<float>(framerateNum) / framerateDenum;
return framerate;
};
bool is16by9RatioAvailable = false;
for (DWORD tyIdx = 0;; ++tyIdx)
{
IMFMediaType* nextType = nullptr;
HRESULT hr = reader->GetNativeMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, tyIdx, &nextType);
if (!nextType)
{
break;
}
UINT32 width = 0;
UINT32 height = 0;
MFGetAttributeSize(nextType, MF_MT_FRAME_SIZE, &width, &height);
double aspectRatio = static_cast<double>(width) / height;
GUID subtype{};
nextType->GetGUID(MF_MT_SUBTYPE, &subtype);
//LogToFile(std::string("Available format: ") +
// toMediaTypeString(subtype) +
// std::string(", width=") +
// std::to_string(width) +
// std::string(", height=") +
// std::to_string(height) +
// std::string(", aspect ratio=") +
// std::to_string(aspectRatio));
if (subtype != MFVideoFormat_YUY2 &&
subtype != MFVideoFormat_RGB24 &&
subtype != MFVideoFormat_MJPG &&
subtype != MFVideoFormat_NV12)
{
continue;
}
if (areSame(aspectRatio, 16. / 9.))
{
is16by9RatioAvailable = true;
}
constexpr float minimalAcceptableFramerate = 15.f;
// Skip low frame types
if (typeFramerate(nextType) < minimalAcceptableFramerate)
{
continue;
}
supportedMediaTypes.emplace_back(nextType);
if (hr == MF_E_NO_MORE_TYPES || FAILED(hr))
{
break;
}
}
if (is16by9RatioAvailable)
{
// Remove all types with non 16 by 9 ratio
supportedMediaTypes.erase(std::remove_if(begin(supportedMediaTypes), end(supportedMediaTypes), [](ComPtr<IMFMediaType>& ptr) {
UINT32 width = 0, height = 0;
MFGetAttributeSize(ptr.Get(), MF_MT_FRAME_SIZE, &width, &height);
double ratio = static_cast<double>(width) / height;
return !areSame(ratio, 16. / 9.);
}),
end(supportedMediaTypes));
}
UINT64 maxResolution = 0;
for (auto& type : supportedMediaTypes)
{
UINT32 width = 0;
UINT32 height = 0;
MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE, &width, &height);
const UINT64 curResolutionMult = static_cast<UINT64>(width) * height;
if (curResolutionMult >= maxResolution)
{
maxResolution = curResolutionMult;
}
}
// Remove all types with non-optimal resolution
supportedMediaTypes.erase(std::remove_if(begin(supportedMediaTypes), end(supportedMediaTypes), [maxResolution](ComPtr<IMFMediaType>& ptr) {
UINT32 w = 0, h = 0;
MFGetAttributeSize(ptr.Get(), MF_MT_FRAME_SIZE, &w, &h);
const UINT64 curResolutionMult = static_cast<UINT64>(w) * h;
return curResolutionMult != maxResolution;
}),
end(supportedMediaTypes));
// Desc-sort by frame_rate
std::sort(begin(supportedMediaTypes), end(supportedMediaTypes), [typeFramerate](ComPtr<IMFMediaType>& lhs, ComPtr<IMFMediaType>& rhs) {
return typeFramerate(lhs.Get()) > typeFramerate(rhs.Get());
});
return std::move(supportedMediaTypes[0]);
}
HRESULT
SimpleMediaStream::RuntimeClassInitialize(
_In_ SimpleMediaSource* pSource)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
if (nullptr == pSource)
{
return E_INVALIDARG;
}
RETURN_IF_FAILED_WITH_LOGGING(pSource->QueryInterface(IID_PPV_ARGS(&_parent)));
const auto newSettings = SyncCurrentSettings();
UpdateSourceCamera(newSettings.newCameraName);
ComPtr<IStream> blackBMPImage = SHCreateMemStream(bmpPixelData, sizeof(bmpPixelData));
if (!blackBMPImage || !_spMediaType)
{
return E_FAIL;
}
if (_spMediaType)
{
if (newSettings.overlayImage)
{
_overlayImage = LoadImageAsSample(newSettings.overlayImage, _spMediaType.Get());
}
_blackImage = LoadImageAsSample(blackBMPImage, _spMediaType.Get());
}
return S_OK;
}
// IMFMediaEventGenerator
IFACEMETHODIMP
SimpleMediaStream::BeginGetEvent(
_In_ IMFAsyncCallback* pCallback,
_In_ IUnknown* punkState)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->BeginGetEvent(pCallback, punkState));
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::EndGetEvent(
_In_ IMFAsyncResult* pResult,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->EndGetEvent(pResult, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::GetEvent(
DWORD dwFlags,
_COM_Outptr_ IMFMediaEvent** ppEvent)
{
// NOTE:
// GetEvent can block indefinitely, so we don't hold the lock.
// This requires some juggling with the event queue pointer.
HRESULT hr = S_OK;
ComPtr<IMFMediaEventQueue> spQueue;
{
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
spQueue = _spEventQueue;
}
// Now get the event.
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->GetEvent(dwFlags, ppEvent));
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::QueueEvent(
MediaEventType eventType,
REFGUID guidExtendedType,
HRESULT hrStatus,
_In_opt_ PROPVARIANT const* pvValue)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamVar(eventType, guidExtendedType, hrStatus, pvValue));
return hr;
}
// IMFMediaStream
IFACEMETHODIMP
SimpleMediaStream::GetMediaSource(
_COM_Outptr_ IMFMediaSource** ppMediaSource)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppMediaSource == nullptr)
{
return E_POINTER;
}
*ppMediaSource = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*ppMediaSource = _parent.Get();
(*ppMediaSource)->AddRef();
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::GetStreamDescriptor(
_COM_Outptr_ IMFStreamDescriptor** ppStreamDescriptor)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
if (ppStreamDescriptor == nullptr)
{
return E_POINTER;
}
*ppStreamDescriptor = nullptr;
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
if (_spStreamDesc != nullptr)
{
*ppStreamDescriptor = _spStreamDesc.Get();
(*ppStreamDescriptor)->AddRef();
}
else
{
return E_UNEXPECTED;
}
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::RequestSample(
_In_ IUnknown* pToken)
{
auto lock = _critSec.Lock();
HRESULT hr{};
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
const auto syncedSettings = SyncCurrentSettings();
// Source camera is updated, we must shutdown ourselves, since we can't modify presentation descriptor while running
if (!syncedSettings.newCameraName.empty())
{
std::thread{ [this] {
auto lock = _critSec.Lock();
SetStreamState(MF_STREAM_STATE_STOPPED);
} }.detach();
}
else if (syncedSettings.overlayImage)
{
_overlayImage = LoadImageAsSample(syncedSettings.overlayImage, _spMediaType.Get());
}
// Request the first video frame.
ComPtr<IMFSample> sample;
DWORD streamFlags = 0;
const auto readSampleResult = _sourceCamera->ReadSample(
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
0,
nullptr,
&streamFlags,
nullptr,
&sample);
IMFSample* outputSample = syncedSettings.webcamDisabled ? _overlayImage.Get() : sample.Get();
const bool noSampleAvailable = !outputSample;
// use black image instead, it should be always available
if (noSampleAvailable)
{
outputSample = _blackImage.Get();
}
RETURN_IF_FAILED_WITH_LOGGING(outputSample->SetSampleTime(MFGetSystemTime()));
RETURN_IF_FAILED_WITH_LOGGING(outputSample->SetSampleDuration(333333));
if (pToken != nullptr)
{
RETURN_IF_FAILED_WITH_LOGGING(outputSample->SetUnknown(MFSampleExtension_Token, pToken));
}
if (FAILED(readSampleResult) && syncedSettings.newCameraName.empty())
{
// Try to reinit webcamera, since it could've been unavailable due to concurrent access from 3rd-party apps
UpdateSourceCamera(_currentSourceCameraName ? *_currentSourceCameraName : L"");
}
RETURN_IF_FAILED_WITH_LOGGING(_spEventQueue->QueueEventParamUnk(MEMediaSample,
GUID_NULL,
S_OK,
outputSample));
return S_OK;
}
//////////////////////////////////////////////////////////////////////////////////////////
// IMFMediaStream2
IFACEMETHODIMP
SimpleMediaStream::SetStreamState(
MF_STREAM_STATE state)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
bool runningState = false;
RETURN_IF_FAILED(_CheckShutdownRequiresLock());
switch (state)
{
case MF_STREAM_STATE_PAUSED:
goto done; // because not supported
case MF_STREAM_STATE_RUNNING:
runningState = true;
break;
case MF_STREAM_STATE_STOPPED:
runningState = false;
_parent->Shutdown();
break;
default:
hr = MF_E_INVALID_STATE_TRANSITION;
break;
}
_isSelected = runningState;
done:
return hr;
}
IFACEMETHODIMP
SimpleMediaStream::GetStreamState(
_Out_ MF_STREAM_STATE* pState)
{
HRESULT hr = S_OK;
auto lock = _critSec.Lock();
RETURN_IF_FAILED_WITH_LOGGING(_CheckShutdownRequiresLock());
*pState = (_isSelected ? MF_STREAM_STATE_RUNNING : MF_STREAM_STATE_STOPPED);
return hr;
}
HRESULT
SimpleMediaStream::Shutdown()
{
VERBOSE_LOG;
HRESULT hr = S_OK;
if (_settingsUpdateChannel.has_value())
{
_settingsUpdateChannel->access([this](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
settings->cameraInUse = false;
});
}
auto lock = _critSec.Lock();
_isShutdown = true;
_parent.Reset();
if (_spEventQueue != nullptr)
{
hr = _spEventQueue->Shutdown();
_spEventQueue.Reset();
}
_spAttributes.Reset();
_spMediaType.Reset();
_spStreamDesc.Reset();
_sourceCamera.Reset();
_currentSourceCameraName.reset();
_settingsUpdateChannel.reset();
_overlayImage.Reset();
_isSelected = false;
return hr;
}
HRESULT SimpleMediaStream::UpdateSourceCamera(std::wstring_view newCameraName)
{
VERBOSE_LOG;
HRESULT hr = S_OK;
_cameraList.Clear();
RETURN_IF_FAILED_WITH_LOGGING(_cameraList.EnumerateDevices());
bool webcamIsChosen = false;
ComPtr<IMFActivate> webcamSourceActivator;
for (UINT32 i = 0; i < _cameraList.Count(); ++i)
{
if (_cameraList.GetDeviceName(i) == newCameraName)
{
_cameraList.GetDevice(i, &webcamSourceActivator);
webcamIsChosen = true;
_currentSourceCameraName.emplace(newCameraName);
break;
}
}
// Try selecting the first camera which isn't us, since at this point we can only guess the user's preferrence
if (!webcamIsChosen)
{
for (UINT32 i = 0; i < _cameraList.Count(); ++i)
{
const auto camName = _cameraList.GetDeviceName(i);
const bool differentCamera = _currentSourceCameraName.has_value() && camName != *_currentSourceCameraName;
if (camName != VIRTUAL_CAMERA_NAME && (differentCamera || !_currentSourceCameraName.has_value()))
{
RETURN_IF_FAILED_WITH_LOGGING(_cameraList.GetDevice(i, &webcamSourceActivator));
webcamIsChosen = true;
_currentSourceCameraName.emplace(camName);
break;
}
}
}
if (!webcamIsChosen)
{
return E_ABORT;
}
ComPtr<IMFMediaSource> realSource;
RETURN_IF_FAILED_WITH_LOGGING(webcamSourceActivator->ActivateObject(
__uuidof(IMFMediaSource),
(void**)realSource.GetAddressOf()));
ComPtr<IMFAttributes> pAttributes;
hr = MFCreateAttributes(&pAttributes, 2);
if (SUCCEEDED(hr))
{
ComPtr<IMFMediaTypeHandler> spTypeHandler;
hr = MFCreateSourceReaderFromMediaSource(
realSource.Get(),
pAttributes.Get(),
&_sourceCamera);
_spAttributes.Reset();
_spMediaType = SelectBestMediaType(_sourceCamera.Get());
RETURN_IF_FAILED_WITH_LOGGING(MFCreateAttributes(&_spAttributes, 10));
RETURN_IF_FAILED_WITH_LOGGING(_SetStreamAttributes(_spAttributes.Get()));
if (_spEventQueue)
{
_spEventQueue->Shutdown();
_spEventQueue.Reset();
}
RETURN_IF_FAILED_WITH_LOGGING(MFCreateEventQueue(&_spEventQueue));
_spStreamDesc.Reset();
// Initialize stream descriptors
RETURN_IF_FAILED_WITH_LOGGING(MFCreateStreamDescriptor(0, 1, _spMediaType.GetAddressOf(), &_spStreamDesc));
RETURN_IF_FAILED_WITH_LOGGING(_spStreamDesc->GetMediaTypeHandler(&spTypeHandler));
RETURN_IF_FAILED_WITH_LOGGING(spTypeHandler->SetCurrentMediaType(_spMediaType.Get()));
RETURN_IF_FAILED_WITH_LOGGING(_SetStreamDescriptorAttributes(_spStreamDesc.Get()));
RETURN_IF_FAILED_WITH_LOGGING(_sourceCamera->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, nullptr, _spMediaType.Get()));
}
return hr;
}
SimpleMediaStream::SyncedSettings SimpleMediaStream::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;
}
HRESULT
SimpleMediaStream::_CheckShutdownRequiresLock()
{
if (_isShutdown)
{
return MF_E_SHUTDOWN;
}
if (_spEventQueue == nullptr)
{
return E_UNEXPECTED;
}
return S_OK;
}
HRESULT
SimpleMediaStream::_SetStreamAttributes(
_In_ IMFAttributes* pAttributeStore)
{
HRESULT hr = S_OK;
if (nullptr == pAttributeStore)
{
return E_INVALIDARG;
}
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, PINNAME_VIDEO_CAPTURE));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_STREAM_ID, 0));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_FRAMESERVER_SHARED, 1));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES, _MFFrameSourceTypes::MFFrameSourceTypes_Color));
return hr;
}
HRESULT
SimpleMediaStream::_SetStreamDescriptorAttributes(
_In_ IMFAttributes* pAttributeStore)
{
HRESULT hr = S_OK;
if (nullptr == pAttributeStore)
{
return E_INVALIDARG;
}
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetGUID(MF_DEVICESTREAM_STREAM_CATEGORY, PINNAME_VIDEO_CAPTURE));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_STREAM_ID, 0));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_FRAMESERVER_SHARED, 1));
RETURN_IF_FAILED_WITH_LOGGING(pAttributeStore->SetUINT32(MF_DEVICESTREAM_ATTRIBUTE_FRAMESOURCE_TYPES, _MFFrameSourceTypes::MFFrameSourceTypes_Color));
return hr;
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include "stdafx.h"
#include <SerializedSharedMemory.h>
#include <CameraStateUpdateChannels.h>
#include <common/VideoCaptureDeviceList.h>
class SimpleMediaSource;
class SimpleMediaStream : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IMFMediaEventGenerator, IMFMediaStream, IMFMediaStream2>
{
friend class SimpleMediaSource;
public:
// IMFMediaEventGenerator
IFACEMETHOD(BeginGetEvent)
(IMFAsyncCallback* pCallback, IUnknown* punkState);
IFACEMETHOD(EndGetEvent)
(IMFAsyncResult* pResult, IMFMediaEvent** ppEvent);
IFACEMETHOD(GetEvent)
(DWORD dwFlags, IMFMediaEvent** ppEvent);
IFACEMETHOD(QueueEvent)
(MediaEventType met, REFGUID guidExtendedType, HRESULT hrStatus, const PROPVARIANT* pvValue);
// IMFMediaStream
IFACEMETHOD(GetMediaSource)
(IMFMediaSource** ppMediaSource);
IFACEMETHOD(GetStreamDescriptor)
(IMFStreamDescriptor** ppStreamDescriptor);
IFACEMETHOD(RequestSample)
(IUnknown* pToken);
// IMFMediaStream2
IFACEMETHOD(SetStreamState)
(MF_STREAM_STATE state);
IFACEMETHOD(GetStreamState)
(_Out_ MF_STREAM_STATE* pState);
// Non-interface methods.
HRESULT RuntimeClassInitialize(_In_ SimpleMediaSource* pSource);
HRESULT Shutdown();
protected:
struct SyncedSettings
{
bool webcamDisabled = false;
std::wstring newCameraName;
ComPtr<IStream> overlayImage;
};
HRESULT UpdateSourceCamera(std::wstring_view newCameraName);
SyncedSettings SyncCurrentSettings();
HRESULT _CheckShutdownRequiresLock();
HRESULT _SetStreamAttributes(IMFAttributes* pAttributeStore);
HRESULT _SetStreamDescriptorAttributes(IMFAttributes* pAttributeStore);
CriticalSection _critSec;
ComPtr<IMFMediaSource> _parent;
ComPtr<IMFMediaEventQueue> _spEventQueue;
ComPtr<IMFAttributes> _spAttributes;
ComPtr<IMFMediaType> _spMediaType;
ComPtr<IMFStreamDescriptor> _spStreamDesc;
bool _isShutdown = false;
bool _isSelected = false;
VideoCaptureDeviceList _cameraList;
ComPtr<IMFSourceReader> _sourceCamera;
ComPtr<IMFSample> _overlayImage;
ComPtr<IMFSample> _blackImage;
std::optional<SerializedSharedMemory> _settingsUpdateChannel;
std::optional<std::wstring> _currentSourceCameraName;
};

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\hasWDK.props" />
<Import Project="WDK_VideoConferenceCustomMediaSource.vcxproj" Condition="$(HasWDK)==true" />
<Import Project="..\..\..\wdk_skip.vcxproj" Condition="$(HasWDK)==false" />
<PropertyGroup Label="Globals">
<IntDirSharingDetected>None</IntDirSharingDetected>
<ProjectGuid>{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}</ProjectGuid>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<AdditionalOptions>/await</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalOptions>/await</AdditionalOptions>
</ClCompile>
</ItemDefinitionGroup>
</Project>

View File

@@ -0,0 +1,134 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<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>{43AD9BF7-E765-48FE-9826-71A8F2CB12DD}</ProjectGuid>
<RootNamespace>VideoConference</RootNamespace>
<WindowsTargetPlatformVersion>10.0.18362.0</WindowsTargetPlatformVersion>
<ProjectName>VideoConferenceCustomMediaSource</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>WindowsApplicationForDrivers10.0</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
<DriverTargetPlatform>Desktop</DriverTargetPlatform>
<Driver_SpectreMitigation>Spectre</Driver_SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</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)'=='Debug|x64'">
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<CodeAnalysisRuleSet>NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RunCodeAnalysis>false</RunCodeAnalysis>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\modules\VideoConference\</OutDir>
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\obj\$(ProjectName)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<EnablePREfast>false</EnablePREfast>
<ExceptionHandling>Sync</ExceptionHandling>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;$(IntDir);..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<AdditionalDependencies>$(OutDir)..\..\common.lib;$(OutDir)VideoConferenceShared.lib;mfplat.lib;Mfsensorgroup.lib;%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
<ModuleDefinitionFile>$(ProjectDir)/module.def</ModuleDefinitionFile>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<EnablePREfast>false</EnablePREfast>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<ExceptionHandling>Sync</ExceptionHandling>
<LanguageStandard>stdcpplatest</LanguageStandard>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;$(IntDir);..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>$(OutDir)..\..\common.lib;$(OutDir)VideoConferenceShared.lib;mfplat.lib;Mfsensorgroup.lib;%(AdditionalDependencies);OneCoreUAP.lib</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>%(IgnoreSpecificDefaultLibraries)</IgnoreSpecificDefaultLibraries>
<ModuleDefinitionFile>$(ProjectDir)/module.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="module.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllMain.cpp" />
<ClCompile Include="SimpleMediaSource.cpp" />
<ClCompile Include="SimpleMediaStream.cpp" />
<ClCompile Include="stdafx.cpp" />
<ClCompile Include="ImageLoader.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="SimpleMediaSource.h" />
<ClInclude Include="SimpleMediaStream.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="ImageLoader.h" />
</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,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="module.def" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="SimpleMediaSource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="SimpleMediaStream.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="stdafx.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dllMain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ImageLoader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SimpleMediaSource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="SimpleMediaStream.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="ImageLoader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="stdafx.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,35 @@
/// <copyright file="dllmain.cpp" company="Microsoft">
/// Copyright (c) Microsoft Corporation. All rights reserved.
/// </copyright>
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
#include <wrl\module.h>
#if !defined(__WRL_CLASSIC_COM__)
STDAPI DllGetActivationFactory(_In_ HSTRING activatibleClassId, _COM_Outptr_ IActivationFactory** factory)
{
return Module<InProc>::GetModule().GetActivationFactory(activatibleClassId, factory);
}
#endif
#if !defined(__WRL_WINRT_STRICT__)
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, _COM_Outptr_ void** ppv)
{
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
}
#endif
STDAPI DllCanUnloadNow()
{
return Module<InProc>::GetModule().Terminate() ? S_OK : S_FALSE;
}
STDAPI_(BOOL) DllMain(_In_opt_ HINSTANCE hinst, DWORD reason, _In_opt_ void*)
{
if (reason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hinst);
}
return TRUE;
}

View File

@@ -0,0 +1,6 @@
LIBRARY
EXPORTS
DllGetActivationFactory PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow 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,8 @@
/// <copyright file="stdafx.cpp" company="Microsoft">
/// Copyright (c) Microsoft Corporation. All rights reserved.
/// </copyright>
// stdafx.cpp : source file that includes just the standard includes
// KinectMFMediaSource.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information
#include "stdafx.h"

View File

@@ -0,0 +1,75 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#ifndef UNICODE
#define UNICODE
#endif
// Windows Header Files:
#include <windows.h>
#include <propvarutil.h>
//#include <mfstd.h> // Must be included before <initguid.h>, or else DirectDraw GUIDs will be defined twice. See the comment in <uuids.h>.
#include <ole2.h>
#include <initguid.h>
#include <ks.h>
#include <ksmedia.h>
#include <mfapi.h>
#include <mferror.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <nserror.h>
#include <winmeta.h>
#include <wrl.h>
#include <d3d9types.h>
#include <new>
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>
#include <Wmcodecdsp.h>
#include <assert.h>
#include <Dbt.h>
#include <shlwapi.h>
#include <string_view>
#include <optional>
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
#if !defined(_IKsControl_)
#define _IKsControl_
interface DECLSPEC_UUID("28F54685-06FD-11D2-B27A-00A0C9223196") IKsControl;
#undef INTERFACE
#define INTERFACE IKsControl
DECLARE_INTERFACE_(IKsControl, IUnknown)
{
STDMETHOD(KsProperty)
(
THIS_
IN PKSPROPERTY Property,
IN ULONG PropertyLength,
IN OUT LPVOID PropertyData,
IN ULONG DataLength,
OUT ULONG * BytesReturned) PURE;
STDMETHOD(KsMethod)
(
THIS_
IN PKSMETHOD Method,
IN ULONG MethodLength,
IN OUT LPVOID MethodData,
IN ULONG DataLength,
OUT ULONG * BytesReturned) PURE;
STDMETHOD(KsEvent)
(
THIS_
IN PKSEVENT Event OPTIONAL,
IN ULONG EventLength,
IN OUT LPVOID EventData,
IN ULONG DataLength,
OUT ULONG * BytesReturned) PURE;
};
#endif // _IKsControl_

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

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