[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
|
@ -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:
|
||||
|
|
|
@ -23,7 +23,6 @@ registrypreview
|
|||
rooler
|
||||
scoobe
|
||||
shortcutguide
|
||||
videoconference
|
||||
|
||||
|
||||
# USERS
|
||||
|
|
|
@ -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, ¤t_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, ¤t_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, ®Filter));
|
||||
}
|
||||
|
||||
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
|