[VCM]Deprecate the Video Conference Mute utility (#36772)

* Remove all VideoConferenceMute related code and files

* Clean up vcm driver registry keys

* Also remove the Webcam report tool

* Also clean out video conference on the installer

* Fix spellcheck

* Remove comment about video conf

* Update gpo files revision

* Revert removing the VCM policies

* Deprecate VCM GPO policy

* Change deprecation message to show first supported version

* Tweak supported strings in the adml
This commit is contained in:
Jaime Bernardo 2025-01-16 15:17:34 +00:00 коммит произвёл GitHub
Родитель 39bcba34d1
Коммит 12bb5c2131
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
157 изменённых файлов: 69 добавлений и 8684 удалений

1
.github/ISSUE_TEMPLATE/bug_report.yml поставляемый
Просмотреть файл

@ -77,7 +77,6 @@ body:
- Shortcut Guide
- System tray interaction
- TextExtractor
- Video Conference Mute
- Workspaces
- Welcome / PowerToys Tour window
validations:

Просмотреть файл

@ -51,7 +51,6 @@ body:
- Shortcut Guide
- System tray interaction
- TextExtractor
- Video Conference Mute
- Workspaces
- Welcome / PowerToys Tour window
validations:

1
.github/actions/spell-check/allow/names.txt поставляемый
Просмотреть файл

@ -23,7 +23,6 @@ registrypreview
rooler
scoobe
shortcutguide
videoconference
# USERS

4
.github/actions/spell-check/expect.txt поставляемый
Просмотреть файл

@ -1185,7 +1185,7 @@ psrm
psrree
pstatstg
pstm
pstr
PStr
pstream
pstrm
PSYSTEM
@ -1648,7 +1648,6 @@ vcamp
vcdl
vcgtq
VCINSTALLDIR
vcm
Vcpkg
VCRT
vcruntime
@ -1667,7 +1666,6 @@ vget
vgetq
vid
VIDCAP
videoconferencevirtualdriver
VIDEOINFOHEADER
viewmodel
vih

Просмотреть файл

@ -17,7 +17,6 @@
"PowerToys.FilePreviewCommon.dll",
"PowerToys.Interop.dll",
"Tools\\PowerToys.BugReportTool.exe",
"WebcamReportTool\\PowerToys.WebcamReportTool.exe",
"StylesReportTool\\PowerToys.StylesReportTool.exe",
"Telemetry.dll",
"PowerToys.ManagedTelemetry.dll",
@ -212,11 +211,6 @@
"PowerToys.ShortcutGuide.exe",
"PowerToys.ShortcutGuideModuleInterface.dll",
"PowerToys.VideoConferenceModule.dll",
"PowerToys.VideoConferenceProxyFilter_x86.dll",
"PowerToys.VideoConferenceProxyFilter_x64.dll",
"PowerToys.VideoConferenceProxyFilter_arm64.dll",
"WinUI3Apps\\PowerToys.Settings.dll",
"WinUI3Apps\\PowerToys.Settings.exe"
],

Просмотреть файл

@ -1,50 +0,0 @@
{
"Version": "1.0.0",
"UseMinimatch": false,
"SignBatches": [
{
"MatchedPath": [
"PowerToys.VideoConferenceProxyFilter_x86.dll"
],
"SigningInfo": {
"Operations": [
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolSign",
"Parameters": [
{
"parameterName": "OpusName",
"parameterValue": "Microsoft"
},
{
"parameterName": "OpusInfo",
"parameterValue": "http://www.microsoft.com"
},
{
"parameterName": "FileDigest",
"parameterValue": "/fd \"SHA256\""
},
{
"parameterName": "PageHash",
"parameterValue": "/NPH"
},
{
"parameterName": "TimeStamp",
"parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
}
],
"ToolName": "sign",
"ToolVersion": "1.0"
},
{
"KeyCode": "CP-230012",
"OperationSetCode": "SigntoolVerify",
"Parameters": [],
"ToolName": "sign",
"ToolVersion": "1.0"
}
]
}
}
]
}

Просмотреть файл

@ -172,7 +172,6 @@ jobs:
- pwsh: |-
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\PowerToys.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\BugReportTool\BugReportTool.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\WebcamReportTool\WebcamReportTool.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\StylesReportTool\StylesReportTool.sln'
& '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\installer\PowerToysSetup.sln'
displayName: Verify ARM64 configurations
@ -361,27 +360,6 @@ jobs:
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: VSBuild@1
displayName: Build WebcamReportTool
inputs:
solution: '**/tools/WebcamReportTool/WebcamReportTool.sln'
vsVersion: 17.0
msbuildArgs: >-
-restore -graph
/p:RestorePackagesConfig=true
/p:CIBuild=true
/bl:$(LogOutputDirectory)\build-webcam-report.binlog
${{ parameters.additionalBuildOptions }}
$(MSBuildCacheParameters)
$(RestoreAdditionalProjectSourcesArg)
platform: $(BuildPlatform)
configuration: $(BuildConfiguration)
msbuildArchitecture: x64
maximumCpuCount: true
${{ if eq(parameters.enableMsBuildCaching, true) }}:
env:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
- task: VSBuild@1
displayName: Build StylesReportTool
inputs:
@ -484,7 +462,7 @@ jobs:
displayName: Sign Core PowerToys
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: '$(BuildPlatform)/$(BuildConfiguration)' # Video conf uses x86 and x64.
FolderPath: '$(BuildPlatform)/$(BuildConfiguration)'
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_core.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
@ -499,16 +477,6 @@ jobs:
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_DSC.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
- template: steps-esrp-signing.yml
parameters:
displayName: Sign x86 DirectShow VCM
signingIdentity: ${{ parameters.signingIdentity }}
inputs:
FolderPath: 'x86/$(BuildConfiguration)' # Video conf uses x86 and x64.
signType: batchSigning
batchSignPolicyFile: '$(build.sourcesdirectory)\.pipelines\ESRPSigning_vcm.json'
ciPolicyFile: '$(build.sourcesdirectory)\.pipelines\CIPolicy.xml'
- template: steps-build-installer.yml
parameters:
codeSign: ${{ parameters.codeSign }}

Просмотреть файл

@ -941,26 +941,6 @@ _If you want to find diagnostic data events in the source code, these two links
</tr>
</table>
### Video Conference Mute
<table style="width:100%">
<tr>
<th>Event Name</th>
<th>Description</th>
</tr>
<tr>
<td>Microsoft.PowerToys.VideoConference_CameraMuted</td>
<td>Triggered when the camera is turned off by Video Conference Mute.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.VideoConference_EnableVideoConference</td>
<td>Occurs when Video Conference Mute is enabled.</td>
</tr>
<tr>
<td>Microsoft.PowerToys.VideoConference_MicrophoneMuted</td>
<td>Occurs when the microphone is muted by Video Conference Mute.</td>
</tr>
</table>
### Workspaces
<table style="width:100%">
<tr>

Просмотреть файл

@ -349,17 +349,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PowerToys.Update", "src\Upd
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerToys.Run.Plugin.WindowsSettings", "src\modules\launcher\Plugins\Microsoft.PowerToys.Run.Plugin.WindowsSettings\Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj", "{5043CECE-E6A7-4867-9CBE-02D27D83747A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceShared", "src\modules\videoconference\VideoConferenceShared\VideoConferenceShared.vcxproj", "{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceModule", "src\modules\videoconference\VideoConferenceModule\VideoConference.vcxproj", "{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoConferenceProxyFilter", "src\modules\videoconference\VideoConferenceProxyFilter\VideoConferenceProxyFilter.vcxproj", "{AC2857B4-103D-4D6D-9740-926EBF785042}"
ProjectSection(ProjectDependencies) = postProject
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "VideoConference", "VideoConference", "{470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PdfThumbnailProvider", "src\modules\previewpane\PdfThumbnailProvider\PdfThumbnailProvider.csproj", "{11491FD8-F921-48BF-880C-7FEA185B80A1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests-PdfThumbnailProvider", "src\modules\previewpane\UnitTests-PdfThumbnailProvider\UnitTests-PdfThumbnailProvider.csproj", "{F40C3397-1834-4530-B2D9-8F8B8456BCDF}"
@ -1509,40 +1498,6 @@ Global
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.ActiveCfg = Release|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x64.Build.0 = Release|x64
{5043CECE-E6A7-4867-9CBE-02D27D83747A}.Release|x86.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|ARM64.ActiveCfg = Debug|ARM64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|ARM64.Build.0 = Debug|ARM64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.ActiveCfg = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x64.Build.0 = Debug|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x86.ActiveCfg = Debug|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Debug|x86.Build.0 = Debug|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|ARM64.ActiveCfg = Release|ARM64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|ARM64.Build.0 = Release|ARM64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.ActiveCfg = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x64.Build.0 = Release|x64
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x86.ActiveCfg = Release|Win32
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A}.Release|x86.Build.0 = Release|Win32
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|ARM64.ActiveCfg = Debug|ARM64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|ARM64.Build.0 = Debug|ARM64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|x64.ActiveCfg = Debug|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|x64.Build.0 = Debug|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Debug|x86.ActiveCfg = Debug|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|ARM64.ActiveCfg = Release|ARM64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|ARM64.Build.0 = Release|ARM64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|x64.ActiveCfg = Release|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|x64.Build.0 = Release|x64
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}.Release|x86.ActiveCfg = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|ARM64.ActiveCfg = Debug|ARM64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|ARM64.Build.0 = Debug|ARM64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x64.ActiveCfg = Debug|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x64.Build.0 = Debug|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x86.ActiveCfg = Debug|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Debug|x86.Build.0 = Debug|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|ARM64.ActiveCfg = Release|ARM64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|ARM64.Build.0 = Release|ARM64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.ActiveCfg = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x64.Build.0 = Release|x64
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x86.ActiveCfg = Release|Win32
{AC2857B4-103D-4D6D-9740-926EBF785042}.Release|x86.Build.0 = Release|Win32
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Debug|ARM64.ActiveCfg = Debug|ARM64
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Debug|ARM64.Build.0 = Debug|ARM64
{11491FD8-F921-48BF-880C-7FEA185B80A1}.Debug|x64.ActiveCfg = Debug|x64
@ -2938,10 +2893,6 @@ Global
{48804216-2A0E-4168-A6D8-9CD068D14227} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{FF1D7936-842A-4BBB-8BEA-E9FE796DE700} = {D1D6BC88-09AE-4FB4-AD24-5DED46A791DD}
{5043CECE-E6A7-4867-9CBE-02D27D83747A} = {4AFC9975-2456-4C70-94A4-84073C1CED93}
{459E0768-7EBD-4C41-BBA1-6DB3B3815E0A} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
{AC2857B4-103D-4D6D-9740-926EBF785042} = {470FBAF9-E1F8-4F3E-8786-198A1C81C8A8}
{470FBAF9-E1F8-4F3E-8786-198A1C81C8A8} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC}
{11491FD8-F921-48BF-880C-7FEA185B80A1} = {2F305555-C296-497E-AC20-5FA1B237996A}
{F40C3397-1834-4530-B2D9-8F8B8456BCDF} = {2F305555-C296-497E-AC20-5FA1B237996A}
{A2D583F0-B70C-4462-B1F0-8E81AFB7BA85} = {4AFC9975-2456-4C70-94A4-84073C1CED93}

Просмотреть файл

@ -18,7 +18,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
| [New+](https://aka.ms/PowerToysOverview_NewPlus) | [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) |
| [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) |
| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) |
| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) |
## Installing and running Microsoft PowerToys
@ -226,6 +226,5 @@ The application logs basic diagnostic data (telemetry). For more information on
[winget-link]: https://github.com/microsoft/winget-cli#installing-the-client
[roadmap]: https://github.com/microsoft/PowerToys/wiki/Roadmap
[privacy-link]: http://go.microsoft.com/fwlink/?LinkId=521839
[vidConfOverview]: https://aka.ms/PowerToysOverview_VideoConference
[loc-bug]: https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=translation_issue.md&title=
[usingPowerToys-docs-link]: https://aka.ms/powertoys-docs

Просмотреть файл

@ -29,7 +29,6 @@
| PowerToysOverview_PowerRename | https://learn.microsoft.com/windows/powertoys/powerrename |
| PowerToysOverview_PowerToysRun | https://learn.microsoft.com/windows/powertoys/run |
| PowerToysOverview_ShortcutGuide | https://learn.microsoft.com/windows/powertoys/shortcut-guide |
| PowerToysOverview_VideoConference | https://learn.microsoft.com/windows/powertoys/video-conference-mute |
| powerToysPowerLauncherImageSmall | https://github.com/microsoft/PowerToys/wiki/images/overview/PowerLauncher_small.png |
| powerToysPowerLauncherSettingImage | https://raw.githubusercontent.com/microsoft/PowerToys/main/doc/images/overview/PowerLauncher_large.png |
| powerToysPowerPreviewImageSmall | https://github.com/microsoft/PowerToys/wiki/images/overview/PowerPreview_small.png |
@ -42,6 +41,4 @@
| powerToysRequestFeature | https://github.com/microsoft/PowerToys/issues/new?assignees=&labels=&template=feature_request.md&title= |
| powerToysShortcutGuideImageSmall | https://github.com/microsoft/PowerToys/wiki/images/overview/ShortcutGuide_small.png |
| powerToysShortcutGuideSettingImage | https://raw.githubusercontent.com/microsoft/PowerToys/main/doc/images/overview/ShortcutGuide_large.png |
| powerToysVideoConferenceImageSmall | https://github.com/microsoft/PowerToys/wiki/images/overview/VideoConference_small.png |
| powerToysVideoConferenceSettingImage | https://github.com/microsoft/PowerToys/wiki/images/overview/VideoConference_large.png |
| powertoyswiki | https://github.com/microsoft/PowerToys/wiki |

Просмотреть файл

@ -73,7 +73,6 @@ The installer can only be compiled in `Release` mode; steps 1 and 2 must be perf
1. Compile `PowerToys.sln`. Instructions are listed above.
1. Compile `BugReportTool.sln` tool. Path from root: `tools\BugReportTool\BugReportTool.sln` (details listed below)
1. Compile `WebcamReportTool.sln` tool. Path from root: `tools\WebcamReportTool\WebcamReportTool.sln` (details listed below)
1. Compile `StylesReportTool.sln` tool. Path from root: `tools\StylesReportTool\StylesReportTool.sln` (details listed below)
1. Compile `PowerToysSetup.sln` Path from root: `installer\PowerToysSetup.sln` (details listed below)
@ -95,9 +94,6 @@ The installer can only be compiled in `Release` mode; steps 1 and 2 must be perf
nuget restore .\tools\BugReportTool\BugReportTool.sln
msbuild -p:Platform=x64 -p:Configuration=Release .\tools\BugReportTool\BugReportTool.sln
nuget restore .\tools\WebcamReportTool\WebcamReportTool.sln
msbuild -p:Platform=x64 -p:Configuration=Release .\tools\WebcamReportTool\WebcamReportTool.sln
nuget restore .\tools\StylesReportTool\StylesReportTool.sln
msbuild -p:Platform=x64 -p:Configuration=Release .\tools\StylesReportTool\StylesReportTool.sln
```
@ -109,9 +105,6 @@ If you prefer, you can alternatively build prerequisite projects for the install
1. Open `tools\BugReportTool\BugReportTool.sln`
1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
1. From the `Build` menu, choose `Build Solution`.
1. Open `tools\WebcamReportTool\WebcamReportTool.sln`
1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
1. From the `Build` menu, choose `Build Solution`.
1. Open `tools\StylesReportTool\StylesReportTool.sln`
1. In Visual Studio, in the `Solutions Configuration` drop-down menu select `Release`
1. From the `Build` menu, choose `Build Solution`.

Просмотреть файл

@ -25,7 +25,3 @@ This script is used by the pipeline to move the .resw files to the correct locat
## [versionSetting.ps1](/tools/build/versionSetting.ps1)
Sets `version.props` file with the version number.
## [video_conference_make_cab.ps1](/tools/build/video_conference_make_cab.ps1)
This script creates a cab file for the Video Conference Mute driver.

Просмотреть файл

@ -18,4 +18,3 @@ Following tools are currently available:
* [project template](/tools/project_template/README.md) - A Visual Studio project template for a new PowerToys project.
* [StylesReportTool](styles-report-tool.md) - A tool to collect information about an open window.
* [Verification scripts](verification-scripts.md) - A set of scripts that help verifying the PowerToys installation.
* [WebcamReportTool](webcam-report-tool.md) - A tool to collect information about the connected webcams.

Просмотреть файл

@ -1,6 +0,0 @@
# [WebcamReportTool](/tools/WebcamReportTool/)
This command line application generates a report about the connected webcams on the desktop called "WebcamReport.txt". The report contains the following information about every webcam:
* Name
* Supported formats

Двоичные данные
doc/images/overview/Original/VideoConference.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 96 KiB

Двоичные данные
doc/images/overview/VideoConference_large.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 54 KiB

Двоичные данные
doc/images/overview/VideoConference_small.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 22 KiB

Просмотреть файл

@ -8,7 +8,6 @@
<?define FileLocksmithProjectName="FileLocksmith"?>
<?define ColorPickerProjectName="ColorPicker"?>
<?define PowerOCRProjectName="PowerOCR"?>
<?define VideoConferenceProjectName="VideoConference"?>
<?define AwakeProjectName="Awake"?>
<?define MouseUtilsProjectName="MouseUtils"?>
<?define AlwaysOnTopProjectName="AlwaysOnTop"?>

Просмотреть файл

@ -51,7 +51,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
call move /Y ..\..\..\Settings.wxs.bk ..\..\..\Settings.wxs
call move /Y ..\..\..\ShortcutGuide.wxs.bk ..\..\..\ShortcutGuide.wxs
call move /Y ..\..\..\Tools.wxs.bk ..\..\..\Tools.wxs
call move /Y ..\..\..\VideoConference.wxs.bk ..\..\..\VideoConference.wxs
call move /Y ..\..\..\WinAppSDK.wxs.bk ..\..\..\WinAppSDK.wxs
call move /Y ..\..\..\WinUI3Applications.wxs.bk ..\..\..\WinUI3Applications.wxs
call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs
@ -119,7 +118,6 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil
<Compile Include="Settings.wxs" />
<Compile Include="ShortcutGuide.wxs" />
<Compile Include="Tools.wxs" />
<Compile Include="VideoConference.wxs" />
<Compile Include="MouseWithoutBorders.wxs" />
<Compile Include="WinUI3Applications.wxs" />
<Compile Include="MonacoSRC.wxs" />

Просмотреть файл

@ -69,7 +69,6 @@
<ComponentGroupRef Id="RunComponentGroup" />
<ComponentGroupRef Id="SettingsComponentGroup" />
<ComponentGroupRef Id="ShortcutGuideComponentGroup" />
<ComponentGroupRef Id="VideoConferenceComponentGroup" />
<ComponentGroupRef Id="MouseWithoutBordersComponentGroup" />
<ComponentGroupRef Id="EnvironmentVariablesComponentGroup" />
<ComponentGroupRef Id="AdvancedPasteComponentGroup" />
@ -196,6 +195,9 @@
<Custom Action="TerminateProcesses" Before="InstallValidate" />
<Custom Action="LaunchPowerToys" Before="InstallFinalize">NOT Installed</Custom>
<!-- Clean Video Conference Mute registry keys that might be around from previous installations. We've deprecated this utility since then. -->
<Custom Action="CleanVideoConferenceRegistry" Before="InstallFinalize">NOT Installed</Custom>
</InstallExecuteSequence>
<CustomAction Id="SetLaunchPowerToysParam"
@ -393,6 +395,14 @@
DllEntry="DetectPrevInstallPathCA"
/>
<CustomAction Id="CleanVideoConferenceRegistry"
Return="ignore"
Impersonate="yes"
Execute="deferred"
BinaryKey="PTCustomActions"
DllEntry="CleanVideoConferenceRegistryCA"
/>
<CustomAction Id="ApplyModulesRegistryChangeSets"
Return="check"
Impersonate="yes"

Просмотреть файл

@ -12,12 +12,6 @@
</RegistryKey>
<File Source="$(var.BinDir)Tools\PowerToys.BugReportTool.exe" Id="BugReportTool.exe" Checksum="yes" />
</Component>
<Component Id="WebcamReportTool_exe" Win64="yes" Guid="41D5209F-7A9A-4DF2-A22A-9F0A9CF5AA63">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="WebcamReportTool_exe" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.BinDir)WebcamReportTool\PowerToys.WebcamReportTool.exe" Id="WebcamReportTool.exe" Checksum="yes" />
</Component>
<Component Id="StylesReportTool_exe" Win64="yes" Guid="9D348A78-38A0-4FDC-8D16-BDB0178E5F1E">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="StylesReportTool_exe" Value="" KeyPath="yes"/>
@ -34,7 +28,6 @@
<RemoveFolder Id="RemoveFolderToolsFolder" Directory="ToolsFolder" On="uninstall"/>
</Component>
<ComponentRef Id="BugReportTool_exe" />
<ComponentRef Id="WebcamReportTool_exe" />
<ComponentRef Id="StylesReportTool_exe" />
</ComponentGroup>

Просмотреть файл

@ -1,56 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension" >
<?include $(sys.CURRENTDIR)\Common.wxi?>
<Fragment>
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BinDir)">
<!-- !Warning! Make sure to change Component Guid if you update the file list -->
<Component Id="Module_VideoConference" Guid="CCE30DCC-AC6B-4A2D-9BD8-2E9598E5B785" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 19041</Condition>
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_VideoConference" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.BinX32Dir)PowerToys.VideoConferenceProxyFilter_x86.dll" />
</Component>
</DirectoryRef>
<DirectoryRef Id="BaseApplicationsAssetsFolder">
<Directory Id="VideoConferenceAssetsFolder" Name="VCM"/>
</DirectoryRef>
<DirectoryRef Id="VideoConferenceAssetsFolder" FileSource="$(var.BinDir)\Assets\VCM">
<!-- !Warning! Make sure to change Component Guid if you update the file list -->
<Component Id="Module_VideoConferenceIcons" Guid="E78339BF-58D8-48F2-A1C3-E1C3DC72DCAE" Win64="yes">
<Condition>WINDOWSBUILDNUMBER >= 19041</Condition>
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="Module_VideoConferenceIcons" Value="" KeyPath="yes"/>
</RegistryKey>
<File Source="$(var.BinDir)Assets\VCM\Off-NotInUse Dark.png" />
<File Source="$(var.BinDir)Assets\VCM\Off-NotInUse Light.png" />
<File Source="$(var.BinDir)Assets\VCM\Off-Off Dark.png" />
<File Source="$(var.BinDir)Assets\VCM\Off-Off Light.png" />
<File Source="$(var.BinDir)Assets\VCM\Off-On Dark.png" />
<File Source="$(var.BinDir)Assets\VCM\Off-On Light.png" />
<File Source="$(var.BinDir)Assets\VCM\On-NotInUse Dark.png" />
<File Source="$(var.BinDir)Assets\VCM\On-NotInUse Light.png" />
<File Source="$(var.BinDir)Assets\VCM\On-Off Light.png" />
<File Source="$(var.BinDir)Assets\VCM\On-Off Dark.png" />
<File Source="$(var.BinDir)Assets\VCM\On-On Dark.png" />
<File Source="$(var.BinDir)Assets\VCM\On-On Light.png" />
<File Source="$(var.BinDir)Assets\VCM\black.bmp" />
</Component>
</DirectoryRef>
<ComponentGroup Id="VideoConferenceComponentGroup">
<Component Id="RemoveVideoConferenceFolder" Guid="C6F2BE3D-FDE3-4CDB-BB51-A43E1B7B1606" Directory="INSTALLFOLDER" >
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\powertoys\components">
<RegistryValue Type="string" Name="RemoveVideoConferenceFolder" Value="" KeyPath="yes"/>
</RegistryKey>
<RemoveFolder Id="RemoveFolderVideoConferenceAssetsFolder" Directory="VideoConferenceAssetsFolder" On="uninstall"/>
</Component>
<ComponentRef Id="Module_VideoConference" />
<ComponentRef Id="Module_VideoConferenceIcons" />
</ComponentGroup>
</Fragment>
</Wix>

Просмотреть файл

@ -11,6 +11,7 @@
#include "../../src/common/updating/installer.h"
#include "../../src/common/version/version.h"
#include "../../src/common/Telemetry/EtwTrace/EtwTrace.h"
#include "../../src/common/utils/clean_video_conference.h"
#include <winrt/Windows.ApplicationModel.h>
#include <winrt/Windows.Foundation.h>
@ -328,6 +329,19 @@ LExit:
return WcaFinalize(er);
}
// We've deprecated Video Conference Mute. This Custom Action cleans up any stray registry entry for the driver dll.
UINT __stdcall CleanVideoConferenceRegistryCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
hr = WcaInitialize(hInstall, "CleanVideoConferenceRegistry");
ExitOnFailure(hr, "Failed to initialize");
clean_video_conference();
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall ApplyModulesRegistryChangeSetsCA(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
@ -1026,164 +1040,6 @@ UINT __stdcall DetectPrevInstallPathCA(MSIHANDLE hInstall)
return WcaFinalize(er);
}
UINT __stdcall CertifyVirtualCameraDriverCA(MSIHANDLE hInstall)
{
#ifdef CIBuild // On pipeline we are using microsoft certification
WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
return WcaFinalize(ERROR_SUCCESS);
#else
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
LPWSTR certificatePath = nullptr;
HCERTSTORE hCertStore = nullptr;
HANDLE hfile = nullptr;
DWORD size = INVALID_FILE_SIZE;
char* pFileContent = nullptr;
hr = WcaInitialize(hInstall, "CertifyVirtualCameraDriverCA");
ExitOnFailure(hr, "Failed to initialize", hr);
hr = WcaGetProperty(L"CustomActionData", &certificatePath);
ExitOnFailure(hr, "Failed to get install property", 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 = static_cast<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,
reinterpret_cast<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, static_cast<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 property");
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, static_cast<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 property");
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("Failed to uninstall virtual camera driver"));
MsiProcessMessage(hInstall, static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING + MB_OK), hRecord);
}
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
UINT __stdcall UnRegisterContextMenuPackagesCA(MSIHANDLE hInstall)
{
using namespace winrt::Windows::Foundation;

Просмотреть файл

@ -3,6 +3,7 @@ LIBRARY "PowerToysSetupCustomActions"
EXPORTS
LaunchPowerToysCA
CheckGPOCA
CleanVideoConferenceRegistryCA
ApplyModulesRegistryChangeSetsCA
DetectPrevInstallPathCA
RemoveScheduledTasksCA
@ -15,12 +16,9 @@ EXPORTS
TelemetryLogRepairCancelCA
TelemetryLogRepairFailCA
TerminateProcessesCA
CertifyVirtualCameraDriverCA
InstallVirtualCameraDriverCA
InstallEmbeddedMSIXCA
InstallDSCModuleCA
UnApplyModulesRegistryChangeSetsCA
UninstallVirtualCameraDriverCA
UnRegisterContextMenuPackagesCA
UninstallEmbeddedMSIXCA
UninstallDSCModuleCA

Просмотреть файл

@ -73,7 +73,6 @@
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Settings.wxs"" ""$(ProjectDir)..\PowerToysSetup\Settings.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\ShortcutGuide.wxs"" ""$(ProjectDir)..\PowerToysSetup\ShortcutGuide.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Tools.wxs"" ""$(ProjectDir)..\PowerToysSetup\Tools.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\VideoConference.wxs"" ""$(ProjectDir)..\PowerToysSetup\VideoConference.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\WinAppSDK.wxs"" ""$(ProjectDir)..\PowerToysSetup\WinAppSDK.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\WinUI3Applications.wxs"" ""$(ProjectDir)..\PowerToysSetup\WinUI3Applications.wxs.bk""""
call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Workspaces.wxs"" ""$(ProjectDir)..\PowerToysSetup\Workspaces.wxs.bk""""

Просмотреть файл

@ -22,7 +22,6 @@ namespace Common.UI
PowerRename,
FileExplorer,
ShortcutGuide,
VideoConference,
Hosts,
MeasureTool,
PowerOCR,
@ -60,8 +59,6 @@ namespace Common.UI
return "FileExplorer";
case SettingsWindow.ShortcutGuide:
return "ShortcutGuide";
case SettingsWindow.VideoConference:
return "VideoConference";
case SettingsWindow.Hosts:
return "Hosts";
case SettingsWindow.MeasureTool:

Просмотреть файл

@ -128,10 +128,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredAdvancedPasteEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredVideoConferenceMuteEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue());
}
GpoRuleConfigured GPOWrapper::GetConfiguredMouseWithoutBordersEnabledValue()
{
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredMouseWithoutBordersEnabledValue());

Просмотреть файл

@ -39,7 +39,6 @@ namespace winrt::PowerToys::GPOWrapper::implementation
static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue();
static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue();
static GpoRuleConfigured GetConfiguredAdvancedPasteEnabledValue();
static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue();
static GpoRuleConfigured GetConfiguredPeekEnabledValue();
static GpoRuleConfigured GetDisableNewUpdateToastValue();
static GpoRuleConfigured GetDisableAutomaticUpdateDownloadValue();

Просмотреть файл

@ -43,7 +43,6 @@ namespace PowerToys
static GpoRuleConfigured GetConfiguredShortcutGuideEnabledValue();
static GpoRuleConfigured GetConfiguredTextExtractorEnabledValue();
static GpoRuleConfigured GetConfiguredAdvancedPasteEnabledValue();
static GpoRuleConfigured GetConfiguredVideoConferenceMuteEnabledValue();
static GpoRuleConfigured GetConfiguredPeekEnabledValue();
static GpoRuleConfigured GetDisableNewUpdateToastValue();
static GpoRuleConfigured GetDisableAutomaticUpdateDownloadValue();

Просмотреть файл

@ -2,8 +2,6 @@
#include "CommonManaged.h"
#include "CommonManaged.g.cpp"
#include <common/version/version.h>
#include "../../modules/videoconference/VideoConferenceShared/MicrophoneDevice.h"
#include "../../modules/videoconference/VideoConferenceShared/VideoCaptureDeviceList.h"
namespace winrt::PowerToys::Interop::implementation
{
@ -11,29 +9,4 @@ namespace winrt::PowerToys::Interop::implementation
{
return hstring{ get_product_version() };
}
winrt::Windows::Foundation::Collections::IVector<hstring> CommonManaged::GetAllActiveMicrophoneDeviceNames()
{
auto names = std::vector<winrt::hstring>();
for (const auto& device : MicrophoneDevice::getAllActive())
{
names.push_back(device->name().data());
}
return winrt::multi_threaded_vector(std::move(names));
}
winrt::Windows::Foundation::Collections::IVector<hstring> CommonManaged::GetAllVideoCaptureDeviceNames()
{
auto names = std::vector<winrt::hstring>();
VideoCaptureDeviceList vcdl;
vcdl.EnumerateDevices();
for (UINT32 i = 0; i < vcdl.Count(); ++i)
{
auto name = vcdl.GetDeviceName(i).data();
if (name != L"PowerToys VideoConference Mute")
{
names.push_back(name);
}
}
return winrt::multi_threaded_vector(std::move(names));
}
}

Просмотреть файл

@ -8,8 +8,6 @@ namespace winrt::PowerToys::Interop::implementation
CommonManaged() = default;
static hstring GetProductVersion();
static winrt::Windows::Foundation::Collections::IVector<hstring> GetAllActiveMicrophoneDeviceNames();
static winrt::Windows::Foundation::Collections::IVector<hstring> GetAllVideoCaptureDeviceNames();
};
}
namespace winrt::PowerToys::Interop::factory_implementation

Просмотреть файл

@ -4,8 +4,6 @@ namespace PowerToys
{
[default_interface] static runtimeclass CommonManaged {
static String GetProductVersion();
static Windows.Foundation.Collections.IVector<String> GetAllActiveMicrophoneDeviceNames();
static Windows.Foundation.Collections.IVector<String> GetAllVideoCaptureDeviceNames();
}
}
}

Просмотреть файл

@ -93,8 +93,6 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.h" />
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.h" />
<ClInclude Include="async_message_queue.h" />
<ClInclude Include="CommonManaged.h">
<DependentUpon>CommonManaged.idl</DependentUpon>
@ -123,8 +121,6 @@
<ClInclude Include="two_way_pipe_message_ipc_impl.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp" />
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp" />
<ClCompile Include="CommonManaged.cpp">
<DependentUpon>CommonManaged.idl</DependentUpon>
</ClCompile>

Просмотреть файл

@ -21,12 +21,6 @@
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shared_constants.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -65,12 +59,6 @@
<ClCompile Include="keyboard_layout.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\MicrophoneDevice.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\videoconference\VideoConferenceShared\VideoCaptureDeviceList.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>

Просмотреть файл

@ -0,0 +1,18 @@
#pragma once
// Video Conference Mute was a utility we deprecated. However, this required a manual user disable of the module to remove the camera registration, so we include the disable code here to be able to clean up.
void clean_video_conference()
{
// 31AD75E9-8C3A-49C8-B9ED-5880D6B4A764 is the CLSID GUID for the 64 video conference mute driver.
// 31AD75E9-8C3A-49C8-B9ED-5880D6B4A732 is the CLSID GUID for the 32 video conference mute driver.
// 860BB310-5D01-11D0-BD3B-00A0C911CE86 is the CLSID GUID for CLSID_VideoInputDeviceCategory.
// Unregister the 64 bit driver CLSID:
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"CLSID\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}");
// Unregister the 64 bit driver CLSID from Video Input Devices:
RegDeleteTreeW(HKEY_CLASSES_ROOT, L"CLSID\\{860BB310-5D01-11D0-BD3B-00A0C911CE86}\\Instance\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A764}");
// Unregister the 32 bit driver CLSID:
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"Software\\WOW6432Node\\Classes\\CLSID\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}");
// Unregister the 32 bit driver CLSID from Video Input Devices:
RegDeleteTreeW(HKEY_LOCAL_MACHINE, L"Software\\WOW6432Node\\Classes\\CLSID\\{860BB310-5D01-11D0-BD3B-00A0C911CE86}\\Instance\\{31AD75E9-8C3A-49C8-B9ED-5880D6B4A732}");
}

Просмотреть файл

@ -53,7 +53,6 @@ namespace powertoys_gpo {
const std::wstring POLICY_CONFIGURE_ENABLED_SHORTCUT_GUIDE = L"ConfigureEnabledUtilityShortcutGuide";
const std::wstring POLICY_CONFIGURE_ENABLED_TEXT_EXTRACTOR = L"ConfigureEnabledUtilityTextExtractor";
const std::wstring POLICY_CONFIGURE_ENABLED_ADVANCED_PASTE = L"ConfigureEnabledUtilityAdvancedPaste";
const std::wstring POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE = L"ConfigureEnabledUtilityVideoConferenceMute";
const std::wstring POLICY_CONFIGURE_ENABLED_REGISTRY_PREVIEW = L"ConfigureEnabledUtilityRegistryPreview";
const std::wstring POLICY_CONFIGURE_ENABLED_MOUSE_WITHOUT_BORDERS = L"ConfigureEnabledUtilityMouseWithoutBorders";
const std::wstring POLICY_CONFIGURE_ENABLED_PEEK = L"ConfigureEnabledUtilityPeek";
@ -414,11 +413,6 @@ namespace powertoys_gpo {
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_WORKSPACES);
}
inline gpo_rule_configured_t getConfiguredVideoConferenceMuteEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_VIDEO_CONFERENCE_MUTE);
}
inline gpo_rule_configured_t getConfiguredMouseWithoutBordersEnabledValue()
{
return getUtilityEnabledValue(POLICY_CONFIGURE_ENABLED_MOUSE_WITHOUT_BORDERS);

Просмотреть файл

@ -10,7 +10,7 @@
</ItemGroup>
<WriteLinesToFile File="Generated Files\version_gen.h" Lines="@(HeaderLines)" Overwrite="true" Encoding="Unicode" WriteOnlyWhenDifferent="true" />
</Target>
<!-- Needed for .\src\modules\videoconference\VideoConferenceProxyFilter\build_vcm_x86.cmd -->
<!-- Project Configurations needed for building Tools -->
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>

Просмотреть файл

@ -63,8 +63,6 @@ properties:
Enabled: false
ShortcutGuide:
Enabled: false
VideoConference:
Enabled: false
MeasureTool:
Enabled: false
Hosts:

Просмотреть файл

@ -63,8 +63,6 @@ properties:
Enabled: true
ShortcutGuide:
Enabled: true
VideoConference:
Enabled: true
MeasureTool:
Enabled: true
Hosts:

Просмотреть файл

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.13" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.15" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyNamespaces>
<target prefix="powertoys" namespace="Microsoft.Policies.PowerToys" />
</policyNamespaces>
<resources minRequiredRevision="1.14"/><!-- Last changed with PowerToys v0.86.0 -->
<resources minRequiredRevision="1.15"/><!-- Last changed with PowerToys v0.88.0 -->
<supportedOn>
<definitions>
<definition name="SUPPORTED_POWERTOYS_0_64_0" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0)"/>
@ -23,6 +23,7 @@
<definition name="SUPPORTED_POWERTOYS_0_84_0" displayName="$(string.SUPPORTED_POWERTOYS_0_84_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_85_0" displayName="$(string.SUPPORTED_POWERTOYS_0_85_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_86_0" displayName="$(string.SUPPORTED_POWERTOYS_0_86_0)"/>
<definition name="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1" displayName="$(string.SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1)"/>
</definitions>
</supportedOn>
<categories>
@ -45,6 +46,9 @@
<category name="NewPlus" displayName="$(string.NewPlus)">
<parentCategory ref="PowerToys" />
</category>
<category name="DeprecatedPolicies" displayName="$(string.DeprecatedPolicies)">
<parentCategory ref="PowerToys" />
</category>
</categories>
<policies>
@ -441,8 +445,8 @@
</disabledValue>
</policy>
<policy name="ConfigureEnabledUtilityVideoConferenceMute" class="Both" displayName="$(string.ConfigureEnabledUtilityVideoConferenceMute)" explainText="$(string.ConfigureEnabledUtilityDescription)" key="Software\Policies\PowerToys" valueName="ConfigureEnabledUtilityVideoConferenceMute">
<parentCategory ref="PowerToys" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0" />
<parentCategory ref="DeprecatedPolicies" />
<supportedOn ref="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1" />
<enabledValue>
<decimal value="1" />
</enabledValue>

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft Corporation.
Licensed under the MIT License. -->
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.14" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<policyDefinitionResources xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" revision="1.15" schemaVersion="1.0" xmlns="http://schemas.microsoft.com/GroupPolicy/2006/07/PolicyDefinitions">
<displayName>PowerToys</displayName>
<description>PowerToys</description>
<resources>
@ -13,6 +13,7 @@
<string id="MouseWithoutBorders">Mouse Without Borders</string>
<string id="GeneralSettings">General settings</string>
<string id="NewPlus">New+</string>
<string id="DeprecatedPolicies">Deprecated policies</string>
<string id="SUPPORTED_POWERTOYS_0_64_0">PowerToys version 0.64.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_68_0">PowerToys version 0.68.0 or later</string>
@ -29,6 +30,7 @@
<string id="SUPPORTED_POWERTOYS_0_84_0">PowerToys version 0.84.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_85_0">PowerToys version 0.85.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_86_0">PowerToys version 0.86.0 or later</string>
<string id="SUPPORTED_POWERTOYS_0_64_0_TO_0_87_1">From PowerToys version 0.64.0 until PowerToys version 0.87.1</string>
<string id="ConfigureAllUtilityGlobalEnabledStateDescription">This policy configures the enabled state for all PowerToys utilities.

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 13 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 13 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 3.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 11 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.6 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 11 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.6 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.5 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.5 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.4 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.9 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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="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"/>
<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>

До

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 6.2 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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="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"/>
<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>

До

Ширина:  |  Высота:  |  Размер: 3.3 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 5.7 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 10 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 4.9 KiB

Просмотреть файл

@ -1,19 +0,0 @@
<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>

До

Ширина:  |  Высота:  |  Размер: 2.3 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 4.9 KiB

Просмотреть файл

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

До

Ширина:  |  Высота:  |  Размер: 2.3 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 822 B

Просмотреть файл

@ -1,78 +0,0 @@
#include "pch.h"
#include "AudioDeviceNotificationClient.h"
AudioDeviceNotificationClient::AudioDeviceNotificationClient()
{
(void)CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&_deviceEnumerator));
if (!_deviceEnumerator)
{
return;
}
if (FAILED(_deviceEnumerator->RegisterEndpointNotificationCallback(this)))
{
_deviceEnumerator->Release();
_deviceEnumerator = nullptr;
}
}
AudioDeviceNotificationClient::~AudioDeviceNotificationClient()
{
if (!_deviceEnumerator)
{
return;
}
_deviceEnumerator->UnregisterEndpointNotificationCallback(this);
_deviceEnumerator->Release();
}
ULONG AudioDeviceNotificationClient::AddRef()
{
return 1;
}
ULONG AudioDeviceNotificationClient::Release()
{
return 1;
}
HRESULT AudioDeviceNotificationClient::QueryInterface(REFIID, void**)
{
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDeviceAdded(LPCWSTR)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDeviceRemoved(LPCWSTR)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDeviceStateChanged(LPCWSTR, DWORD)
{
_deviceConfigurationChanged = true;
return S_OK;
}
HRESULT AudioDeviceNotificationClient::OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR)
{
if (role == eConsole && (flow == eCapture || flow == eAll))
{
_deviceConfigurationChanged = true;
}
return S_OK;
}

Просмотреть файл

@ -1,30 +0,0 @@
#pragma once
#include <MMDeviceAPI.h>
struct AudioDeviceNotificationClient : IMMNotificationClient
{
AudioDeviceNotificationClient();
~AudioDeviceNotificationClient();
bool PullPendingNotifications()
{
const bool result = _deviceConfigurationChanged;
_deviceConfigurationChanged = false;
return result;
}
private:
ULONG AddRef() override;
ULONG Release() override;
HRESULT QueryInterface(REFIID, void**) override;
HRESULT OnPropertyValueChanged(LPCWSTR, const PROPERTYKEY) override;
HRESULT OnDeviceAdded(LPCWSTR) override;
HRESULT OnDeviceRemoved(LPCWSTR) override;
HRESULT OnDeviceStateChanged(LPCWSTR, DWORD) override;
HRESULT OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR) override;
IMMDeviceEnumerator* _deviceEnumerator = nullptr;
bool _deviceConfigurationChanged = false;
};

Просмотреть файл

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

Просмотреть файл

@ -1,390 +0,0 @@
#include "pch.h"
#include "Toolbar.h"
#include <windowsx.h>
#include <common/Themes/windows_colors.h>
#include <common/Display/dpi_aware.h>
#include "Logging.h"
#include "VideoConferenceModule.h"
Toolbar* toolbar = nullptr;
const int REFRESH_RATE = 100;
const int OVERLAY_SHOW_TIME = 500;
const int BORDER_OFFSET = 12;
const int TOP_RIGHT_BORDER_OFFSET = 40;
std::wstring cached_position = L"";
Toolbar::Toolbar()
{
toolbar = this;
darkImages.camOnMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-On Dark.png");
darkImages.camOffMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-Off Dark.png");
darkImages.camOnMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-On Dark.png");
darkImages.camOffMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-Off Dark.png");
darkImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-NotInUse Dark.png");
darkImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-NotInUse Dark.png");
lightImages.camOnMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-On Light.png");
lightImages.camOffMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-Off Light.png");
lightImages.camOnMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-On Light.png");
lightImages.camOffMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-Off Light.png");
lightImages.camUnusedMicOn = Gdiplus::Image::FromFile(L"Assets/VCM/On-NotInUse Light.png");
lightImages.camUnusedMicOff = Gdiplus::Image::FromFile(L"Assets/VCM/Off-NotInUse Light.png");
}
void Toolbar::scheduleModuleSettingsUpdate()
{
moduleSettingsUpdateScheduled = true;
}
void Toolbar::scheduleGeneralSettingsUpdate()
{
generalSettingsUpdateScheduled = true;
}
inline POINT calculateToolbarPositioning(Box const& screenSize, std::wstring& position, const int desiredWidth, const int desiredHeight)
{
POINT p;
p.x = p.y = 0;
if (position == L"Top left corner")
{
p.x = screenSize.left() + BORDER_OFFSET;
p.y = screenSize.top() + BORDER_OFFSET;
}
else if (position == L"Top center")
{
p.x = screenSize.middle().x - desiredWidth / 2;
p.y = screenSize.top() + BORDER_OFFSET;
}
else if (position == L"Bottom left corner")
{
p.x = screenSize.left() + BORDER_OFFSET;
p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET;
}
else if (position == L"Bottom center")
{
p.x = screenSize.middle().x - desiredWidth / 2;
p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET;
}
else if (position == L"Bottom right corner")
{
p.x = screenSize.right() - desiredWidth - BORDER_OFFSET;
p.y = screenSize.bottom() - desiredHeight - BORDER_OFFSET;
}
else //"Top right corner" or non-present
{
p.x = screenSize.right() - desiredWidth - BORDER_OFFSET;
p.y = screenSize.top() + TOP_RIGHT_BORDER_OFFSET;
}
return p;
}
LRESULT Toolbar::WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_DESTROY:
return 0;
case WM_LBUTTONDOWN:
{
int x = GET_X_LPARAM(lparam);
int y = GET_Y_LPARAM(lparam);
UINT dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForWindow(hwnd, dpi);
if (x < 322 * static_cast<int>(dpi) / static_cast<int>(DPIAware::DEFAULT_DPI) / 2)
{
VideoConferenceModule::reverseMicrophoneMute();
}
else
{
VideoConferenceModule::reverseVirtualCameraMuteState();
}
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
case WM_DPICHANGED:
{
UINT dpi = LOWORD(wparam);
RECT* prcNewWindow = reinterpret_cast<RECT*>(lparam);
POINT suggestedPosition;
suggestedPosition.x = prcNewWindow->left;
suggestedPosition.y = prcNewWindow->top;
int desiredWidth = prcNewWindow->right - prcNewWindow->left;
int desiredHeight = prcNewWindow->bottom - prcNewWindow->top;
HMONITOR hMonitor = MonitorFromPoint(suggestedPosition, MONITOR_DEFAULTTONEAREST);
MonitorInfo info{ hMonitor };
suggestedPosition = calculateToolbarPositioning(info.GetScreenSize(false), cached_position, desiredWidth, desiredHeight);
SetWindowPos(hwnd,
NULL,
suggestedPosition.x,
suggestedPosition.y,
desiredWidth,
desiredHeight,
SWP_NOZORDER | SWP_NOACTIVATE);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
case WM_CREATE:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc;
UINT dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForWindow(hwnd, dpi);
hdc = BeginPaint(hwnd, &ps);
Gdiplus::Graphics graphic(hdc);
ToolbarImages* themeImages = &toolbar->darkImages;
if (toolbar->theme == L"light" || (toolbar->theme == L"system" && !WindowsColors::is_dark_mode()))
{
themeImages = &toolbar->lightImages;
}
else
{
themeImages = &toolbar->darkImages;
}
Gdiplus::Image* toolbarImage = nullptr;
if (!toolbar->cameraInUse)
{
if (toolbar->microphoneMuted)
{
toolbarImage = themeImages->camUnusedMicOff;
}
else
{
toolbarImage = themeImages->camUnusedMicOn;
}
}
else if (toolbar->microphoneMuted)
{
if (toolbar->cameraMuted)
{
toolbarImage = themeImages->camOffMicOff;
}
else
{
toolbarImage = themeImages->camOnMicOff;
}
}
else
{
if (toolbar->cameraMuted)
{
toolbarImage = themeImages->camOffMicOn;
}
else
{
toolbarImage = themeImages->camOnMicOn;
}
}
// Images are scaled by 4 to support higher dpi values.
graphic.DrawImage(toolbarImage, 0, 0, toolbarImage->GetWidth() / 4 * dpi / DPIAware::DEFAULT_DPI, toolbarImage->GetHeight() / 4 * dpi / DPIAware::DEFAULT_DPI);
EndPaint(hwnd, &ps);
break;
}
case WM_TIMER:
{
if (toolbar->audioConfChangesNotifier.PullPendingNotifications())
{
instance->onMicrophoneConfigurationChanged();
}
toolbar->microphoneMuted = instance->getMicrophoneMuteState();
if (toolbar->generalSettingsUpdateScheduled)
{
instance->onGeneralSettingsChanged();
toolbar->generalSettingsUpdateScheduled = false;
}
if (toolbar->moduleSettingsUpdateScheduled)
{
instance->onModuleSettingsChanged();
toolbar->moduleSettingsUpdateScheduled = false;
}
toolbar->cameraInUse = VideoConferenceModule::getVirtualCameraInUse();
InvalidateRect(hwnd, NULL, NULL);
using namespace std::chrono;
const auto nowMillis = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
const bool showOverlayTimeout = nowMillis - toolbar->lastTimeCamOrMicMuteStateChanged > OVERLAY_SHOW_TIME;
static bool previousShow = false;
bool show = toolbar->ToolbarHide == L"Never";
const bool cameraJustStoppedInUse = toolbar->previouscameraInUse && !toolbar->cameraInUse;
bool shouldUnmuteAll = cameraJustStoppedInUse;
if (toolbar->ToolbarHide == L"When both camera and microphone are muted")
{
// We shouldn't unmute devices, since we'd like to only show the toolbar only
// when something is unmuted -> the use case is to keep everything muted by default and track it
shouldUnmuteAll = false;
show = (!toolbar->cameraMuted && toolbar->cameraInUse) || !toolbar->microphoneMuted;
}
else if (toolbar->ToolbarHide == L"When both camera and microphone are unmuted")
show = (toolbar->cameraMuted && toolbar->cameraInUse) || toolbar->microphoneMuted;
if (shouldUnmuteAll && !toolbar->moduleSettingsUpdateScheduled)
VideoConferenceModule::unmuteAll();
show = show || !showOverlayTimeout;
ShowWindow(hwnd, show ? SW_SHOW : SW_HIDE);
if (previousShow != show)
{
previousShow = show;
LOG(show ? "Toolbar visibility changed to shown" : "Toolbar visibility changed to hidden");
}
KillTimer(hwnd, toolbar->nTimerId);
toolbar->previouscameraInUse = toolbar->cameraInUse;
break;
}
default:
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
toolbar->nTimerId = SetTimer(hwnd, 101, REFRESH_RATE, nullptr);
return DefWindowProcW(hwnd, msg, wparam, lparam);
}
void Toolbar::show(std::wstring position, std::wstring monitorString)
{
cached_position = position;
for (auto& hwnd : hwnds)
{
PostMessageW(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
// Images are scaled by 4 to support higher dpi values.
int overlayWidth = darkImages.camOffMicOff->GetWidth() / 4;
int overlayHeight = darkImages.camOffMicOff->GetHeight() / 4;
// Register the window class
LPCWSTR CLASS_NAME = L"MuteNotificationWindowClass";
WNDCLASS wc{};
wc.hInstance = GetModuleHandleW(nullptr);
wc.lpszClassName = CLASS_NAME;
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
wc.lpfnWndProc = WindowProcessMessages;
RegisterClassW(&wc);
// Create the window
DWORD dwExtStyle = 0;
DWORD dwStyle = WS_POPUPWINDOW;
std::vector<MonitorInfo> monitorInfos;
if (monitorString == L"All monitors")
{
monitorInfos = MonitorInfo::GetMonitors(false);
}
else //"Main monitor" or non-present
{
monitorInfos.push_back(MonitorInfo::GetPrimaryMonitor());
}
for (auto& monitorInfo : monitorInfos)
{
const auto screenSize = monitorInfo.GetScreenSize(false);
UINT dpi = DPIAware::DEFAULT_DPI;
DPIAware::GetScreenDPIForMonitor(monitorInfo.GetHandle(), dpi);
int scaledOverlayWidth = overlayWidth * dpi / DPIAware::DEFAULT_DPI;
int scaledOverlayHeight = overlayHeight * dpi / DPIAware::DEFAULT_DPI;
POINT p = calculateToolbarPositioning(screenSize, position, scaledOverlayWidth, scaledOverlayHeight);
HWND hwnd;
hwnd = CreateWindowExW(
WS_EX_TOOLWINDOW | WS_EX_LAYERED,
CLASS_NAME,
CLASS_NAME,
WS_POPUP,
static_cast<int>(p.x),
static_cast<int>(p.y),
scaledOverlayWidth,
scaledOverlayHeight,
nullptr,
nullptr,
GetModuleHandleW(nullptr),
nullptr);
auto transparentColorKey = RGB(0, 0, 255);
HBRUSH brush = CreateSolidBrush(transparentColorKey);
SetClassLongPtr(hwnd, GCLP_HBRBACKGROUND, reinterpret_cast<LONG_PTR>(brush));
SetLayeredWindowAttributes(hwnd, transparentColorKey, 0, LWA_COLORKEY);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
hwnds.push_back(hwnd);
}
}
void Toolbar::hide()
{
for (auto& hwnd : hwnds)
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
hwnds.clear();
}
bool Toolbar::getCameraMute()
{
return cameraMuted;
}
void Toolbar::setCameraMute(bool mute)
{
if (mute != cameraMuted)
{
lastTimeCamOrMicMuteStateChanged = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
cameraMuted = mute;
}
bool Toolbar::getMicrophoneMute()
{
return microphoneMuted;
}
void Toolbar::setMicrophoneMute(bool mute)
{
if (mute != microphoneMuted)
{
lastTimeCamOrMicMuteStateChanged = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
microphoneMuted = mute;
}
void Toolbar::setToolbarHide(std::wstring hide)
{
ToolbarHide = hide;
}
void Toolbar::setTheme(std::wstring theme)
{
Toolbar::theme = theme;
}

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -1,189 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" />
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{5ABA70DE-3A3F-41F6-A1F5-D1F74F54F9BB}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>overlaywindow</RootNamespace>
<ProjectName>VideoConferenceModule</ProjectName>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
<TargetName>PowerToys.VideoConferenceModule</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation>Spectre</SpectreMitigation>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;OVERLAYWINDOW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..\..\..\;..\..\;..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;OVERLAYWINDOW_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..\..\..\common\inc;..\..\..\common\Telemetry;..\..\..\;..\..\;..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
<AdditionalDependencies>shlwapi.lib;gdiplus.lib;dwmapi.lib;uxtheme.lib;shcore.lib;Wtsapi32.lib;dxguid.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="AudioDeviceNotificationClient.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="VideoConferenceModule.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="AudioDeviceNotificationClient.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="Toolbar.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(UsePrecompiledHeaders)' != 'false'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="VideoConferenceModule.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VideoConferenceShared\VideoConferenceShared.vcxproj">
<Project>{459e0768-7ebd-4c41-bba1-6db3b3815e0a}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="Assets\VCM\black.bmp">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\Off-NotInUse Dark.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\Off-NotInUse Light.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\Off-Off Dark.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\Off-Off Light.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\Off-On Dark.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\Off-On Light.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\On-NotInUse Dark.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\On-NotInUse Light.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\On-Off Dark.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\On-Off Light.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\On-On Dark.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="Assets\VCM\On-On Light.png">
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\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.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
<ItemGroup>
<ProjectReference Include="..\..\..\common\Display\Display.vcxproj">
<Project>{caba8dfb-823b-4bf2-93ac-3f31984150d9}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="VideoConferenceModule.rc" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="Toolbar.cpp" />
<ClCompile Include="pch.cpp" />
<ClCompile Include="VideoConferenceModule.cpp" />
<ClCompile Include="trace.cpp" />
<ClCompile Include="AudioDeviceNotificationClient.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="Toolbar.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="VideoConferenceModule.h" />
<ClInclude Include="trace.h" />
<ClInclude Include="AudioDeviceNotificationClient.h" />
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="Assets">
<UniqueIdentifier>{efd98846-4568-41d4-a425-451c246802cd}</UniqueIdentifier>
</Filter>
<Filter Include="Assets\VCM">
<UniqueIdentifier>{e79f8c1a-b78b-4ba5-b923-f7db79eba776}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="VideoConferenceModule.rc" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\VCM\Off-NotInUse Dark.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\Off-NotInUse Light.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\Off-Off Dark.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\Off-Off Light.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\Off-On Dark.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\Off-On Light.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\On-NotInUse Dark.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\On-NotInUse Light.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\On-Off Dark.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\On-Off Light.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\On-On Dark.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\On-On Light.png">
<Filter>Assets\VCM</Filter>
</Content>
<Content Include="Assets\VCM\black.bmp">
<Filter>Assets\VCM</Filter>
</Content>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

Просмотреть файл

@ -1,733 +0,0 @@
#include "pch.h"
#include "VideoConferenceModule.h"
#include <WinUser.h>
#include <gdiplus.h>
#include <shellapi.h>
#include <filesystem>
#include <unordered_set>
#include <common/debug_control.h>
#include <common/SettingsAPI/settings_helpers.h>
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <CameraStateUpdateChannels.h>
#include "logging.h"
#include "trace.h"
extern "C" IMAGE_DOS_HEADER __ImageBase;
VideoConferenceModule* instance = nullptr;
VideoConferenceSettings VideoConferenceModule::settings;
Toolbar VideoConferenceModule::toolbar;
bool VideoConferenceModule::pushToTalkPressed = false;
HHOOK VideoConferenceModule::hook_handle;
IAudioEndpointVolume* endpointVolume = NULL;
bool VideoConferenceModule::isKeyPressed(unsigned int keyCode)
{
return (GetKeyState(keyCode) & 0x8000);
}
namespace fs = std::filesystem;
bool VideoConferenceModule::isHotkeyPressed(DWORD code, PowerToysSettings::HotkeyObject& hotkey)
{
return code == hotkey.get_code() &&
isKeyPressed(VK_SHIFT) == hotkey.shift_pressed() &&
isKeyPressed(VK_CONTROL) == hotkey.ctrl_pressed() &&
isKeyPressed(VK_LWIN) == hotkey.win_pressed() &&
(isKeyPressed(VK_LMENU)) == hotkey.alt_pressed();
}
void VideoConferenceModule::reverseMicrophoneMute()
{
// All controlled mic should same state with _microphoneTrackedInUI
// Avoid manually change in Control Panel make controlled mic has different state
bool muted = !getMicrophoneMuteState();
for (auto& controlledMic : instance->_controlledMicrophones)
{
controlledMic->set_muted(muted);
}
if (muted)
{
Trace::MicrophoneMuted();
}
instance->_mic_muted_state_during_disconnect = !instance->_mic_muted_state_during_disconnect;
toolbar.setMicrophoneMute(muted);
}
bool VideoConferenceModule::getMicrophoneMuteState()
{
return instance->_microphoneTrackedInUI ? instance->_microphoneTrackedInUI->muted() : instance->_mic_muted_state_during_disconnect;
}
void VideoConferenceModule::reverseVirtualCameraMuteState()
{
bool muted = false;
if (!instance->_settingsUpdateChannel.has_value())
{
return;
}
instance->_settingsUpdateChannel->access([&muted](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
settings->useOverlayImage = !settings->useOverlayImage;
muted = settings->useOverlayImage;
});
if (muted)
{
Trace::CameraMuted();
}
toolbar.setCameraMute(muted);
}
bool VideoConferenceModule::getVirtualCameraMuteState()
{
bool disabled = false;
if (!instance->_settingsUpdateChannel.has_value())
{
return disabled;
}
instance->_settingsUpdateChannel->access([&disabled](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
disabled = settings->useOverlayImage;
});
return disabled;
}
bool VideoConferenceModule::getVirtualCameraInUse()
{
if (!instance->_settingsUpdateChannel.has_value())
{
return false;
}
bool inUse = false;
instance->_settingsUpdateChannel->access([&inUse](auto settingsMemory) {
auto settings = reinterpret_cast<CameraSettingsUpdateChannel*>(settingsMemory._data);
inUse = settings->cameraInUse;
});
return inUse;
}
LRESULT CALLBACK VideoConferenceModule::LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* kbd = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
switch (wParam)
{
case WM_KEYDOWN:
if (isHotkeyPressed(kbd->vkCode, settings.cameraAndMicrophoneMuteHotkey))
{
const bool cameraInUse = getVirtualCameraInUse();
const bool microphoneIsMuted = getMicrophoneMuteState();
const bool cameraIsMuted = cameraInUse && getVirtualCameraMuteState();
if (cameraInUse)
{
// we're likely on a video call, so we must mute the unmuted cam/mic or reverse the mute state
// of everything, if cam and mic mute states are the same
if (microphoneIsMuted == cameraIsMuted)
{
reverseMicrophoneMute();
reverseVirtualCameraMuteState();
}
else if (cameraIsMuted)
{
reverseMicrophoneMute();
}
else if (microphoneIsMuted)
{
reverseVirtualCameraMuteState();
}
}
else
{
// if the camera is not in use, we just mute/unmute the mic
reverseMicrophoneMute();
}
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.microphoneMuteHotkey))
{
reverseMicrophoneMute();
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.microphonePushToTalkHotkey))
{
if (!pushToTalkPressed)
{
if (settings.pushToReverseEnabled || getMicrophoneMuteState())
{
reverseMicrophoneMute();
}
pushToTalkPressed = true;
}
return 1;
}
else if (isHotkeyPressed(kbd->vkCode, settings.cameraMuteHotkey))
{
reverseVirtualCameraMuteState();
return 1;
}
break;
case WM_KEYUP:
if (pushToTalkPressed && (kbd->vkCode == settings.microphonePushToTalkHotkey.get_code()))
{
reverseMicrophoneMute();
pushToTalkPressed = false;
return 1;
}
}
}
return CallNextHookEx(hook_handle, nCode, wParam, lParam);
}
void VideoConferenceModule::onGeneralSettingsChanged()
{
auto settings = PTSettingsHelper::load_general_settings();
bool enabled = false;
try
{
if (json::has(settings, L"enabled"))
{
for (const auto& mod : settings.GetNamedObject(L"enabled"))
{
const auto value = mod.Value();
if (value.ValueType() != json::JsonValueType::Boolean)
{
continue;
}
if (mod.Key() == get_key())
{
enabled = value.GetBoolean();
break;
}
}
}
}
catch (...)
{
LOG("Couldn't get enabled state");
}
if (enabled)
{
enable();
}
else
{
disable();
}
}
void VideoConferenceModule::onModuleSettingsChanged()
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::load_from_settings_file(get_key());
//Trace::SettingsChanged(pressTime.value, overlayOpacity.value, theme.value);
if (_enabled)
{
if (const auto val = values.get_json(L"mute_camera_and_microphone_hotkey"))
{
settings.cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(L"mute_microphone_hotkey"))
{
settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_json(L"push_to_talk_microphone_hotkey"))
{
settings.microphonePushToTalkHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_bool_value(L"push_to_reverse_enabled"))
{
settings.pushToReverseEnabled = *val;
}
if (const auto val = values.get_json(L"mute_camera_hotkey"))
{
settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = values.get_string_value(L"toolbar_position"))
{
settings.toolbarPositionString = val.value();
}
if (const auto val = values.get_string_value(L"toolbar_monitor"))
{
settings.toolbarMonitorString = val.value();
}
if (const auto val = values.get_string_value(L"selected_camera"); val && val != settings.selectedCamera)
{
settings.selectedCamera = val.value();
sendSourceCameraNameUpdate();
}
if (const auto val = values.get_string_value(L"camera_overlay_image_path"); val && val != settings.imageOverlayPath)
{
settings.imageOverlayPath = val.value();
sendOverlayImageUpdate();
}
if (const auto val = values.get_string_value(L"toolbar_hide"))
{
toolbar.setToolbarHide(val.value());
}
if (const auto val = values.get_string_value(L"startup_action"))
{
settings.startupAction = val.value();
}
const auto selectedMic = values.get_string_value(L"selected_mic");
if (selectedMic && selectedMic != settings.selectedMicrophone)
{
settings.selectedMicrophone = *selectedMic;
updateControlledMicrophones(settings.selectedMicrophone);
}
toolbar.show(settings.toolbarPositionString, settings.toolbarMonitorString);
}
}
catch (...)
{
LOG("onModuleSettingsChanged encountered an exception");
}
}
void VideoConferenceModule::onMicrophoneConfigurationChanged()
{
if (!_controllingAllMics)
{
// Don't care if we don't control all the mics
return;
}
const bool mutedStateForNewMics = getMicrophoneMuteState();
std::unordered_set<std::wstring_view> currentlyTrackedMicsIds;
for (const auto& controlledMic : _controlledMicrophones)
{
currentlyTrackedMicsIds.emplace(controlledMic->id());
}
auto allMics = MicrophoneDevice::getAllActive();
for (auto& newMic : allMics)
{
if (currentlyTrackedMicsIds.contains(newMic->id()))
{
continue;
}
if (mutedStateForNewMics)
{
newMic->set_muted(true);
}
_controlledMicrophones.emplace_back(std::move(newMic));
}
// Restore invalidated pointer
_microphoneTrackedInUI = controlledDefaultMic();
if (_microphoneTrackedInUI)
{
_microphoneTrackedInUI->set_mute_changed_callback([](const bool muted) {
toolbar.setMicrophoneMute(muted);
});
}
}
VideoConferenceModule::VideoConferenceModule()
{
init_settings();
_settingsUpdateChannel =
SerializedSharedMemory::create(CameraSettingsUpdateChannel::endpoint(), sizeof(CameraSettingsUpdateChannel), false);
if (_settingsUpdateChannel)
{
_settingsUpdateChannel->access([](auto memory) {
// Suppress warning 26403 - Reset or explicitly delete an owner<T> pointer 'variable' (r.3)
// the video conference class should be only instantiated once and it is using placement new
// the access to the data can be done through memory._data
#pragma warning(push)
#pragma warning(disable : 26403)
auto updatesChannel = new (memory._data) CameraSettingsUpdateChannel{};
#pragma warning(pop)
});
}
sendSourceCameraNameUpdate();
sendOverlayImageUpdate();
}
inline VideoConferenceModule::~VideoConferenceModule()
{
toolbar.hide();
}
const wchar_t* VideoConferenceModule::get_name()
{
return L"Video Conference";
}
const wchar_t* VideoConferenceModule::get_key()
{
return L"Video Conference";
}
// Return the configured status for the gpo policy for the module
powertoys_gpo::gpo_rule_configured_t VideoConferenceModule::gpo_policy_enabled_configuration()
{
return powertoys_gpo::getConfiguredVideoConferenceMuteEnabledValue();
}
bool VideoConferenceModule::get_config(wchar_t* buffer, int* buffer_size)
{
return true;
}
void VideoConferenceModule::set_config(const wchar_t* config)
{
try
{
PowerToysSettings::PowerToyValues values = PowerToysSettings::PowerToyValues::from_json_string(config, get_key());
values.save_to_settings_file();
}
catch (...)
{
LOG("VideoConferenceModule::set_config: exception during saving new settings values");
}
}
void VideoConferenceModule::init_settings()
{
try
{
PowerToysSettings::PowerToyValues powerToysSettings = PowerToysSettings::PowerToyValues::load_from_settings_file(L"Video Conference");
if (const auto val = powerToysSettings.get_json(L"mute_camera_and_microphone_hotkey"))
{
settings.cameraAndMicrophoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_json(L"mute_microphone_hotkey"))
{
settings.microphoneMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_json(L"push_to_talk_microphone_hotkey"))
{
settings.microphonePushToTalkHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_bool_value(L"push_to_reverse_enabled"))
{
settings.pushToReverseEnabled = *val;
}
if (const auto val = powerToysSettings.get_json(L"mute_camera_hotkey"))
{
settings.cameraMuteHotkey = PowerToysSettings::HotkeyObject::from_json(*val);
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_position"))
{
settings.toolbarPositionString = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_monitor"))
{
settings.toolbarMonitorString = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"selected_camera"))
{
settings.selectedCamera = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"camera_overlay_image_path"))
{
settings.imageOverlayPath = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"toolbar_hide"))
{
toolbar.setToolbarHide(val.value());
}
if (const auto val = powerToysSettings.get_string_value(L"startup_action"))
{
settings.startupAction = val.value();
}
if (const auto val = powerToysSettings.get_string_value(L"selected_mic"); val && *val != settings.selectedMicrophone)
{
settings.selectedMicrophone = *val;
updateControlledMicrophones(settings.selectedMicrophone);
}
}
catch (std::exception&)
{
// Error while loading from the settings file. Just let default values stay as they are.
}
try
{
auto loaded = PTSettingsHelper::load_general_settings();
std::wstring settings_theme{ static_cast<std::wstring_view>(loaded.GetNamedString(L"theme", L"system")) };
if (settings_theme != L"dark" && settings_theme != L"light")
{
settings_theme = L"system";
}
toolbar.setTheme(settings_theme);
}
catch (...)
{
}
}
void VideoConferenceModule::updateControlledMicrophones(const std::wstring_view new_mic)
{
for (auto& controlledMic : _controlledMicrophones)
{
controlledMic->set_muted(false);
}
_controlledMicrophones.clear();
_microphoneTrackedInUI = nullptr;
auto allMics = MicrophoneDevice::getAllActive();
if (new_mic == L"[All]")
{
_controllingAllMics = true;
_controlledMicrophones = std::move(allMics);
_microphoneTrackedInUI = controlledDefaultMic();
}
else
{
_controllingAllMics = false;
for (auto& controlledMic : allMics)
{
if (controlledMic->name() == new_mic)
{
_controlledMicrophones.emplace_back(std::move(controlledMic));
_microphoneTrackedInUI = _controlledMicrophones[0].get();
break;
}
}
}
if (_microphoneTrackedInUI)
{
_microphoneTrackedInUI->set_mute_changed_callback([](const bool muted) {
toolbar.setMicrophoneMute(muted);
});
toolbar.setMicrophoneMute(_microphoneTrackedInUI->muted());
}
if (settings.startupAction == L"Unmute")
{
for (auto& controlledMic : _controlledMicrophones)
{
controlledMic->set_muted(false);
}
}
else if (settings.startupAction == L"Mute")
{
for (auto& controlledMic : _controlledMicrophones)
{
controlledMic->set_muted(true);
}
}
}
MicrophoneDevice* VideoConferenceModule::controlledDefaultMic()
{
if (auto defaultMic = MicrophoneDevice::getDefault())
{
for (auto& controlledMic : _controlledMicrophones)
{
if (controlledMic->id() == defaultMic->id())
{
return controlledMic.get();
}
}
}
return nullptr;
}
void toggleProxyCamRegistration(const bool enable)
{
if (!is_process_elevated())
{
return;
}
auto vcmRoot = fs::path{ get_module_folderpath() };
#if defined(_M_ARM64)
std::array<fs::path, 2> proxyFilters = { vcmRoot / "PowerToys.VideoConferenceProxyFilter_ARM64.dll", vcmRoot / "PowerToys.VideoConferenceProxyFilter_x86.dll" };
#else
std::array<fs::path, 2> proxyFilters = { vcmRoot / "PowerToys.VideoConferenceProxyFilter_x64.dll", vcmRoot / "PowerToys.VideoConferenceProxyFilter_x86.dll" };
#endif
for (const auto filter : proxyFilters)
{
std::wstring params{ L"/s " };
if (!enable)
{
params += L"/u ";
}
params += '"';
params += filter;
params += '"';
SHELLEXECUTEINFOW sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
sei.lpFile = L"regsvr32";
sei.lpParameters = params.c_str();
sei.nShow = SW_SHOWNORMAL;
ShellExecuteExW(&sei);
}
}
void VideoConferenceModule::enable()
{
if (!_enabled)
{
_generalSettingsWatcher = std::make_unique<FileWatcher>(
PTSettingsHelper::get_powertoys_general_save_file_location(), [this] {
toolbar.scheduleGeneralSettingsUpdate();
});
_moduleSettingsWatcher = std::make_unique<FileWatcher>(
PTSettingsHelper::get_module_save_file_location(get_key()), [this] {
toolbar.scheduleModuleSettingsUpdate();
});
toggleProxyCamRegistration(true);
toolbar.setMicrophoneMute(getMicrophoneMuteState());
toolbar.setCameraMute(getVirtualCameraMuteState());
toolbar.show(settings.toolbarPositionString, settings.toolbarMonitorString);
_enabled = true;
#if defined(DISABLE_LOWLEVEL_HOOKS_WHEN_DEBUGGED)
if (IsDebuggerPresent())
{
return;
}
#endif
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, GetModuleHandle(NULL), NULL);
}
Trace::EnableVideoConference(true);
}
void VideoConferenceModule::unmuteAll()
{
if (getVirtualCameraMuteState())
{
reverseVirtualCameraMuteState();
}
if (getMicrophoneMuteState())
{
reverseMicrophoneMute();
}
}
void VideoConferenceModule::muteAll()
{
if (!getVirtualCameraMuteState())
{
reverseVirtualCameraMuteState();
}
if (!getMicrophoneMuteState())
{
reverseMicrophoneMute();
}
}
void VideoConferenceModule::disable()
{
if (_enabled)
{
_generalSettingsWatcher.reset();
_moduleSettingsWatcher.reset();
toggleProxyCamRegistration(false);
if (hook_handle)
{
bool success = UnhookWindowsHookEx(hook_handle);
if (success)
{
hook_handle = nullptr;
}
}
if (getVirtualCameraMuteState())
{
reverseVirtualCameraMuteState();
}
toolbar.hide();
_enabled = false;
}
Trace::EnableVideoConference(false);
}
bool VideoConferenceModule::is_enabled()
{
return _enabled;
}
void VideoConferenceModule::destroy()
{
delete this;
instance = nullptr;
}
bool VideoConferenceModule::is_enabled_by_default() const
{
return false;
}
void VideoConferenceModule::sendSourceCameraNameUpdate()
{
if (!_settingsUpdateChannel.has_value() || settings.selectedCamera.empty())
{
return;
}
_settingsUpdateChannel->access([](auto memory) {
auto updatesChannel = reinterpret_cast<CameraSettingsUpdateChannel*>(memory._data);
updatesChannel->sourceCameraName.emplace();
std::copy(begin(settings.selectedCamera), end(settings.selectedCamera), begin(*updatesChannel->sourceCameraName));
if (settings.startupAction == L"Unmute")
{
updatesChannel->useOverlayImage = false;
}
else if (settings.startupAction == L"Mute")
{
updatesChannel->useOverlayImage = true;
}
});
}
void VideoConferenceModule::sendOverlayImageUpdate()
{
if (!_settingsUpdateChannel.has_value())
{
return;
}
_imageOverlayChannel.reset();
wchar_t powertoysDirectory[MAX_PATH + 1];
DWORD length = GetModuleFileNameW(nullptr, powertoysDirectory, MAX_PATH);
PathRemoveFileSpecW(powertoysDirectory);
std::wstring blankImagePath(powertoysDirectory);
blankImagePath += L"\\Assets\\VCM\\black.bmp";
_imageOverlayChannel = SerializedSharedMemory::create_readonly(CameraOverlayImageChannel::endpoint(),
settings.imageOverlayPath != L"" ? settings.imageOverlayPath : blankImagePath);
const auto imageSize = static_cast<uint32_t>(_imageOverlayChannel->size());
_settingsUpdateChannel->access([imageSize](auto memory) {
auto updatesChannel = reinterpret_cast<CameraSettingsUpdateChannel*>(memory._data);
updatesChannel->overlayImageSize.emplace(imageSize);
updatesChannel->newOverlayImagePosted = true;
});
}

Просмотреть файл

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

Просмотреть файл

@ -1,40 +0,0 @@
#include <windows.h>
#include "resource.h"
#include "../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -1,13 +0,0 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by VideoConferenceModule.rc
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys VideoConferenceMute Module"
#define INTERNAL_NAME "PowerToys.VideoConferenceModule"
#define ORIGINAL_FILENAME "PowerToys.VideoConferenceModule.dll"
// Non-localizable
//////////////////////////////

Просмотреть файл

@ -1,60 +0,0 @@
#include "pch.h"
#include "trace.h"
#include <common/Telemetry/TraceBase.h>
TRACELOGGING_DEFINE_PROVIDER(
g_hProvider,
"Microsoft.PowerToys",
// {38e8889b-9731-53f5-e901-e8a7c1753074}
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
TraceLoggingOptionProjectTelemetry());
// Log if the user has VCM enabled or disabled
void Trace::EnableVideoConference(const bool enabled) noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"VideoConference_EnableVideoConference",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
TraceLoggingBoolean(enabled, "Enabled"));
}
void Trace::SettingsChanged(const struct VideoConferenceSettings& settings) noexcept
{
bool CustomOverlayImage = (settings.imageOverlayPath.length() > 0);
TraceLoggingWriteWrapper(
g_hProvider,
"VideoConference_SettingsChanged",
TraceLoggingWideString(settings.toolbarPositionString.c_str(), "ToolbarPosition"),
TraceLoggingWideString(settings.toolbarMonitorString.c_str(), "ToolbarMonitorSelection"),
TraceLoggingBool(CustomOverlayImage, "CustomImageOverlayUsed"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::MicrophoneMuted() noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"VideoConference_MicrophoneMuted",
TraceLoggingBoolean(true, "MicrophoneMuted"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}
void Trace::CameraMuted() noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"VideoConference_CameraMuted",
TraceLoggingBoolean(true, "CameraMuted"),
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

Просмотреть файл

@ -1,13 +0,0 @@
#pragma once
#include "VideoConferenceModule.h"
#include <common/Telemetry/TraceBase.h>
class Trace : public telemetry::TraceBase
{
public:
static void EnableVideoConference(const bool enabled) noexcept;
static void SettingsChanged(const struct VideoConferenceSettings &settings) noexcept;
static void MicrophoneMuted() noexcept;
static void CameraMuted() noexcept;
};

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -1,124 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="VideoCaptureProxyFilter" xml:space="preserve">
<value>VideoCaptureProxyFilter</value>
<comment>do not loc, product name</comment>
</data>
</root>

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

@ -1,47 +0,0 @@
#ifdef _WIN64
#include "resource.h"
#else
#include "resource.x86.h"
#endif
#include <windows.h>
#include "../../../common/version/version.h"
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
1 VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0" // US English (0x0409), Unicode (0x04B0) charset
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200 // US English (0x0409), Unicode (1200) charset
END
END

Просмотреть файл

@ -1,157 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<ProjectGuid>{AC2857B4-103D-4D6D-9740-926EBF785042}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>VideoConferenceProxyFilter</RootNamespace>
<ProjectName>VideoConferenceProxyFilter</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)'=='Debug'">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<PropertyGroup Label="Configuration" Condition="'$(Configuration)'=='Release'">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;$(ProjectDir)..\..\..\</AdditionalIncludeDirectories>
<WarningLevel>Level4</WarningLevel>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)\src\;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
</Link>
<Lib>
<TreatLibWarningAsErrors>true</TreatLibWarningAsErrors>
</Lib>
<PreBuildEvent Condition="'$(Platform)'!='Win32'">
<Command>call "$(ProjectDir)build_vcm_x86.cmd"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<PropertyGroup Condition="'$(Platform)'!='Win32'">
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\</OutDir>
<TargetName>PowerToys.VideoConferenceProxyFilter_$(Platform)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Platform)'=='Win32'">
<OutDir>..\..\..\..\x86\$(Configuration)\</OutDir>
<TargetName>PowerToys.VideoConferenceProxyFilter_x86</TargetName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\VideoConferenceShared\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_LIB;NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MultiThreaded</RuntimeLibrary>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<AdditionalDependencies>$(OutDir)VideoConferenceShared.lib;Windowscodecs.lib;Wtsapi32.lib;mfplat.lib;WindowsApp.lib;Mfsensorgroup.lib;Mf.lib;Shlwapi.lib;Strmiids.lib;%(AdditionalDependencies);</AdditionalDependencies>
<ModuleDefinitionFile>module.def</ModuleDefinitionFile>
</Link>
<ResourceCompile>
<PreprocessorDefinitions Condition="'$(Platform)'!='Win32'">_WIN64;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="DirectShowUtils.h" />
<ClInclude Include="VideoCaptureDevice.h" />
<ClInclude Include="VideoCaptureProxyFilter.h" />
<ClInclude Include="Generated Files/resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="DirectShowUtils.cpp" />
<ClCompile Include="VideoCaptureDevice.cpp" />
<ClCompile Include="VideoCaptureProxyFilter.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="ImageLoading.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="module.def" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
<ClInclude Include="resource.x86.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="VideoConferenceProxyFilter.rc" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\VideoConferenceShared\VideoConferenceShared.vcxproj">
<Project>{459e0768-7ebd-4c41-bba1-6db3b3815e0a}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
</ProjectReference>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\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.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

Просмотреть файл

@ -1,51 +0,0 @@

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

Просмотреть файл

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

Просмотреть файл

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

Просмотреть файл

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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше