├── .gitattributes
├── .gitignore
├── DesktopRecorderLibrary.sln
├── LICENSE
├── SampleApp
├── Resource.rc
├── SampleApp.vcxproj
├── SampleApp.vcxproj.filters
├── WindowFactory.h
├── icon.ico
├── main.cpp
├── packages.config
├── pch.cpp
├── pch.h
└── resource.h
├── VideoLibrary
├── AsyncMediaSourceReader.cpp
├── AsyncMediaSourceReader.h
├── AudioMedia.cpp
├── AudioMedia.h
├── CaptureFrameStep.cpp
├── CaptureFrameStep.h
├── DesktopMonitor.cpp
├── DesktopMonitor.h
├── DesktopPointer.cpp
├── DesktopPointer.h
├── DisplayAdapter.cpp
├── DisplayAdapter.h
├── DxMultithread.h
├── DxResource.cpp
├── DxResource.h
├── EncodingContext.h
├── Errors.cpp
├── Errors.h
├── Frame.cpp
├── Frame.h
├── KeyedMutexLock.h
├── Pipeline.cpp
├── Pipeline.h
├── PixelShader.hlsl
├── RecordingStep.cpp
├── RecordingStep.h
├── RenderDirtyRectsStep.cpp
├── RenderDirtyRectsStep.h
├── RenderMoveRectsStep.cpp
├── RenderMoveRectsStep.h
├── RenderPointerTextureStep.cpp
├── RenderPointerTextureStep.h
├── ScreenDuplicator.cpp
├── ScreenDuplicator.h
├── ScreenMediaSinkWriter.cpp
├── ScreenMediaSinkWriter.h
├── ShaderCache.cpp
├── ShaderCache.h
├── SharedSurface.cpp
├── SharedSurface.h
├── SourceState.h
├── TexturePool.cpp
├── TexturePool.h
├── TextureToMediaSampleStep.cpp
├── TextureToMediaSampleStep.h
├── Vertex.h
├── VertexShader.hlsl
├── VideoLibrary.vcxproj
├── VideoLibrary.vcxproj.filters
├── VirtualDesktop.cpp
├── VirtualDesktop.h
├── packages.config
├── pch.cpp
└── pch.h
├── VideoLibraryTests
├── AudioTests.cpp
├── DesktopMonitorTests.cpp
├── RecordingStepsTests.cpp
├── VideoLibraryTests.vcxproj
├── VideoLibraryTests.vcxproj.filters
├── packages.config
├── stdafx.cpp
├── stdafx.h
└── targetver.h
└── readme.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 | [Ll]og/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | project.fragment.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 | *.VC.VC.opendb
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 | # TODO: Comment the next line if you want to checkin your web deploy settings
145 | # but database connection strings (with potential passwords) will be unencrypted
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
150 | # checkin your Azure Web App publish settings, but sensitive information contained
151 | # in these scripts will be unencrypted
152 | PublishScripts/
153 |
154 | # NuGet Packages
155 | *.nupkg
156 | # The packages folder can be ignored because of Package Restore
157 | **/packages/*
158 | # except build/, which is used as an MSBuild target.
159 | !**/packages/build/
160 | # Uncomment if necessary however generally it will be regenerated when needed
161 | #!**/packages/repositories.config
162 | # NuGet v3's project.json files produces more ignoreable files
163 | *.nuget.props
164 | *.nuget.targets
165 |
166 | # Microsoft Azure Build Output
167 | csx/
168 | *.build.csdef
169 |
170 | # Microsoft Azure Emulator
171 | ecf/
172 | rcf/
173 |
174 | # Windows Store app package directories and files
175 | AppPackages/
176 | BundleArtifacts/
177 | Package.StoreAssociation.xml
178 | _pkginfo.txt
179 |
180 | # Visual Studio cache files
181 | # files ending in .cache can be ignored
182 | *.[Cc]ache
183 | # but keep track of directories ending in .cache
184 | !*.[Cc]ache/
185 |
186 | # Others
187 | ClientBin/
188 | ~$*
189 | *~
190 | *.dbmdl
191 | *.dbproj.schemaview
192 | *.jfm
193 | *.pfx
194 | *.publishsettings
195 | node_modules/
196 | orleans.codegen.cs
197 |
198 | # Since there are multiple workflows, uncomment next line to ignore bower_components
199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
200 | #bower_components/
201 |
202 | # RIA/Silverlight projects
203 | Generated_Code/
204 |
205 | # Backup & report files from converting an old project file
206 | # to a newer Visual Studio version. Backup files are not needed,
207 | # because we have git ;-)
208 | _UpgradeReport_Files/
209 | Backup*/
210 | UpgradeLog*.XML
211 | UpgradeLog*.htm
212 |
213 | # SQL Server files
214 | *.mdf
215 | *.ldf
216 |
217 | # Business Intelligence projects
218 | *.rdl.data
219 | *.bim.layout
220 | *.bim_*.settings
221 |
222 | # Microsoft Fakes
223 | FakesAssemblies/
224 |
225 | # GhostDoc plugin setting file
226 | *.GhostDoc.xml
227 |
228 | # Node.js Tools for Visual Studio
229 | .ntvs_analysis.dat
230 |
231 | # Visual Studio 6 build log
232 | *.plg
233 |
234 | # Visual Studio 6 workspace options file
235 | *.opt
236 |
237 | # Visual Studio LightSwitch build output
238 | **/*.HTMLClient/GeneratedArtifacts
239 | **/*.DesktopClient/GeneratedArtifacts
240 | **/*.DesktopClient/ModelManifest.xml
241 | **/*.Server/GeneratedArtifacts
242 | **/*.Server/ModelManifest.xml
243 | _Pvt_Extensions
244 |
245 | # Paket dependency manager
246 | .paket/paket.exe
247 | paket-files/
248 |
249 | # FAKE - F# Make
250 | .fake/
251 |
252 | # JetBrains Rider
253 | .idea/
254 | *.sln.iml
255 |
256 | # CodeRush
257 | .cr/
258 |
259 | # Python Tools for Visual Studio (PTVS)
260 | __pycache__/
261 | *.pyc
262 |
263 | **/Generated Files/**
264 |
265 | **/dist/**
266 | **/package-lock.json
267 | **/*.appxbundle
268 | *.mp4
269 |
270 | **/VertexShader.h
271 | **/PixelShader.h
--------------------------------------------------------------------------------
/DesktopRecorderLibrary.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32630.192
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SampleApp", "SampleApp\SampleApp.vcxproj", "{9EDDC606-86CE-49A2-9174-7CEA60F26405}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoLibrary", "VideoLibrary\VideoLibrary.vcxproj", "{3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}"
9 | EndProject
10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "VideoLibraryTests", "VideoLibraryTests\VideoLibraryTests.vcxproj", "{348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B4B5EDA1-4563-4501-8F0A-BDA59882F9EF}"
13 | ProjectSection(SolutionItems) = preProject
14 | readme.md = readme.md
15 | EndProjectSection
16 | EndProject
17 | Global
18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
19 | Debug|x64 = Debug|x64
20 | Debug|x86 = Debug|x86
21 | Release|x64 = Release|x64
22 | Release|x86 = Release|x86
23 | EndGlobalSection
24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
25 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Debug|x64.ActiveCfg = Debug|x64
26 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Debug|x64.Build.0 = Debug|x64
27 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Debug|x86.ActiveCfg = Debug|Win32
28 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Debug|x86.Build.0 = Debug|Win32
29 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Release|x64.ActiveCfg = Release|x64
30 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Release|x64.Build.0 = Release|x64
31 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Release|x86.ActiveCfg = Release|Win32
32 | {9EDDC606-86CE-49A2-9174-7CEA60F26405}.Release|x86.Build.0 = Release|Win32
33 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Debug|x64.ActiveCfg = Debug|x64
34 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Debug|x64.Build.0 = Debug|x64
35 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Debug|x86.ActiveCfg = Debug|Win32
36 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Debug|x86.Build.0 = Debug|Win32
37 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Release|x64.ActiveCfg = Release|x64
38 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Release|x64.Build.0 = Release|x64
39 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Release|x86.ActiveCfg = Release|Win32
40 | {3CE469F1-0BE6-4AAD-ABD3-E49A9E270CD0}.Release|x86.Build.0 = Release|Win32
41 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Debug|x64.ActiveCfg = Debug|x64
42 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Debug|x64.Build.0 = Debug|x64
43 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Debug|x86.ActiveCfg = Debug|Win32
44 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Debug|x86.Build.0 = Debug|Win32
45 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Release|x64.ActiveCfg = Release|x64
46 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Release|x64.Build.0 = Release|x64
47 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Release|x86.ActiveCfg = Release|Win32
48 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A}.Release|x86.Build.0 = Release|Win32
49 | EndGlobalSection
50 | GlobalSection(SolutionProperties) = preSolution
51 | HideSolutionNode = FALSE
52 | EndGlobalSection
53 | GlobalSection(ExtensibilityGlobals) = postSolution
54 | SolutionGuid = {E1FCAAB2-181D-4A98-9DBA-33441BD5A07F}
55 | EndGlobalSection
56 | EndGlobal
57 |
--------------------------------------------------------------------------------
/SampleApp/Resource.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgcoded/DesktopRecorderLibrary/a33dbb294bdaca75a775fd6aac5f9c0cf48bd743/SampleApp/Resource.rc
--------------------------------------------------------------------------------
/SampleApp/SampleApp.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | true
6 | $(RequiredBundles);
7 | true
8 | 15.0
9 | {9eddc606-86ce-49a2-9174-7cea60f26405}
10 | Win32Proj
11 | SampleApp
12 | 10.0
13 | 10.0.17134.0
14 | SampleApp
15 |
16 |
17 |
18 |
19 | Debug
20 | Win32
21 |
22 |
23 | Release
24 | Win32
25 |
26 |
27 | Debug
28 | x64
29 |
30 |
31 | Release
32 | x64
33 |
34 |
35 |
36 | Application
37 | v143
38 | Unicode
39 |
40 |
41 | true
42 | true
43 |
44 |
45 | false
46 | true
47 | false
48 |
49 |
50 | false
51 |
52 |
53 | false
54 |
55 |
56 | false
57 |
58 |
59 | false
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | Use
73 | pch.h
74 | $(IntDir)pch.pch
75 | _CONSOLE;%(PreprocessorDefinitions)
76 | Level4
77 | %(AdditionalOptions) /permissive- /bigobj
78 |
79 |
80 |
81 |
82 | Disabled
83 | _DEBUG;%(PreprocessorDefinitions)
84 | $(SolutionDir);%(AdditionalIncludeDirectories)
85 |
86 |
87 | Windows
88 | false
89 | mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;user32.lib;gdi32.lib
90 | %(AdditionalLibraryDirectories)
91 |
92 |
93 |
94 |
95 |
96 |
97 | PerMonitorHighDPIAware
98 |
99 |
100 |
101 |
102 | WIN32;%(PreprocessorDefinitions)
103 | $(SolutionDir);%(AdditionalIncludeDirectories)
104 |
105 |
106 | PerMonitorHighDPIAware
107 |
108 |
109 | mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;user32.lib;gdi32.lib
110 |
111 |
112 |
113 |
114 | MaxSpeed
115 | true
116 | true
117 | NDEBUG;%(PreprocessorDefinitions)
118 | $(SolutionDir);%(AdditionalIncludeDirectories)
119 | $(SolutionDir);%(AdditionalIncludeDirectories)
120 |
121 |
122 | Windows
123 | true
124 | true
125 | false
126 | mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;user32.lib;gdi32.lib
127 | mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;user32.lib;gdi32.lib
128 |
129 |
130 |
131 |
132 |
133 |
134 | PerMonitorHighDPIAware
135 |
136 |
137 | PerMonitorHighDPIAware
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 | Create
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 | {3ce469f1-0be6-4aad-abd3-e49a9e270cd0}
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 | 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}.
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/SampleApp/SampleApp.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 | Header Files
26 |
27 |
28 |
29 |
30 | Source Files
31 |
32 |
33 | Source Files
34 |
35 |
36 |
37 |
38 | Resource Files
39 |
40 |
41 |
42 |
43 | Resource Files
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/SampleApp/WindowFactory.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define WIN32_LEAN_AND_MEAN
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | using EventCallback = std::function;
12 |
13 | class WindowInterface
14 | {
15 | public:
16 | virtual void Size(int width, int height) const = 0;
17 | virtual void Position(int x, int y) const = 0;
18 | virtual void Button(LPCWSTR text, int x, int y, int width, int height, EventCallback cb) = 0;
19 | virtual void Close() = 0;
20 | virtual bool Closed() const = 0;
21 | virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) = 0;
22 | };
23 |
24 | class Window : public WindowInterface
25 | {
26 | public:
27 | Window()
28 | : mHwnd{ nullptr }
29 | , mControlId{ 100 }
30 | , mClosed{ false }
31 | {
32 | }
33 |
34 | virtual ~Window()
35 | {
36 | SetWindowLongPtr(Handle(), GWLP_USERDATA, (LONG_PTR)nullptr);
37 |
38 | if (!Closed())
39 | {
40 | Closed(true);
41 | Close();
42 | }
43 | }
44 |
45 | virtual void Size(int width, int height) const override
46 | {
47 | SetWindowPos(mHwnd, nullptr, 0, 0, width, height, SWP_NOMOVE | SWP_FRAMECHANGED);
48 | }
49 |
50 | virtual void Position(int x, int y) const override
51 | {
52 | SetWindowPos(mHwnd, nullptr, x, y, 0, 0, SWP_NOSIZE | SWP_FRAMECHANGED);
53 | }
54 |
55 | virtual void SizeAndPosition(int newX, int newY, int newWidth, int newHeight)
56 | {
57 | SetWindowPos(mHwnd, nullptr, newX, newY, newWidth, newHeight, SWP_FRAMECHANGED);
58 | }
59 |
60 | virtual void Button(LPCWSTR text, int x, int y, int width, int height, EventCallback cb)
61 | {
62 | // https://docs.microsoft.com/en-us/windows/win32/controls/create-a-button
63 | auto id = mControlId++;
64 | mCallbacks.insert(std::make_pair(id, cb));
65 | (void)CreateWindow(
66 | WC_BUTTON, // Predefined class; Unicode assumed
67 | text, // Button text
68 | WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
69 | x, // x position
70 | y, // y position
71 | width, // Button width
72 | height, // Button height
73 | mHwnd, // Parent window
74 | (HMENU)id,
75 | (HINSTANCE)GetWindowLongPtr(mHwnd, GWLP_HINSTANCE),
76 | nullptr); // Pointer not needed.
77 | }
78 |
79 | virtual void Close() override
80 | {
81 | CloseWindow(mHwnd);
82 | }
83 |
84 | virtual bool Closed() const override
85 | {
86 | return mClosed;
87 | }
88 |
89 | protected:
90 |
91 | virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) override
92 | {
93 | switch (msg)
94 | {
95 | case WM_CREATE:
96 | {
97 | Handle(hwnd);
98 | ShowWindow(hwnd, SW_SHOWDEFAULT);
99 | return (LRESULT)nullptr;
100 | }
101 |
102 | case WM_CLOSE:
103 | {
104 | Closed(true);
105 | DestroyWindow(hwnd);
106 | return 0;
107 | }
108 |
109 | // https://docs.microsoft.com/en-us/windows/win32/menurc/wm-command
110 | case WM_COMMAND:
111 | {
112 | auto code = HIWORD(wparam);
113 |
114 | switch (code)
115 | {
116 | // https://docs.microsoft.com/en-us/windows/win32/controls/bn-clicked
117 | case BN_CLICKED:
118 | {
119 | long id = LOWORD(wparam);
120 | auto button = (HWND)lparam;
121 | auto cb = mCallbacks.find(id);
122 | if (cb != mCallbacks.end())
123 | cb->second(button);
124 |
125 | break;
126 | }
127 |
128 | default:
129 | break;
130 | }
131 | break;
132 | }
133 | default:
134 | break;
135 | }
136 |
137 | return DefWindowProc(hwnd, msg, wparam, lparam);
138 | }
139 |
140 | protected:
141 | HWND Handle() const
142 | {
143 | return mHwnd;
144 | }
145 |
146 | void Handle(HWND hwnd)
147 | {
148 | mHwnd = hwnd;
149 | }
150 |
151 | void Closed(bool closed)
152 | {
153 | mClosed = closed;
154 | }
155 |
156 | private:
157 | volatile long long mControlId;
158 | std::unordered_map mCallbacks;
159 | bool mClosed;
160 | HWND mHwnd;
161 |
162 | };
163 |
164 | class BorderWindow : public Window
165 | {
166 | const int BorderWidth = 10;
167 |
168 | public:
169 | virtual ~BorderWindow()
170 | {
171 | if (!Closed())
172 | {
173 | Close();
174 | }
175 | }
176 |
177 | protected:
178 |
179 | virtual LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) override
180 | {
181 | switch (msg)
182 | {
183 | case WM_CREATE:
184 | Handle(hwnd);
185 |
186 | // https://docs.microsoft.com/en-us/windows/win32/winmsg/using-windows#using-layered-windows
187 | // https://docs.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
188 | SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_NOACTIVATE | WS_EX_TRANSPARENT | WS_EX_TOPMOST);
189 | SetLayeredWindowAttributes(
190 | hwnd,
191 | RGB(0, 0, 0),
192 | 128,
193 | LWA_COLORKEY | LWA_ALPHA);
194 |
195 | SetWindowDisplayAffinity(hwnd, WDA_EXCLUDEFROMCAPTURE);
196 | SetWindowLong(hwnd, GWL_STYLE, 0); // Without 1 point border = white rectangle
197 | SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED);
198 | ShowWindow(hwnd, SW_SHOWDEFAULT);
199 | break;
200 | case WM_MOVE:
201 | case WM_SIZE:
202 | Repaint();
203 | break;
204 | default:
205 | return Window::WndProc(hwnd, msg, wparam, lparam);
206 | }
207 |
208 | return (LRESULT)nullptr;
209 | }
210 |
211 | virtual void Close() override
212 | {
213 | Closed(true);
214 | DestroyWindow(Handle());
215 | }
216 |
217 | private:
218 | void Repaint()
219 | {
220 | RECT rect;
221 | GetClientRect(Handle(), &rect);
222 |
223 | if (rect.left == 0 &&
224 | rect.right == 0 &&
225 | rect.top == 0 &&
226 | rect.bottom == 0)
227 | {
228 | return;
229 | }
230 |
231 | // https://docs.microsoft.com/en-us/windows/win32/gdi/using-filled-shapes
232 | PAINTSTRUCT ps;
233 |
234 | BeginPaint(Handle(), &ps);
235 |
236 | SelectObject(ps.hdc, GetStockObject(NULL_PEN));
237 |
238 | HBRUSH eraser = CreateSolidBrush(RGB(0, 0, 0));
239 | HBRUSH brush = CreateSolidBrush(RGB(255, 0, 0));
240 |
241 | // erase
242 | SelectObject(ps.hdc, eraser);
243 | Rectangle(ps.hdc, rect.left, rect.top, rect.right, rect.bottom);
244 |
245 | // borders
246 | SelectObject(ps.hdc, brush);
247 |
248 | // Top Border
249 | Rectangle(ps.hdc, rect.left, rect.top, rect.right, BorderWidth);
250 |
251 | // Left Border
252 | Rectangle(ps.hdc, rect.left, rect.top, BorderWidth, rect.bottom);
253 |
254 | // Bottom Border
255 | Rectangle(ps.hdc, rect.left, rect.bottom - BorderWidth, rect.right, rect.bottom);
256 |
257 | // Right Border
258 | Rectangle(ps.hdc, rect.right - BorderWidth, rect.top, rect.right, rect.bottom);
259 |
260 | DeleteObject(brush);
261 | DeleteObject(eraser);
262 | EndPaint(Handle(), &ps);
263 | }
264 | };
265 |
266 | template
267 | class WindowFactory
268 | {
269 | public:
270 |
271 | static WindowFactory Create(HINSTANCE hInstance)
272 | {
273 | return WindowFactory{ hInstance };
274 | }
275 |
276 | std::unique_ptr NewWindow(Args&&... args)
277 | {
278 | auto newWindow = std::make_unique(std::forward(args)...);
279 | HWND windowHandle = CreateWindowHandle(newWindow.get());
280 | winrt::check_pointer(windowHandle);
281 | return std::move(newWindow);
282 | }
283 |
284 | private:
285 |
286 | HWND CreateWindowHandle(WindowInterface* windowInterface)
287 | {
288 | return CreateWindowEx(
289 | 0, // Optional window styles.
290 | mWindowClassName.c_str(), // Window class
291 | L"Window", // Window title
292 | WS_OVERLAPPEDWINDOW, // Window style
293 |
294 | // Size and position
295 | CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
296 |
297 | NULL, // Parent window
298 | NULL, // Menu
299 | mInstanceHandle, // Instance handle
300 | windowInterface // Additional application data
301 | );
302 | }
303 |
304 | static LRESULT WndProcDispatcher(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
305 | {
306 | if (msg == WM_CREATE)
307 | {
308 | auto createStruct = (LPCREATESTRUCT)lparam;
309 | winrt::check_pointer(createStruct);
310 | SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)createStruct->lpCreateParams);
311 | }
312 |
313 | auto window = (WindowInterface*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
314 | if (window == nullptr)
315 | {
316 | return DefWindowProc(hwnd, msg, wparam, lparam);
317 | }
318 |
319 | return window->WndProc(hwnd, msg, wparam, lparam);
320 | }
321 |
322 | WindowFactory(HINSTANCE hInstance)
323 | : mInstanceHandle{ hInstance }
324 | , mWindowClass{ }
325 | {
326 | mWindowClass.hInstance = hInstance;
327 | mWindowClass.lpfnWndProc = &WindowFactory::WndProcDispatcher;
328 |
329 | std::wstringstream wss;
330 | wss << typeid(T).raw_name();
331 | mWindowClassName = wss.str();
332 | LPCWSTR lpcClassName = mWindowClassName.c_str();
333 |
334 | mWindowClass.lpszClassName = lpcClassName;
335 | RegisterClass(&mWindowClass);
336 | }
337 |
338 | std::wstring mWindowClassName;
339 | WNDCLASS mWindowClass;
340 | const HINSTANCE mInstanceHandle;
341 | };
342 |
--------------------------------------------------------------------------------
/SampleApp/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jgcoded/DesktopRecorderLibrary/a33dbb294bdaca75a775fd6aac5f9c0cf48bd743/SampleApp/icon.ico
--------------------------------------------------------------------------------
/SampleApp/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SampleApp/pch.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 |
--------------------------------------------------------------------------------
/SampleApp/pch.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | //
21 | // pch.h
22 | // Precompiled header for commonly included header files
23 | //
24 |
25 | #pragma once
26 |
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include
32 | #include
33 |
34 | #include
35 | #include
36 | #include
37 |
38 | #include
39 | #include
40 | #include
41 | #include
42 | #include
43 | #include
44 | #include
45 |
46 | #include
47 | #include
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 |
55 | #include "VideoLibrary\VirtualDesktop.h"
56 | #include "VideoLibrary\Pipeline.h"
57 | #include "VideoLibrary\AsyncMediaSourceReader.h"
58 | #include "VideoLibrary\ScreenMediaSinkWriter.h"
59 | #include "VideoLibrary\AudioMedia.h"
60 | #include "VideoLibrary\Errors.h"
61 |
62 | #include "WindowFactory.h"
63 |
--------------------------------------------------------------------------------
/SampleApp/resource.h:
--------------------------------------------------------------------------------
1 | //{{NO_DEPENDENCIES}}
2 | // Microsoft Visual C++ generated include file.
3 | // Used by Resource.rc
4 | //
5 | #define IDI_ICON1 102
6 |
7 | // Next default values for new objects
8 | //
9 | #ifdef APSTUDIO_INVOKED
10 | #ifndef APSTUDIO_READONLY_SYMBOLS
11 | #define _APS_NEXT_RESOURCE_VALUE 106
12 | #define _APS_NEXT_COMMAND_VALUE 40001
13 | #define _APS_NEXT_CONTROL_VALUE 1002
14 | #define _APS_NEXT_SYMED_VALUE 101
15 | #endif
16 | #endif
17 |
--------------------------------------------------------------------------------
/VideoLibrary/AsyncMediaSourceReader.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "ScreenMediaSinkWriter.h"
22 | #include "DesktopMonitor.h"
23 | #include "AsyncMediaSourceReader.h"
24 |
25 | AsyncMediaSourceReader::AsyncMediaSourceReader(
26 | winrt::com_ptr mediaSource,
27 | std::function callback,
28 | int samplingDelayMs,
29 | DWORD streamIndex)
30 | : mSource{ mediaSource }
31 | , mCallback{ callback }
32 | , mSamplingDelayMs{ samplingDelayMs }
33 | , mStreamIndex{ streamIndex }
34 | , m_refCount{ 1 }
35 | {
36 | }
37 |
38 | AsyncMediaSourceReader::~AsyncMediaSourceReader()
39 | {
40 | assert(m_refCount == 0);
41 |
42 | if (!mStopping)
43 | {
44 | this->Stop();
45 | }
46 | }
47 |
48 | void AsyncMediaSourceReader::Start()
49 | {
50 | winrt::com_ptr attributes;
51 | winrt::check_hresult(MFCreateAttributes(attributes.put(), 1));
52 |
53 | winrt::com_ptr unk;
54 | winrt::check_hresult(this->QueryInterface(IID_PPV_ARGS(unk.put())));
55 |
56 | winrt::check_hresult(attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, unk.get()));
57 |
58 | winrt::com_ptr source = mSource.as();
59 | winrt::check_hresult(MFCreateSourceReaderFromMediaSource(source.get(), attributes.get(), mReader.put()));
60 |
61 | winrt::check_hresult(mReader->SetStreamSelection(
62 | (DWORD)MF_SOURCE_READER_ALL_STREAMS, FALSE));
63 |
64 | winrt::check_hresult(mReader->SetStreamSelection(mStreamIndex, TRUE));
65 |
66 | this->RequestSample();
67 | }
68 |
69 | void AsyncMediaSourceReader::Stop()
70 | {
71 | mStopping = true;
72 | }
73 |
74 | HRESULT __stdcall AsyncMediaSourceReader::OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample * pSample) noexcept
75 | {
76 | UNREFERENCED_PARAMETER(llTimestamp);
77 | UNREFERENCED_PARAMETER(dwStreamFlags);
78 | UNREFERENCED_PARAMETER(dwStreamIndex);
79 | UNREFERENCED_PARAMETER(dwStreamFlags);
80 |
81 | if (FAILED(hrStatus))
82 | {
83 | mCallback(nullptr, hrStatus);
84 | }
85 |
86 | try
87 | {
88 | if (mStopping)
89 | {
90 | return S_OK;
91 | }
92 |
93 | if (mCallback && pSample)
94 | {
95 | mCallback(pSample, S_OK);
96 | }
97 |
98 | Sleep(mSamplingDelayMs);
99 |
100 | if (!mStopping)
101 | {
102 | this->RequestSample();
103 | }
104 | }
105 | catch (...)
106 | {
107 | if(!mStopping)
108 | return winrt::to_hresult();
109 | }
110 |
111 | return S_OK;
112 | }
113 |
114 | inline HRESULT __stdcall AsyncMediaSourceReader::OnFlush(DWORD dwStreamIndex) noexcept
115 | {
116 | UNREFERENCED_PARAMETER(dwStreamIndex);
117 | return E_NOTIMPL;
118 | }
119 |
120 | HRESULT __stdcall AsyncMediaSourceReader::OnEvent(DWORD dwStreamIndex, IMFMediaEvent * pEvent) noexcept
121 | {
122 | UNREFERENCED_PARAMETER(dwStreamIndex);
123 | UNREFERENCED_PARAMETER(pEvent);
124 | return S_OK;
125 | }
126 |
127 | inline HRESULT __stdcall AsyncMediaSourceReader::OnTransformChange(void) noexcept
128 | {
129 | return E_NOTIMPL;
130 | }
131 |
132 | inline HRESULT __stdcall AsyncMediaSourceReader::OnStreamError(DWORD dwStreamIndex, HRESULT hrStatus) noexcept
133 | {
134 | UNREFERENCED_PARAMETER(dwStreamIndex);
135 | UNREFERENCED_PARAMETER(hrStatus);
136 | return E_NOTIMPL;
137 | }
138 |
139 | HRESULT AsyncMediaSourceReader::QueryInterface(REFIID riid, void ** ppv) noexcept
140 | {
141 | static const QITAB qit[] =
142 | {
143 | QITABENT(AsyncMediaSourceReader, IMFSourceReaderCallback),
144 | { 0 }
145 | };
146 | return QISearch(this, qit, riid, ppv);
147 | }
148 |
149 | void AsyncMediaSourceReader::RequestSample()
150 | {
151 | std::scoped_lock lock{ mMutex };
152 | winrt::check_hresult(mReader->ReadSample(mStreamIndex, 0, nullptr, nullptr, nullptr, nullptr));
153 | }
154 |
--------------------------------------------------------------------------------
/VideoLibrary/AsyncMediaSourceReader.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | class AsyncMediaSourceReader : public IMFSourceReaderCallback
23 | {
24 | public:
25 |
26 | AsyncMediaSourceReader(
27 | winrt::com_ptr mediaSource,
28 | std::function callback,
29 | int desiredFrameRate,
30 | DWORD streamIndex);
31 |
32 | virtual ~AsyncMediaSourceReader();
33 |
34 | virtual void Start();
35 | virtual void Stop();
36 | virtual void RequestSample();
37 |
38 | // Inherited via IMFSourceReaderCallback2
39 | virtual HRESULT __stdcall OnReadSample(HRESULT hrStatus, DWORD dwStreamIndex, DWORD dwStreamFlags, LONGLONG llTimestamp, IMFSample * pSample) noexcept override;
40 | virtual HRESULT __stdcall OnFlush(DWORD dwStreamIndex) noexcept override;
41 | virtual HRESULT __stdcall OnEvent(DWORD dwStreamIndex, IMFMediaEvent * pEvent) noexcept override;
42 | virtual HRESULT __stdcall OnTransformChange(void) noexcept;
43 |
44 | virtual HRESULT __stdcall OnStreamError(DWORD dwStreamIndex, HRESULT hrStatus) noexcept;
45 |
46 | STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_refCount); }
47 | STDMETHODIMP_(ULONG) Release()
48 | {
49 | assert(m_refCount > 0);
50 | ULONG uCount = InterlockedDecrement(&m_refCount);
51 | if (uCount == 0)
52 | {
53 | delete this;
54 | }
55 | return uCount;
56 | }
57 | virtual HRESULT QueryInterface(REFIID riid, void** ppv) noexcept override;
58 |
59 | private:
60 |
61 |
62 | winrt::com_ptr mSource;
63 |
64 | /*
65 | https://docs.microsoft.com/en-us/windows/desktop/medfound/mf-readwrite-enable-hardware-transforms
66 | By default, the source reader and sink writer do not use hardware decoders or encoders.
67 | To enable the use of hardware MFTs, set this attribute to TRUE
68 | when you create the source reader or sink writer.
69 |
70 | https://docs.microsoft.com/en-us/windows/desktop/medfound/source-reader-attributes
71 | https://docs.microsoft.com/en-us/windows/desktop/medfound/sink-writer-attributes
72 |
73 | There is one exception to the default behavior.
74 | The source reader and sink writer automatically use MFTs
75 | that are registered locally in the caller's process.
76 | To register an MFT locally, call MFTRegisterLocal or MFTRegisterLocalByCLSID.
77 | Hardware MFTs that are registered locally are used even if
78 | the MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS attribute is not set.
79 |
80 | see MFCaptureToFile to see how to use MFTRegisterLocalByCLSID to use Color Converter DSP
81 | */
82 | winrt::com_ptr mReader;
83 | std::atomic mStopping = false;
84 | std::mutex mMutex;
85 | int mSamplingDelayMs;
86 | DWORD mStreamIndex;
87 |
88 | //std::function)> mCallback;
89 | std::function mCallback;
90 |
91 | volatile long m_refCount;
92 | };
--------------------------------------------------------------------------------
/VideoLibrary/AudioMedia.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include
22 | #include "AudioMedia.h"
23 |
24 | std::vector AudioMedia::GetAudioRecordingDevices()
25 | {
26 | std::vector result;
27 | winrt::com_ptr attributes;
28 | winrt::check_hresult(MFCreateAttributes(attributes.put(), 1));
29 |
30 | winrt::check_hresult(attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID));
31 |
32 | IMFActivate** activationObjects = nullptr;
33 | UINT32 numObjects;
34 | winrt::check_hresult(MFEnumDeviceSources(attributes.get(), &activationObjects, &numObjects));
35 |
36 | for (UINT32 i = 0; i < numObjects; ++i) {
37 |
38 | auto activationObject = activationObjects[i];
39 |
40 | auto device = AudioMedia::GetAudioRecordingDeviceFromActivator(activationObject);
41 |
42 | if (!device.endpoint.empty()) {
43 | result.push_back(device);
44 | }
45 |
46 | activationObject->Release();
47 | activationObject = nullptr;
48 | }
49 |
50 | CoTaskMemFree(activationObjects);
51 |
52 | return result;
53 | }
54 |
55 | winrt::com_ptr AudioMedia::GetAudioMediaSourceFromEndpoint(std::wstring endpoint)
56 | {
57 | winrt::com_ptr attributes;
58 | winrt::check_hresult(MFCreateAttributes(attributes.put(), 2));
59 | winrt::check_hresult(attributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID));
60 | winrt::check_hresult(attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, endpoint.c_str()));
61 |
62 | winrt::com_ptr source;
63 | winrt::check_hresult(MFCreateDeviceSource(attributes.get(), source.put()));
64 |
65 | return source;
66 | }
67 |
68 | bool AudioMedia::IsAudioRecordingDeviceAvailable(std::wstring endpoint)
69 | {
70 | auto devices = AudioMedia::GetAudioRecordingDevices();
71 | for (const auto& device : devices) {
72 | if (device.endpoint == endpoint) {
73 | return true;
74 | }
75 | }
76 | return false;
77 | }
78 |
79 | AudioDevice AudioMedia::GetAudioRecordingDeviceFromActivator(IMFActivate* activationObject)
80 | {
81 | AudioDevice device;
82 |
83 | // get friendly name
84 | UINT32 strLength = 0;
85 | winrt::check_hresult(activationObject->GetStringLength(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &strLength));
86 |
87 | device.friendlyName = std::wstring(strLength, '\0');
88 | winrt::check_hresult(activationObject->GetString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, (LPWSTR)device.friendlyName.data(), (UINT32)device.friendlyName.size() + 1, nullptr));
89 |
90 | // get endpoint
91 | winrt::check_hresult(activationObject->GetStringLength(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, &strLength));
92 |
93 | device.endpoint = std::wstring(strLength, '\0');
94 | winrt::check_hresult(activationObject->GetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, (LPWSTR)device.endpoint.data(), (UINT32)device.endpoint.size() + 1, nullptr));
95 |
96 | return device;
97 | }
98 |
--------------------------------------------------------------------------------
/VideoLibrary/AudioMedia.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | struct AudioDevice
23 | {
24 | std::wstring friendlyName;
25 | std::wstring endpoint;
26 | };
27 |
28 | class AudioMedia
29 | {
30 |
31 | public:
32 |
33 | static std::vector GetAudioRecordingDevices();
34 |
35 | static winrt::com_ptr GetAudioMediaSourceFromEndpoint(std::wstring endpoint);
36 |
37 | static bool IsAudioRecordingDeviceAvailable(std::wstring endpoint);
38 |
39 | private:
40 |
41 | static AudioDevice GetAudioRecordingDeviceFromActivator(IMFActivate* activationObject);
42 | };
43 |
--------------------------------------------------------------------------------
/VideoLibrary/CaptureFrameStep.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "CaptureFrameStep.h"
22 |
23 | CaptureFrameStep::CaptureFrameStep(ScreenDuplicator& duplicator)
24 | : mDupl{ duplicator }
25 | {
26 | }
27 |
28 | CaptureFrameStep::~CaptureFrameStep()
29 | {
30 | }
31 |
32 | void CaptureFrameStep::Perform()
33 | {
34 | mFrame = std::make_shared(mDupl);
35 | }
36 |
37 | std::shared_ptr CaptureFrameStep::Result()
38 | {
39 | return mFrame;
40 | }
41 |
--------------------------------------------------------------------------------
/VideoLibrary/CaptureFrameStep.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "RecordingStep.h"
23 | #include "DesktopMonitor.h"
24 | #include "ScreenDuplicator.h"
25 | #include "Frame.h"
26 |
27 | class CaptureFrameStep :
28 | public RecordingStep
29 | {
30 | public:
31 | CaptureFrameStep(ScreenDuplicator& duplicator);
32 | virtual ~CaptureFrameStep();
33 |
34 | virtual void Perform() override;
35 |
36 | std::shared_ptr Result();
37 |
38 | private:
39 | ScreenDuplicator& mDupl;
40 | std::shared_ptr mFrame;
41 | };
42 |
--------------------------------------------------------------------------------
/VideoLibrary/DesktopMonitor.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DesktopMonitor.h"
22 |
23 | DesktopMonitor::DesktopMonitor(
24 | std::shared_ptr displayAdapter,
25 | winrt::com_ptr output,
26 | int outputIndex)
27 | : mDisplayAdapter{ displayAdapter }
28 | , mOutput{ output }
29 | , mOutputIndex{ outputIndex }
30 | {
31 | DXGI_OUTPUT_DESC outputDesc;
32 | winrt::check_hresult(mOutput->GetDesc(&outputDesc));
33 |
34 | DISPLAY_DEVICE displayDevice;
35 | displayDevice.cb = sizeof(DISPLAY_DEVICE);
36 | winrt::check_bool(EnumDisplayDevices(outputDesc.DeviceName, 0, &displayDevice, 0));
37 |
38 | mOutputName = displayDevice.DeviceString;
39 | mDesktopMonitorBounds = outputDesc.DesktopCoordinates;
40 | mRotation = outputDesc.Rotation;
41 | }
42 |
--------------------------------------------------------------------------------
/VideoLibrary/DesktopMonitor.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 | #include "DisplayAdapter.h"
22 | #include "DesktopPointer.h"
23 |
24 | class DesktopMonitor
25 | {
26 | public:
27 |
28 | DesktopMonitor(
29 | std::shared_ptr displayAdapter,
30 | winrt::com_ptr output,
31 | int outputIndex);
32 |
33 | std::wstring OutputName() const { return mOutputName; }
34 |
35 | RECT DesktopMonitorBounds() const { return mDesktopMonitorBounds; }
36 |
37 | DXGI_MODE_ROTATION Rotation() const { return mRotation; }
38 |
39 | DisplayAdapter const& Adapter() const { return *mDisplayAdapter; }
40 |
41 | winrt::com_ptr Output() const { return mOutput; }
42 |
43 | int OutputIndex() const { return mOutputIndex; }
44 |
45 | private:
46 |
47 | winrt::com_ptr mOutput;
48 | RECT mDesktopMonitorBounds;
49 | std::wstring mOutputName;
50 | int mOutputIndex;
51 | DXGI_MODE_ROTATION mRotation;
52 |
53 | std::shared_ptr mDisplayAdapter;
54 | };
55 |
--------------------------------------------------------------------------------
/VideoLibrary/DesktopPointer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DesktopPointer.h"
22 |
23 |
24 | DesktopPointer::DesktopPointer(RECT virtualDesktopBounds)
25 | : mIsPointerTextureStale { true }
26 | , mPointerOwnerIndex { UINT_MAX }
27 | , mLastUpdateTime { 0 }
28 | , mPosition{}
29 | , mShapeInfo{}
30 | , mVisible{ false }
31 | , mVirtualDesktopBounds{ virtualDesktopBounds }
32 | {
33 | }
34 |
35 | DesktopPointer::~DesktopPointer()
36 | {
37 | }
38 |
39 | std::size_t DesktopPointer::BufferSize() const
40 | {
41 | return mBuffer.size();
42 | }
43 |
44 | byte* DesktopPointer::PutBuffer(std::size_t requiredSize)
45 | {
46 | mIsPointerTextureStale = true;
47 |
48 | if (requiredSize > mBuffer.size()) {
49 | mBuffer.resize(requiredSize);
50 | }
51 |
52 | return mBuffer.data();
53 | }
54 |
55 | DXGI_OUTDUPL_POINTER_POSITION DesktopPointer::Position() const
56 | {
57 | return mPosition;
58 | }
59 |
60 | void DesktopPointer::UpdatePosition(
61 | DXGI_OUTDUPL_POINTER_POSITION newPosition,
62 | LARGE_INTEGER updateTime,
63 | UINT outputIndex,
64 | RECT desktopMonitorBounds)
65 | {
66 | if (updateTime.QuadPart == 0) {
67 | return;
68 | }
69 |
70 | // In a multi-monitor recording scenario, need to figure out which output is showing the most recent pointer
71 |
72 | // update pointer position if the pointer is visible or on this output
73 | bool shouldUpdatePosition =
74 | newPosition.Visible ||
75 | mPointerOwnerIndex == outputIndex;
76 |
77 | // update mouse position if two outputs have visible pointers, but this output has a more recent time
78 | shouldUpdatePosition = shouldUpdatePosition ||
79 | (newPosition.Visible &&
80 | mPosition.Visible &&
81 | mPointerOwnerIndex != outputIndex &&
82 | updateTime.QuadPart > mLastUpdateTime.QuadPart);
83 |
84 | if (shouldUpdatePosition) {
85 | newPosition.Position.x += desktopMonitorBounds.left - mVirtualDesktopBounds.left;
86 | newPosition.Position.y += desktopMonitorBounds.top - mVirtualDesktopBounds.top;
87 | mLastUpdateTime = updateTime;
88 | mPosition = newPosition;
89 | mPointerOwnerIndex = outputIndex;
90 | mVisible = newPosition.Visible != 0;
91 | }
92 | // TODO: warn if the mouse is never updated
93 | // if it is not updated, that will cause
94 | // flickering with RenderPointerTextureStep
95 | }
96 |
97 | DXGI_OUTDUPL_POINTER_SHAPE_INFO DesktopPointer::ShapeInfo() const
98 | {
99 | return mShapeInfo;
100 | }
101 |
102 | void DesktopPointer::ShapeInfo(DXGI_OUTDUPL_POINTER_SHAPE_INFO newShapeInfo)
103 | {
104 | mShapeInfo = newShapeInfo;
105 | }
106 |
107 | void DesktopPointer::UpdateTexture(winrt::com_ptr const& newImage)
108 | {
109 | if (mPointerTexture) {
110 | mPointerTexture = nullptr;
111 | }
112 | mPointerTexture = newImage;
113 | mIsPointerTextureStale = false;
114 | }
115 |
116 | winrt::com_ptr DesktopPointer::Texture() const
117 | {
118 | return mPointerTexture;
119 | }
120 |
121 | bool DesktopPointer::Visible() const
122 | {
123 | return mVisible;
124 | }
125 |
--------------------------------------------------------------------------------
/VideoLibrary/DesktopPointer.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | class DesktopPointer
23 | {
24 | public:
25 | DesktopPointer(RECT virtualDesktopBounds);
26 | virtual ~DesktopPointer();
27 |
28 | std::size_t BufferSize() const;
29 |
30 | byte* PutBuffer(std::size_t requiredSize = 0);
31 |
32 | /*
33 | TODO in a multi monitor recording scenario, need to offset the Position based on entire virtual desktop
34 | The returned position will work when recording a single monitor because it is
35 | relative to the monitor that currently owns the pointer
36 | */
37 | DXGI_OUTDUPL_POINTER_POSITION Position() const;
38 | void UpdatePosition(DXGI_OUTDUPL_POINTER_POSITION newPosition, LARGE_INTEGER updateTime, UINT outputIndex, RECT desktopMonitorBounds);
39 |
40 | DXGI_OUTDUPL_POINTER_SHAPE_INFO ShapeInfo() const;
41 | void ShapeInfo(DXGI_OUTDUPL_POINTER_SHAPE_INFO newShapeInfo);
42 |
43 | void UpdateTexture(winrt::com_ptr const& newImage);
44 | winrt::com_ptr Texture() const;
45 |
46 | bool Visible() const;
47 |
48 | private:
49 | std::vector mBuffer;
50 | DXGI_OUTDUPL_POINTER_SHAPE_INFO mShapeInfo;
51 | bool mIsPointerTextureStale;
52 | winrt::com_ptr mPointerTexture;
53 | DXGI_OUTDUPL_POINTER_POSITION mPosition;
54 | LARGE_INTEGER mLastUpdateTime;
55 | UINT mPointerOwnerIndex;
56 | bool mVisible;
57 | RECT mVirtualDesktopBounds;
58 | };
59 |
60 |
--------------------------------------------------------------------------------
/VideoLibrary/DisplayAdapter.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DxResource.h"
22 | #include "DisplayAdapter.h"
23 |
24 | DisplayAdapter::DisplayAdapter(winrt::com_ptr const& adapter)
25 | : mAdapter{ adapter }
26 | {
27 | DXGI_ADAPTER_DESC1 adapterDesc;
28 | winrt::check_hresult(mAdapter->GetDesc1(&adapterDesc));
29 |
30 | mAdapterName = adapterDesc.Description;
31 | mDevice = DxResource::MakeVideoEnabledDevice(mAdapter);
32 | }
33 |
--------------------------------------------------------------------------------
/VideoLibrary/DisplayAdapter.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | class DisplayAdapter
23 | {
24 | public:
25 |
26 | DisplayAdapter(winrt::com_ptr const& adapter);
27 |
28 | winrt::com_ptr Device() const { return mDevice; }
29 |
30 | std::wstring const& Name() const { return mAdapterName; }
31 |
32 | private:
33 |
34 | winrt::com_ptr mDevice;
35 | winrt::com_ptr mAdapter;
36 |
37 | std::wstring mAdapterName;
38 | };
39 |
--------------------------------------------------------------------------------
/VideoLibrary/DxMultithread.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 |
25 | class DxMultithread
26 | {
27 | public:
28 | DxMultithread(winrt::com_ptr multithread)
29 | : mMultithread{multithread}
30 | {
31 | mMultithread->Enter();
32 | }
33 |
34 | ~DxMultithread()
35 | {
36 | mMultithread->Leave();
37 | }
38 |
39 | private:
40 | winrt::com_ptr mMultithread;
41 | };
--------------------------------------------------------------------------------
/VideoLibrary/DxResource.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DxResource.h"
22 |
23 | void EnableDebugOnDevice(winrt::com_ptr device)
24 | {
25 | winrt::com_ptr debug{ device.as() };
26 | winrt::com_ptr info{ debug.as() };
27 | info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
28 | info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
29 | //info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, true);
30 | //info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_INFO, true);
31 | //info->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_MESSAGE, true);
32 | info->SetMuteDebugOutput(false);
33 | }
34 |
35 | winrt::com_ptr DxResource::MakeDevice()
36 | {
37 | winrt::com_ptr device;
38 |
39 | int flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
40 | #ifdef _DEBUG
41 | flags |= D3D11_CREATE_DEVICE_DEBUG;
42 | #endif
43 |
44 | winrt::check_hresult(D3D11CreateDevice(
45 | nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
46 | flags,
47 | nullptr, 0,
48 | D3D11_SDK_VERSION,
49 | device.put(),
50 | nullptr,
51 | nullptr
52 | ));
53 |
54 | winrt::com_ptr multithread{ device.as() };
55 | multithread->SetMultithreadProtected(true);
56 |
57 | #ifdef _DEBUG
58 | EnableDebugOnDevice(device);
59 | #endif
60 |
61 | return device;
62 | }
63 |
64 | winrt::com_ptr DxResource::MakeVideoEnabledDevice(winrt::com_ptr const& adapter)
65 | {
66 | winrt::com_ptr device;
67 |
68 | // This flag is needed by Media Foundation:
69 | // https://docs.microsoft.com/en-us/windows/win32/api/mfapi/nf-mfapi-mfcreatedxgidevicemanager#remarks
70 | int flags = D3D11_CREATE_DEVICE_VIDEO_SUPPORT;
71 | #ifdef _DEBUG
72 | flags |= D3D11_CREATE_DEVICE_DEBUG;
73 | #endif
74 |
75 | winrt::check_hresult(D3D11CreateDevice(
76 | adapter.get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
77 | flags,
78 | nullptr, 0,
79 | D3D11_SDK_VERSION,
80 | device.put(),
81 | nullptr,
82 | nullptr
83 | ));
84 |
85 | winrt::com_ptr multithread{ device.as() };
86 | multithread->SetMultithreadProtected(true);
87 |
88 | #ifdef _DEBUG
89 | EnableDebugOnDevice(device);
90 | #endif
91 |
92 | return device;
93 | }
94 |
95 | winrt::com_ptr DxResource::MakeDxgiFactory()
96 | {
97 | winrt::com_ptr factory;
98 | winrt::check_hresult(CreateDXGIFactory1(__uuidof(factory), factory.put_void()));
99 | return factory;
100 | }
101 |
102 | DxResource::DxResource()
103 | {
104 | }
105 |
--------------------------------------------------------------------------------
/VideoLibrary/DxResource.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | class DxResource
23 | {
24 | public:
25 |
26 | static winrt::com_ptr MakeDevice();
27 |
28 | static winrt::com_ptr MakeVideoEnabledDevice(winrt::com_ptr const& adapter);
29 |
30 | static winrt::com_ptr MakeDxgiFactory();
31 |
32 | private:
33 | DxResource();
34 | };
35 |
--------------------------------------------------------------------------------
/VideoLibrary/EncodingContext.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include
24 |
25 | using ResolutionOption = winrt::Windows::Media::MediaProperties::VideoEncodingQuality;
26 | using AudioQuality = winrt::Windows::Media::MediaProperties::AudioEncodingQuality;
27 |
28 | struct EncodingContext
29 | {
30 | std::wstring fileName;
31 | ResolutionOption resolutionOption;
32 | AudioQuality audioQuality;
33 | int frameRate;
34 | int bitRate;
35 | winrt::com_ptr videoInputMediaType;
36 | winrt::com_ptr audioInputMediaType;
37 | winrt::com_ptr device;
38 | };
39 |
--------------------------------------------------------------------------------
/VideoLibrary/Errors.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "Errors.h"
22 |
23 | HRESULT TranslateHresultFailureWithDevice(winrt::com_ptr device, HRESULT hr)
24 | {
25 | HRESULT translatedHr = hr;
26 | HRESULT deviceRemovedReason = device->GetDeviceRemovedReason();
27 |
28 | if (device)
29 | {
30 | switch (deviceRemovedReason)
31 | {
32 | case DXGI_ERROR_DEVICE_REMOVED:
33 | case DXGI_ERROR_DEVICE_RESET:
34 | case static_cast(E_OUTOFMEMORY) :
35 | {
36 | // Our device has been stopped due to an external event on the GPU so map them all to
37 | // device removed and continue processing the condition
38 | translatedHr = DXGI_ERROR_DEVICE_REMOVED;
39 | break;
40 | }
41 |
42 | case S_OK:
43 | // Device is not removed
44 | break;
45 |
46 | default:
47 | // Device is removed, but we don't want to lose this removal reason
48 | translatedHr = deviceRemovedReason;
49 | break;
50 | }
51 | }
52 |
53 | return translatedHr;
54 | }
55 |
56 | void ThrowExceptionCheckRecoverable(winrt::com_ptr device, const std::vector expectedErrors, HRESULT error)
57 | {
58 | HRESULT translatedHr = TranslateHresultFailureWithDevice(device, error);
59 |
60 | if (std::find(expectedErrors.begin(), expectedErrors.end(), translatedHr) != expectedErrors.end())
61 | {
62 | throw RecoverableVideoException(translatedHr);
63 | }
64 |
65 | winrt::throw_hresult(error);
66 | }
67 |
68 | inline RecoverableVideoException::RecoverableVideoException(HRESULT hr)
69 | {
70 | mHresult = hr;
71 | }
72 |
73 | inline HRESULT RecoverableVideoException::Hresult() const
74 | {
75 | return mHresult;
76 | }
77 |
--------------------------------------------------------------------------------
/VideoLibrary/Errors.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 |
24 | // The below is from the DesktopDuplication sample from Microsoft in the DesktopDuplication.cpp file
25 |
26 | // Below are lists of errors expect from Dxgi API calls when a transition event like mode change, PnpStop, PnpStart
27 | // desktop switch, TDR or session disconnect/reconnect. In all these cases we want the application to clean up the threads that process
28 | // the desktop updates and attempt to recreate them.
29 | // If we get an error that is not on the appropriate list then we exit the application
30 |
31 | // These are the errors we expect from general Dxgi API due to a transition
32 | const std::vector SystemTransitionsExpectedErrors = {
33 | DXGI_ERROR_DEVICE_REMOVED,
34 | DXGI_ERROR_ACCESS_LOST,
35 | static_cast(WAIT_ABANDONED),
36 | };
37 |
38 | // These are the errors we expect from IDXGIOutput1::DuplicateOutput due to a transition
39 | const std::vector CreateDuplicationExpectedErrors = {
40 | DXGI_ERROR_DEVICE_REMOVED,
41 | static_cast(E_ACCESSDENIED),
42 | DXGI_ERROR_UNSUPPORTED,
43 | DXGI_ERROR_SESSION_DISCONNECTED
44 | };
45 |
46 | // These are the errors we expect from IDXGIOutputDuplication methods due to a transition
47 | const std::vector FrameInfoExpectedErrors = {
48 | DXGI_ERROR_DEVICE_REMOVED,
49 | DXGI_ERROR_ACCESS_LOST,
50 | DXGI_ERROR_INVALID_CALL
51 | };
52 |
53 | class RecoverableVideoException : public std::exception
54 | {
55 | public:
56 |
57 | RecoverableVideoException(HRESULT hr);
58 |
59 | HRESULT Hresult() const;
60 |
61 | private:
62 | HRESULT mHresult;
63 | };
64 |
65 | HRESULT TranslateHresultFailureWithDevice(winrt::com_ptr device, HRESULT hr);
66 |
67 | void ThrowExceptionCheckRecoverable(winrt::com_ptr device, const std::vector expectedErrors, HRESULT error);
68 |
--------------------------------------------------------------------------------
/VideoLibrary/Frame.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "Errors.h"
22 | #include "Frame.h"
23 |
24 | Frame::Frame(ScreenDuplicator& duplicator)
25 | : mDupl{ duplicator.Duplication()}
26 | , mCaptured{ false }
27 | , mRectBuffer{ duplicator.Buffer()}
28 | , mNumMoveRects{ 0 }
29 | , mNumDirtyRects{ 0 }
30 | , mMoveRects{ nullptr }
31 | , mDirtyRects{ nullptr }
32 | , mDesktopMonitorBounds{ }
33 | , mRotation{ DXGI_MODE_ROTATION_UNSPECIFIED }
34 | {
35 | try
36 | {
37 | winrt::com_ptr desktopImageResource;
38 | HRESULT hr = mDupl->AcquireNextFrame(1, &mFrameInfo, desktopImageResource.put());
39 |
40 | if (hr == DXGI_ERROR_WAIT_TIMEOUT || hr == DXGI_STATUS_OCCLUDED)
41 | {
42 | return;
43 | }
44 |
45 | winrt::check_hresult(hr);
46 |
47 | mCaptured = true;
48 | mFrameTexture = desktopImageResource.as();
49 |
50 | // Don't care about move or dirty rects, just get the pointer data and update the pointer cache
51 | if (mFrameInfo.LastMouseUpdateTime.QuadPart != 0 && mFrameInfo.PointerShapeBufferSize != 0) {
52 |
53 | UINT requiredBufferSize;
54 | DXGI_OUTDUPL_POINTER_SHAPE_INFO pointerInfo;
55 | winrt::check_hresult(mDupl->GetFramePointerShape(mFrameInfo.PointerShapeBufferSize,
56 | reinterpret_cast(duplicator.DesktopPointerPtr()->PutBuffer(mFrameInfo.PointerShapeBufferSize)),
57 | &requiredBufferSize,
58 | &pointerInfo));
59 |
60 | duplicator.DesktopPointerPtr()->ShapeInfo(pointerInfo);
61 | }
62 |
63 | DXGI_OUTPUT_DESC desc;
64 | winrt::check_hresult(duplicator.Output()->GetDesc(&desc));
65 | mDesktopMonitorBounds = desc.DesktopCoordinates;
66 | mRotation = desc.Rotation;
67 |
68 | duplicator.DesktopPointerPtr()->UpdatePosition(
69 | mFrameInfo.PointerPosition,
70 | mFrameInfo.LastMouseUpdateTime,
71 | duplicator.OutputIndex(),
72 | mDesktopMonitorBounds);
73 |
74 | // get frame metadata
75 | if (mFrameInfo.TotalMetadataBufferSize != 0) {
76 |
77 | UINT totalBufferSize = mFrameInfo.TotalMetadataBufferSize;
78 | if (totalBufferSize > mRectBuffer->size()) {
79 | mRectBuffer->resize(totalBufferSize);
80 | }
81 |
82 | UINT moveRectsBufferSize = 0;
83 | winrt::check_hresult(mDupl->GetFrameMoveRects(
84 | totalBufferSize,
85 | reinterpret_cast(mRectBuffer->data()),
86 | &moveRectsBufferSize));
87 |
88 | UINT dirtyRectsBufferSize = totalBufferSize - moveRectsBufferSize;
89 |
90 | winrt::check_hresult(mDupl->GetFrameDirtyRects(
91 | dirtyRectsBufferSize,
92 | reinterpret_cast(mRectBuffer->data() + moveRectsBufferSize),
93 | &dirtyRectsBufferSize));
94 |
95 | mNumMoveRects = moveRectsBufferSize / sizeof(DXGI_OUTDUPL_MOVE_RECT);
96 | mNumDirtyRects = dirtyRectsBufferSize / sizeof(RECT);
97 | }
98 | }
99 | catch (...)
100 | {
101 | HRESULT hr = winrt::to_hresult();
102 | ThrowExceptionCheckRecoverable(duplicator.Device(), FrameInfoExpectedErrors, hr);
103 | }
104 | }
105 |
106 | Frame::~Frame()
107 | {
108 | if (mDupl)
109 | {
110 | try
111 | {
112 | (void)mDupl->ReleaseFrame();
113 | }
114 | catch (...)
115 | {
116 | }
117 | }
118 | }
119 |
120 | winrt::com_ptr Frame::DesktopImage() const
121 | {
122 | return mFrameTexture;
123 | }
124 |
125 | RECT Frame::DesktopMonitorBounds() const
126 | {
127 | return mDesktopMonitorBounds;
128 | }
129 |
130 | int64_t Frame::PresentationTime() const
131 | {
132 | // LARGE_INTEGER frequency;
133 | // QueryPerformanceFrequency(&frequency);
134 |
135 | int64_t nanoSeconds = mFrameInfo.LastPresentTime.QuadPart;
136 |
137 | // ticks / ticks per second = seconds
138 | // save precision by dividing first and then multipling by 1e9 (1e9 ns in one sec)
139 |
140 | // nanoSeconds /= frequency.QuadPart;
141 | // nanoSeconds *= 1'000'000'000;
142 |
143 |
144 | return nanoSeconds;
145 | }
146 |
147 | bool Frame::Captured() const
148 | {
149 | return mCaptured;
150 | }
151 |
152 | DXGI_MODE_ROTATION Frame::Rotation() const { return mRotation; }
153 |
154 | DXGI_OUTDUPL_MOVE_RECT* Frame::MoveRects() const
155 | {
156 | return reinterpret_cast(mRectBuffer->data());
157 | }
158 |
159 | RECT* Frame::DirtyRects() const
160 | {
161 | return reinterpret_cast(mRectBuffer->data() + (mNumMoveRects * sizeof(DXGI_OUTDUPL_MOVE_RECT)));
162 | }
163 |
164 | size_t Frame::MoveRectsCount() const { return mNumMoveRects; }
165 |
166 | size_t Frame::DirtyRectsCount() const { return mNumDirtyRects; }
167 |
--------------------------------------------------------------------------------
/VideoLibrary/Frame.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "ScreenDuplicator.h"
23 |
24 | class Frame
25 | {
26 | public:
27 | Frame(ScreenDuplicator& duplicator);
28 | ~Frame();
29 |
30 | winrt::com_ptr DesktopImage() const;
31 |
32 | RECT DesktopMonitorBounds() const;
33 |
34 | int64_t PresentationTime() const;
35 |
36 | bool Captured() const;
37 |
38 | DXGI_MODE_ROTATION Rotation() const;
39 |
40 | DXGI_OUTDUPL_MOVE_RECT* MoveRects() const;
41 |
42 | RECT* DirtyRects() const;
43 |
44 | size_t MoveRectsCount() const;
45 |
46 | size_t DirtyRectsCount() const;
47 |
48 | private:
49 | RECT mDesktopMonitorBounds;
50 | winrt::com_ptr mFrameTexture;
51 | DXGI_OUTDUPL_FRAME_INFO mFrameInfo;
52 | winrt::com_ptr mDupl;
53 | std::shared_ptr> mRectBuffer;
54 |
55 | bool mCaptured;
56 |
57 | // move/dirty rects data
58 | DXGI_OUTDUPL_MOVE_RECT* mMoveRects;
59 | size_t mNumMoveRects;
60 |
61 | RECT* mDirtyRects;
62 | size_t mNumDirtyRects;
63 |
64 | DXGI_MODE_ROTATION mRotation;
65 | };
66 |
--------------------------------------------------------------------------------
/VideoLibrary/KeyedMutexLock.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | class RotatingKeys
23 | {
24 | public:
25 |
26 | RotatingKeys()
27 | : RotatingKeys(0, 1)
28 | {
29 | }
30 |
31 | RotatingKeys(int acquireKey, int releaseKey)
32 | : mAcquireKey{ acquireKey }
33 | , mReleaseKey{ releaseKey }
34 | {
35 | }
36 |
37 | int AcquireKey() const
38 | {
39 | return mAcquireKey;
40 | }
41 |
42 | int ReleaseKey() const
43 | {
44 | return mReleaseKey;
45 | }
46 |
47 | void Rotate()
48 | {
49 | auto previousRelease = mReleaseKey;
50 | mReleaseKey = mAcquireKey;
51 | mAcquireKey = previousRelease;
52 | }
53 |
54 | private:
55 | int mAcquireKey;
56 | int mReleaseKey;
57 | };
58 |
59 | class KeyedMutexLock
60 | {
61 | public:
62 | KeyedMutexLock(
63 | winrt::com_ptr texture,
64 | winrt::com_ptr mutex,
65 | std::shared_ptr rotatingKeys)
66 | : mMutex{ mutex }
67 | , mRotatingKeys{ rotatingKeys }
68 | , mLocked{ false }
69 | {
70 | if (mMutex == nullptr)
71 | {
72 | throw std::exception("null mutex");
73 | }
74 |
75 | if (mRotatingKeys == nullptr)
76 | {
77 | throw std::exception("null rotating keys keyed mutex lock");
78 | }
79 |
80 | HRESULT hr = mutex->AcquireSync(mRotatingKeys->AcquireKey(), 10);
81 | if (hr == static_cast(WAIT_TIMEOUT))
82 | {
83 | return;
84 | }
85 |
86 | winrt::check_hresult(hr);
87 | mLocked = true;
88 | mTexture = texture;
89 | }
90 |
91 | ~KeyedMutexLock()
92 | {
93 | // only release and rotate if mLocked is true?
94 | auto releaseKey = mRotatingKeys->ReleaseKey();
95 | mRotatingKeys->Rotate();
96 | winrt::check_hresult(mMutex->ReleaseSync(releaseKey));
97 | }
98 |
99 | bool Locked() const { return mLocked; }
100 |
101 | ID3D11Texture2D* TexturePtr() const
102 | {
103 | return mTexture.get();
104 | }
105 |
106 | private:
107 | winrt::com_ptr mMutex;
108 | bool mLocked;
109 | std::shared_ptr mRotatingKeys;
110 | winrt::com_ptr mTexture;
111 | };
112 |
--------------------------------------------------------------------------------
/VideoLibrary/Pipeline.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DxMultithread.h"
22 | #include "CaptureFrameStep.h"
23 | #include "RenderMoveRectsStep.h"
24 | #include "RenderDirtyRectsStep.h"
25 | #include "RenderPointerTextureStep.h"
26 | #include "TextureToMediaSampleStep.h"
27 | #include "Pipeline.h"
28 |
29 | Pipeline::Pipeline(
30 | std::shared_ptr duplicator,
31 | std::shared_ptr sharedSurface,
32 | RECT virtualDesktopBounds
33 | )
34 | : mDuplicator{ duplicator }
35 | , mSharedSurface{ sharedSurface }
36 | , mVirtualDesktopBounds{ virtualDesktopBounds }
37 | {
38 | if (mDuplicator == nullptr)
39 | {
40 | throw std::exception("Null duplicator");
41 | }
42 |
43 | winrt::check_pointer(mSharedSurface.get());
44 | mShaderCache = std::make_shared(mDuplicator->Device());
45 | mVertexBuffer = std::make_shared>();
46 |
47 | }
48 |
49 | Pipeline::~Pipeline()
50 | {
51 | }
52 |
53 | void Pipeline::Perform()
54 | {
55 | mSample == nullptr;
56 | auto device = mDuplicator->Device();
57 | // need to use multithread protect because of Media Foundation api
58 | // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfdxgidevicemanager-resetdevice#remarks
59 | DxMultithread multithread{ device.as() };
60 | {
61 | auto lock = mSharedSurface->Lock();
62 |
63 | if (!lock->Locked())
64 | {
65 | return;
66 | }
67 |
68 | CaptureFrameStep captureFrame{ *mDuplicator };
69 | captureFrame.Perform();
70 |
71 | std::shared_ptr frame = captureFrame.Result();
72 | mDesktopMonitorBounds = frame->DesktopMonitorBounds();
73 | if (frame->Captured())
74 | {
75 | if (mTexturePool == nullptr)
76 | {
77 | AllocateTexturePool();
78 | }
79 |
80 | if (mStagingTexture == nullptr)
81 | {
82 | D3D11_TEXTURE2D_DESC stagingDesc;
83 | frame->DesktopImage()->GetDesc(&stagingDesc);
84 | stagingDesc.BindFlags = D3D11_BIND_RENDER_TARGET;
85 | stagingDesc.MiscFlags = 0;
86 | AllocateStagingTexture(device, stagingDesc);
87 | }
88 |
89 | if (mRenderTargetView == nullptr)
90 | {
91 | winrt::check_hresult(mDuplicator->Device()->CreateRenderTargetView(
92 | lock->TexturePtr(),
93 | nullptr,
94 | mRenderTargetView.put()
95 | ));
96 | }
97 |
98 | RenderMoveRectsStep renderMoves{
99 | frame,
100 | mVirtualDesktopBounds,
101 | mStagingTexture,
102 | lock->TexturePtr()
103 | };
104 |
105 | renderMoves.Perform();
106 |
107 | RenderDirtyRectsStep renderDirty{
108 | frame,
109 | mVirtualDesktopBounds,
110 | mVertexBuffer,
111 | mShaderCache,
112 | lock->TexturePtr(),
113 | mRenderTargetView
114 | };
115 | renderDirty.Perform();
116 | }
117 | }
118 |
119 | RenderPointerTextureStep renderPointer{
120 | mDuplicator->DesktopPointerPtr(),
121 | mSharedSurface,
122 | mDuplicator->Device(),
123 | mShaderCache,
124 | mTexturePool,
125 | mVirtualDesktopBounds,
126 | mDesktopMonitorBounds
127 | };
128 | renderPointer.Perform();
129 |
130 | if (renderPointer.Result() == nullptr)
131 | {
132 | return;
133 | }
134 |
135 | winrt::com_ptr desktopTexture = renderPointer.Result();
136 |
137 | TextureToMediaSampleStep convertTexture{
138 | desktopTexture,
139 | mTexturePool
140 | };
141 | convertTexture.Perform();
142 |
143 | mSample = convertTexture.Result();
144 | }
145 |
146 | winrt::com_ptr Pipeline::Sample() const
147 | {
148 | return mSample;
149 | }
150 |
151 | void Pipeline::AllocateTexturePool()
152 | {
153 | D3D11_TEXTURE2D_DESC desc = mSharedSurface->Desc();
154 | // Use the same device that was used to open the shared surface
155 | // instead of the device used by Desktop Duplication API's desktop image.
156 | mTexturePool.attach(new TexturePool(mDuplicator->Device(), desc));
157 | winrt::check_pointer(mTexturePool.get());
158 | }
159 |
160 | void Pipeline::AllocateStagingTexture(winrt::com_ptr device, const D3D11_TEXTURE2D_DESC& desc)
161 | {
162 | winrt::check_hresult(device->CreateTexture2D(
163 | &desc,
164 | nullptr,
165 | mStagingTexture.put()));
166 | }
167 |
--------------------------------------------------------------------------------
/VideoLibrary/Pipeline.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 | #include "RecordingStep.h"
22 | #include "TexturePool.h"
23 | #include "DesktopMonitor.h"
24 | #include "DesktopPointer.h"
25 | #include "ScreenDuplicator.h"
26 | #include "Vertex.h"
27 | #include "ShaderCache.h"
28 | #include "SharedSurface.h"
29 |
30 | class Pipeline : public RecordingStep
31 | {
32 | public:
33 | Pipeline(
34 | std::shared_ptr duplicator,
35 | std::shared_ptr sharedSurface,
36 | RECT virtualDesktopBounds
37 | );
38 |
39 | virtual ~Pipeline();
40 |
41 | // Inherited via RecordingStep
42 | virtual void Perform() override;
43 |
44 | winrt::com_ptr Sample() const;
45 |
46 | private:
47 |
48 | void AllocateTexturePool();
49 | void AllocateStagingTexture(winrt::com_ptr device, const D3D11_TEXTURE2D_DESC& desc);
50 |
51 | std::shared_ptr mDuplicator;
52 | std::shared_ptr mSharedSurface;
53 | std::shared_ptr mShaderCache;
54 | std::shared_ptr> mVertexBuffer;
55 | winrt::com_ptr mTexturePool;
56 | winrt::com_ptr mStagingTexture;
57 | winrt::com_ptr mSample;
58 | winrt::com_ptr mRenderTargetView;
59 | RECT mVirtualDesktopBounds;
60 | RECT mDesktopMonitorBounds;
61 | };
62 |
--------------------------------------------------------------------------------
/VideoLibrary/PixelShader.hlsl:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | Texture2D tx : register(t0);
21 | SamplerState samLinear : register(s0);
22 |
23 | struct PS_INPUT
24 | {
25 | float4 Pos : SV_POSITION;
26 | float2 Tex : TEXCOORD;
27 | };
28 |
29 | //--------------------------------------------------------------------------------------
30 | // Pixel Shader
31 | //--------------------------------------------------------------------------------------
32 | float4 main(PS_INPUT input) : SV_Target
33 | {
34 | return tx.Sample(samLinear, input.Tex);
35 | }
--------------------------------------------------------------------------------
/VideoLibrary/RecordingStep.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "RecordingStep.h"
22 |
23 |
24 | RecordingStep::RecordingStep()
25 | {
26 | }
27 |
28 | RecordingStep::~RecordingStep()
29 | {
30 | }
31 |
--------------------------------------------------------------------------------
/VideoLibrary/RecordingStep.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | class RecordingStep
23 | {
24 | public:
25 | RecordingStep();
26 | virtual ~RecordingStep();
27 |
28 | virtual void Perform() = 0;
29 | };
30 |
--------------------------------------------------------------------------------
/VideoLibrary/RenderDirtyRectsStep.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "VirtualDesktop.h"
22 | #include "RenderPointerTextureStep.h"
23 | #include "RenderDirtyRectsStep.h"
24 |
25 | RenderDirtyRectsStep::RenderDirtyRectsStep(
26 | std::shared_ptr frame,
27 | RECT virtualDesktopBounds,
28 | std::shared_ptr> vertexBuffer,
29 | std::shared_ptr shaderCache,
30 | ID3D11Texture2D* sharedSurfacePtr,
31 | winrt::com_ptr renderTargetView)
32 | : mFrame{ frame }
33 | , mVirtualDesktopBounds{ virtualDesktopBounds }
34 | , mVertexBuffer{ vertexBuffer }
35 | , mShaderCache{ shaderCache }
36 | , mSharedSurfacePtr{ sharedSurfacePtr }
37 | , mRenderTargetView{ renderTargetView }
38 | {
39 | if (mFrame == nullptr)
40 | {
41 | throw std::exception("Null frame");
42 | }
43 |
44 | if (mVertexBuffer == nullptr)
45 | {
46 | throw std::exception("null vertex buffer");
47 | }
48 |
49 | if (mShaderCache == nullptr)
50 | {
51 | throw std::exception("null shader cache");
52 | }
53 |
54 | winrt::check_pointer(mSharedSurfacePtr);
55 | winrt::check_pointer(mRenderTargetView.get());
56 | }
57 |
58 | RenderDirtyRectsStep::~RenderDirtyRectsStep()
59 | {
60 | }
61 |
62 | void RenderDirtyRectsStep::Perform()
63 | {
64 | if (mFrame->DirtyRectsCount() == 0) {
65 | return;
66 | }
67 |
68 | UpdateDirtyRects();
69 |
70 | RenderDirtyRects();
71 | }
72 |
73 | void RenderDirtyRectsStep::UpdateDirtyRects()
74 | {
75 | // create dirty vertex buffer
76 | mVertexBuffer->resize(mFrame->DirtyRectsCount() * g_VerticesPerRect);
77 |
78 | D3D11_TEXTURE2D_DESC sharedSurfaceDesc;
79 | mSharedSurfacePtr->GetDesc(&sharedSurfaceDesc);
80 |
81 | D3D11_TEXTURE2D_DESC desktopImageDesc;
82 | mFrame->DesktopImage()->GetDesc(&desktopImageDesc);
83 |
84 | // be careful with the types of these integers - they should be signed ints
85 | // or else the below vertices position calculations will overflow
86 | const LONG centerX = (LONG)sharedSurfaceDesc.Width / 2;
87 | const LONG centerY = (LONG)sharedSurfaceDesc.Height / 2;
88 | const LONG offsetX = mVirtualDesktopBounds.left;
89 | const LONG offsetY = mVirtualDesktopBounds.top;
90 |
91 | const RECT desktopBounds = mFrame->DesktopMonitorBounds();
92 | const LONG desktopWidth = desktopBounds.right - desktopBounds.left;
93 | const LONG desktopHeight = desktopBounds.bottom - desktopBounds.top;
94 |
95 | RECT* dirtyRects = mFrame->DirtyRects();
96 |
97 | for (size_t i = 0; i < mFrame->DirtyRectsCount(); ++i) {
98 | /*
99 | Identity and unspecified:
100 | 2 4
101 | +---------------+
102 | | |
103 | | |
104 | +---------------+
105 | 1 3
106 |
107 | 90 CCW:
108 | 2 4
109 | +--------+
110 | | |
111 | | |
112 | | |
113 | | |
114 | +--------+
115 | 1 3
116 |
117 |
118 | 180 CCW:
119 | 4 1
120 | +---------------+
121 | | |
122 | | |
123 | +---------------+
124 | 2 3
125 |
126 | 270 CCW:
127 | 1 3
128 | +--------+
129 | | |
130 | | |
131 | | |
132 | | |
133 | +--------+
134 | 4 2
135 |
136 | */
137 | RECT dirtyRect = dirtyRects[i];
138 | RECT rotatedRect = dirtyRects[i];
139 |
140 | switch (mFrame->Rotation())
141 | {
142 | case DXGI_MODE_ROTATION_ROTATE90:
143 | rotatedRect.left = desktopWidth - dirtyRect.bottom;
144 | rotatedRect.top = dirtyRect.left;
145 | rotatedRect.right = desktopWidth - dirtyRect.top;
146 | rotatedRect.bottom = dirtyRect.right;
147 | break;
148 |
149 | case DXGI_MODE_ROTATION_ROTATE180:
150 | rotatedRect.left = desktopWidth - dirtyRect.right;
151 | rotatedRect.top = desktopHeight - dirtyRect.bottom;
152 | rotatedRect.right = desktopWidth - dirtyRect.left;
153 | rotatedRect.bottom = desktopHeight - dirtyRect.top;
154 | break;
155 |
156 | case DXGI_MODE_ROTATION_ROTATE270:
157 | rotatedRect.left = dirtyRect.top;
158 | rotatedRect.top = desktopHeight - dirtyRect.right;
159 | rotatedRect.right = dirtyRect.bottom;
160 | rotatedRect.bottom = desktopHeight - dirtyRect.left;
161 | break;
162 |
163 | case DXGI_MODE_ROTATION_IDENTITY:
164 | case DXGI_MODE_ROTATION_UNSPECIFIED:
165 | default:
166 | break;
167 | }
168 |
169 | TexCoord bottomLeft = { dirtyRect.left / static_cast(desktopImageDesc.Width), dirtyRect.bottom / static_cast(desktopImageDesc.Height) };
170 | TexCoord topLeft = { dirtyRect.left / static_cast(desktopImageDesc.Width), dirtyRect.top / static_cast(desktopImageDesc.Height) };
171 | TexCoord bottomRight = { dirtyRect.right / static_cast(desktopImageDesc.Width), dirtyRect.bottom / static_cast(desktopImageDesc.Height) };
172 | TexCoord topRight = { dirtyRect.right / static_cast(desktopImageDesc.Width), dirtyRect.top / static_cast(desktopImageDesc.Height) };
173 |
174 | // set vertex buffer at [i]
175 | auto vertices = mVertexBuffer->data() + i * g_VerticesPerRect;
176 |
177 | switch (mFrame->Rotation()) {
178 | case DXGI_MODE_ROTATION_IDENTITY:
179 | case DXGI_MODE_ROTATION_UNSPECIFIED:
180 |
181 | vertices[0].texCoord = bottomLeft;
182 | vertices[1].texCoord = topLeft;
183 | vertices[2].texCoord = bottomRight;
184 | vertices[3].texCoord = vertices[2].texCoord;
185 | vertices[4].texCoord = vertices[1].texCoord;
186 | vertices[5].texCoord = topRight;
187 | break;
188 |
189 | case DXGI_MODE_ROTATION_ROTATE90:
190 | vertices[0].texCoord = bottomRight;
191 | vertices[1].texCoord = bottomLeft;
192 | vertices[2].texCoord = topRight;
193 | vertices[3].texCoord = vertices[2].texCoord;
194 | vertices[4].texCoord = vertices[1].texCoord;
195 | vertices[5].texCoord = topLeft;
196 | break;
197 |
198 | case DXGI_MODE_ROTATION_ROTATE180:
199 | vertices[0].texCoord = topRight;
200 | vertices[1].texCoord = bottomRight;
201 | vertices[2].texCoord = topLeft;
202 | vertices[3].texCoord = vertices[2].texCoord;
203 | vertices[4].texCoord = vertices[1].texCoord;
204 | vertices[5].texCoord = bottomLeft;
205 | break;
206 |
207 | case DXGI_MODE_ROTATION_ROTATE270:
208 | vertices[0].texCoord = topLeft;
209 | vertices[1].texCoord = topRight;
210 | vertices[2].texCoord = bottomLeft;
211 | vertices[3].texCoord = vertices[2].texCoord;
212 | vertices[4].texCoord = vertices[1].texCoord;
213 | vertices[5].texCoord = bottomRight;
214 | break;
215 |
216 | default:
217 | throw std::exception("render dirty rects with unimplemented rotation");
218 | }
219 |
220 | // Set the vertices of the two triangles that make up the dirty rectangle
221 | // The second triangle shares a side with the first triangle
222 | // vertices proceed clockwise
223 |
224 | // bottomLeft -> topLeft -> bottomRight
225 | vertices[0].pos = { (rotatedRect.left + desktopBounds.left - offsetX - centerX) / static_cast(centerX),
226 | -1 * (rotatedRect.bottom + desktopBounds.top - offsetY - centerY) / static_cast(centerY),
227 | 0.0f };
228 | vertices[1].pos = { (rotatedRect.left + desktopBounds.left - offsetX - centerX) / static_cast(centerX),
229 | -1 * (rotatedRect.top + desktopBounds.top - offsetY - centerY) / static_cast(centerY),
230 | 0.0f };
231 | vertices[2].pos = { (rotatedRect.right + desktopBounds.left - offsetX - centerX) / static_cast(centerX),
232 | -1 * (rotatedRect.bottom + desktopBounds.top - offsetY - centerY) / static_cast(centerY),
233 | 0.0f };
234 |
235 | // bottomRight -> topLeft -> topRight
236 | vertices[3].pos = vertices[2].pos;
237 | vertices[4].pos = vertices[1].pos;
238 | vertices[5].pos = { (rotatedRect.right + desktopBounds.left - offsetX - centerX) / static_cast(centerX),
239 | -1 * (rotatedRect.top + desktopBounds.top - offsetY - centerY) / static_cast(centerY),
240 | 0.0f };
241 | }
242 | }
243 |
244 | void RenderDirtyRectsStep::RenderDirtyRects()
245 | {
246 | winrt::com_ptr device;
247 | mSharedSurfacePtr->GetDevice(device.put());
248 |
249 | winrt::com_ptr context;
250 | device->GetImmediateContext(context.put());
251 |
252 | D3D11_TEXTURE2D_DESC srcTextureDesc;
253 | mFrame->DesktopImage()->GetDesc(&srcTextureDesc);
254 |
255 | // setup vertex buffer, vertex shader view, etc
256 | D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
257 | srvDesc.Format = srcTextureDesc.Format;
258 | srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
259 | srvDesc.Texture2D.MostDetailedMip = srcTextureDesc.MipLevels - 1;
260 | srvDesc.Texture2D.MipLevels = srcTextureDesc.MipLevels;
261 | winrt::com_ptr srView;
262 | winrt::check_hresult(device->CreateShaderResourceView(
263 | mFrame->DesktopImage().get(),
264 | &srvDesc,
265 | srView.put()
266 | ));
267 | auto srTemp = srView.get();
268 | ID3D11ShaderResourceView** srvPtr = &srTemp;
269 |
270 | auto rtv = mRenderTargetView.get();
271 | ID3D11RenderTargetView** rtvPtr = &rtv;
272 |
273 | auto sampler = mShaderCache->LinearSampler().get();
274 | auto samplerPtr = &sampler;
275 |
276 | // set device context properties
277 | const float blendFactor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
278 | context->OMSetBlendState(nullptr, blendFactor, 0xffffffff);
279 | context->OMSetRenderTargets(1, rtvPtr, nullptr);
280 | context->VSSetShader(mShaderCache->VertexShader().get(), nullptr, 0);
281 | context->PSSetShader(mShaderCache->PixelShader().get(), nullptr, 0);
282 | context->PSSetShaderResources(0, 1, srvPtr);
283 | context->PSSetSamplers(0, 1, samplerPtr);
284 | context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
285 |
286 | D3D11_BUFFER_DESC bufferDesc;
287 | bufferDesc.Usage = D3D11_USAGE_DEFAULT;
288 | bufferDesc.ByteWidth = static_cast(mVertexBuffer->size() * sizeof(Vertex));
289 | bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
290 | bufferDesc.CPUAccessFlags = 0;
291 | bufferDesc.MiscFlags = 0;
292 | bufferDesc.StructureByteStride = 0;
293 |
294 | D3D11_SUBRESOURCE_DATA bufferData;
295 | bufferData.pSysMem = reinterpret_cast(mVertexBuffer->data());
296 | bufferData.SysMemPitch = 0;
297 | bufferData.SysMemSlicePitch = 0;
298 |
299 | winrt::com_ptr buffer;
300 | winrt::check_hresult(device->CreateBuffer(&bufferDesc, &bufferData, buffer.put()));
301 |
302 | constexpr UINT stride = sizeof(Vertex);
303 | constexpr UINT offset = 0;
304 | ID3D11Buffer* buf = buffer.get();
305 | ID3D11Buffer** bufAddr = &buf;
306 | context->IASetVertexBuffers(0, 1, bufAddr, &stride, &offset);
307 |
308 | D3D11_TEXTURE2D_DESC sharedSurfaceDesc;
309 | mSharedSurfacePtr->GetDesc(&sharedSurfaceDesc);
310 |
311 | D3D11_VIEWPORT VP;
312 | VP.Width = static_cast(sharedSurfaceDesc.Width);
313 | VP.Height = static_cast(sharedSurfaceDesc.Height);
314 | VP.MinDepth = 0.0f;
315 | VP.MaxDepth = 1.0f;
316 | VP.TopLeftX = 0.0f;
317 | VP.TopLeftY = 0.0f;
318 | context->RSSetViewports(1, &VP);
319 |
320 | context->Draw(static_cast(mVertexBuffer->size()), 0);
321 | }
322 |
--------------------------------------------------------------------------------
/VideoLibrary/RenderDirtyRectsStep.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 | #include "RecordingStep.h"
22 | #include "DesktopMonitor.h"
23 | #include "ShaderCache.h"
24 | #include "Frame.h"
25 | #include "Vertex.h"
26 |
27 | class RenderDirtyRectsStep : public RecordingStep
28 | {
29 | public:
30 | RenderDirtyRectsStep(
31 | std::shared_ptr frame,
32 | RECT virtualDesktopBounds,
33 | std::shared_ptr> vertexBuffer,
34 | std::shared_ptr shaderCache,
35 | ID3D11Texture2D* sharedSurfacePtr,
36 | winrt::com_ptr renderTargetView
37 | );
38 | ~RenderDirtyRectsStep();
39 |
40 | // Inherited via RecordingStep
41 | virtual void Perform() override;
42 |
43 | private:
44 |
45 | void UpdateDirtyRects();
46 |
47 | void RenderDirtyRects();
48 |
49 | std::shared_ptr mFrame;
50 | RECT mVirtualDesktopBounds;
51 | std::shared_ptr> mVertexBuffer;
52 | std::shared_ptr mShaderCache;
53 | ID3D11Texture2D* mSharedSurfacePtr;
54 | winrt::com_ptr mRenderTargetView;
55 | };
56 |
--------------------------------------------------------------------------------
/VideoLibrary/RenderMoveRectsStep.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DesktopMonitor.h"
22 | #include "RenderDirtyRectsStep.h"
23 | #include "RenderMoveRectsStep.h"
24 |
25 |
26 | RenderMoveRectsStep::RenderMoveRectsStep(
27 | std::shared_ptr frame,
28 | RECT virtualDesktopBounds,
29 | winrt::com_ptr stagingTexture,
30 | ID3D11Texture2D* sharedSurfacePtr)
31 | : mFrame { frame }
32 | , mVirtualDesktopBounds{ virtualDesktopBounds }
33 | , mStagingTexture{ stagingTexture }
34 | , mSharedSurfacePtr{ sharedSurfacePtr }
35 | {
36 | if (mFrame == nullptr)
37 | {
38 | throw std::exception("null frame");
39 | }
40 |
41 | winrt::check_pointer(mStagingTexture.get());
42 | winrt::check_pointer(mSharedSurfacePtr);
43 | }
44 |
45 | RenderMoveRectsStep::~RenderMoveRectsStep()
46 | {
47 | }
48 |
49 | void RenderMoveRectsStep::Perform()
50 | {
51 | if (mFrame->MoveRectsCount() == 0)
52 | {
53 | return;
54 | }
55 |
56 | DXGI_OUTDUPL_MOVE_RECT* moveRects = mFrame->MoveRects();
57 | const LONG offsetX = mVirtualDesktopBounds.left;
58 | const LONG offsetY = mVirtualDesktopBounds.top;
59 |
60 | D3D11_TEXTURE2D_DESC desktopImageDesc;
61 | mFrame->DesktopImage()->GetDesc(&desktopImageDesc);
62 | const LONG desktopWidth = (LONG)desktopImageDesc.Width;
63 | const LONG desktopHeight = (LONG)desktopImageDesc.Height;
64 |
65 | winrt::com_ptr device;
66 | mSharedSurfacePtr->GetDevice(device.put());
67 |
68 | winrt::com_ptr context;
69 | device->GetImmediateContext(context.put());
70 |
71 | for (size_t i = 0; i < mFrame->MoveRectsCount(); ++i) {
72 | const DXGI_OUTDUPL_MOVE_RECT& moveRect = moveRects[i];
73 |
74 | RECT srcRect{}, dstRect{};
75 |
76 | // set src and dstRect based on rotation of output device
77 | switch (mFrame->Rotation())
78 | {
79 | case DXGI_MODE_ROTATION_UNSPECIFIED:
80 | case DXGI_MODE_ROTATION_IDENTITY:
81 | srcRect.left = moveRect.SourcePoint.x;
82 | srcRect.top = moveRect.SourcePoint.y;
83 | srcRect.bottom = moveRect.SourcePoint.y + moveRect.DestinationRect.bottom - moveRect.DestinationRect.top;
84 | srcRect.right = moveRect.SourcePoint.x + moveRect.DestinationRect.right - moveRect.DestinationRect.left;
85 | dstRect = moveRect.DestinationRect;
86 | break;
87 |
88 | case DXGI_MODE_ROTATION_ROTATE90:
89 | srcRect.left = desktopHeight - (moveRect.SourcePoint.y + moveRect.DestinationRect.bottom - moveRect.DestinationRect.top);
90 | srcRect.top = moveRect.SourcePoint.x;
91 | srcRect.right = desktopHeight - moveRect.SourcePoint.y;
92 | srcRect.bottom = moveRect.SourcePoint.x + moveRect.DestinationRect.right - moveRect.DestinationRect.left;
93 |
94 | dstRect.left = desktopHeight - moveRect.DestinationRect.bottom;
95 | dstRect.top = moveRect.DestinationRect.left;
96 | dstRect.right = desktopHeight - moveRect.DestinationRect.top;
97 | dstRect.bottom = moveRect.DestinationRect.right;
98 | break;
99 |
100 | case DXGI_MODE_ROTATION_ROTATE180:
101 | srcRect.left = desktopWidth - (moveRect.SourcePoint.x + moveRect.DestinationRect.right - moveRect.DestinationRect.left);
102 | srcRect.top = desktopHeight - (moveRect.SourcePoint.y + moveRect.DestinationRect.bottom - moveRect.DestinationRect.top);
103 | srcRect.right = desktopWidth - moveRect.SourcePoint.x;
104 | srcRect.bottom = desktopHeight - moveRect.SourcePoint.y;
105 |
106 | dstRect.left = desktopWidth - moveRect.DestinationRect.right;
107 | dstRect.top = desktopHeight - moveRect.DestinationRect.bottom;
108 | dstRect.right = desktopWidth - moveRect.DestinationRect.left;
109 | dstRect.bottom = desktopHeight - moveRect.DestinationRect.top;
110 | break;
111 |
112 | case DXGI_MODE_ROTATION_ROTATE270:
113 | srcRect.left = moveRect.SourcePoint.x;
114 | srcRect.top = desktopWidth - (moveRect.SourcePoint.x + moveRect.DestinationRect.right - moveRect.DestinationRect.left);
115 | srcRect.right = moveRect.SourcePoint.y + moveRect.DestinationRect.bottom - moveRect.DestinationRect.top;
116 | srcRect.bottom = desktopWidth - moveRect.SourcePoint.x;
117 |
118 | dstRect.left = moveRect.DestinationRect.top;
119 | dstRect.top = desktopWidth - moveRect.DestinationRect.right;
120 | dstRect.right = moveRect.DestinationRect.bottom;
121 | dstRect.bottom = desktopWidth - moveRect.DestinationRect.left;
122 | break;
123 |
124 | default:
125 | throw std::exception("Move rect rotation unimplemented");
126 | }
127 |
128 | // copy rect from shared surface to move surface, keeping same position
129 | const auto desktopCoordinates = mFrame->DesktopMonitorBounds();
130 | D3D11_BOX box;
131 | box.left = desktopCoordinates.left + srcRect.left - offsetX;
132 | box.right = desktopCoordinates.left + srcRect.right - offsetX;
133 | box.top = desktopCoordinates.top + srcRect.top - offsetY;
134 | box.bottom = desktopCoordinates.top + srcRect.bottom - offsetY;
135 | box.front = 0;
136 | box.back = 1;
137 |
138 | context->CopySubresourceRegion(
139 | mStagingTexture.get(), 0,
140 | srcRect.left,
141 | srcRect.top,
142 | 0,
143 | mSharedSurfacePtr, 0,
144 | &box
145 | );
146 |
147 | // copy from move surface to new relative position in shared surface
148 | box.left = srcRect.left;
149 | box.right = srcRect.right;
150 | box.top = srcRect.top;
151 | box.bottom = srcRect.bottom;
152 | box.front = 0;
153 | box.back = 1;
154 | context->CopySubresourceRegion(
155 | mSharedSurfacePtr, 0,
156 | desktopCoordinates.left + dstRect.left - offsetX,
157 | desktopCoordinates.top + dstRect.top - offsetY,
158 | 0,
159 | mStagingTexture.get(), 0,
160 | &box
161 | );
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/VideoLibrary/RenderMoveRectsStep.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "Frame.h"
23 | #include "RecordingStep.h"
24 |
25 | class RenderMoveRectsStep : public RecordingStep
26 | {
27 | public:
28 | RenderMoveRectsStep(
29 | std::shared_ptr frame,
30 | RECT virtualDesktopBounds,
31 | winrt::com_ptr stagingTexture,
32 | ID3D11Texture2D* sharedSurfacePtr);
33 |
34 | ~RenderMoveRectsStep();
35 |
36 | // Inherited via RecordingStep
37 | virtual void Perform() override;
38 |
39 | private:
40 | winrt::com_ptr mStagingTexture;
41 | ID3D11Texture2D* mSharedSurfacePtr;
42 | std::shared_ptr mFrame;
43 | RECT mVirtualDesktopBounds;
44 | };
45 |
46 |
--------------------------------------------------------------------------------
/VideoLibrary/RenderPointerTextureStep.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "RecordingStep.h"
23 | #include "ShaderCache.h"
24 | #include "TexturePool.h"
25 | #include "SharedSurface.h"
26 |
27 | class RenderPointerTextureStep : public RecordingStep
28 | {
29 | public:
30 | RenderPointerTextureStep(
31 | std::shared_ptr desktopPointer,
32 | std::shared_ptr sharedSurface,
33 | winrt::com_ptr device,
34 | std::shared_ptr shaderCache,
35 | winrt::com_ptr texturePool,
36 | RECT virtualDesktopBounds,
37 | RECT desktopMonitorBounds);
38 |
39 | virtual ~RenderPointerTextureStep();
40 |
41 | virtual void Perform() override;
42 |
43 | winrt::com_ptr Result();
44 |
45 | private:
46 | winrt::com_ptr MakePointerTexture();
47 | winrt::com_ptr MakeColorPointerTexture();
48 | winrt::com_ptr MakeMaskedPointerTexture();
49 |
50 | void MakeMonochromePointerBuffer(UINT* dest, const UINT * colorData, UINT colorPitch, byte* maskData, UINT maskPitch, UINT width, UINT height, UINT maskX, UINT maskY, UINT maskHeight);
51 | void MakeMaskedColorPointerBuffer(UINT * dest, const UINT * colorData, UINT colorPitch, UINT * maskData, UINT maskPitch, UINT width, UINT height, UINT maskX, UINT maskY);
52 | winrt::com_ptr MakeColorPointer(byte* data, int width, int height);
53 |
54 | winrt::com_ptr mDevice;
55 | std::shared_ptr mDesktopPointer;
56 | std::shared_ptr mShaderCache;
57 | std::shared_ptr mSharedSurface;
58 | winrt::com_ptr mTexturePool;
59 |
60 | ID3D11Texture2D* mSharedSurfacePtr;
61 |
62 | winrt::com_ptr mResult;
63 |
64 | RECT mVirtualDesktopBounds;
65 | RECT mDesktopMonitorBounds;
66 | };
67 |
--------------------------------------------------------------------------------
/VideoLibrary/ScreenDuplicator.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "Errors.h"
22 | #include "ScreenDuplicator.h"
23 |
24 | ScreenDuplicator::ScreenDuplicator(
25 | DesktopMonitor const& monitor,
26 | std::shared_ptr desktopPointer)
27 | : mDevice {monitor.Adapter().Device() }
28 | , mOutput{ monitor.Output()}
29 | , mDesktopPointer{ desktopPointer }
30 | , mRectBuffer{ std::make_shared>() }
31 | , mOutputIndex{ monitor.OutputIndex() }
32 | {
33 | HRESULT hr = mOutput->DuplicateOutput(mDevice.get(), mDupl.put());
34 | if (FAILED(hr))
35 | {
36 | ThrowExceptionCheckRecoverable(mDevice, CreateDuplicationExpectedErrors, hr);
37 | }
38 | }
39 |
40 | std::shared_ptr ScreenDuplicator::DesktopPointerPtr()
41 | {
42 | return mDesktopPointer;
43 | }
44 |
45 | ScreenDuplicator::~ScreenDuplicator()
46 | {
47 | if (mDupl)
48 | {
49 | // ignore hr, just release in case this object went out of scope
50 | (void)mDupl->ReleaseFrame();
51 | mDupl = nullptr;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/VideoLibrary/ScreenDuplicator.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include "DesktopPointer.h"
23 | #include "DisplayAdapter.h"
24 | #include "DesktopMonitor.h"
25 |
26 | class ScreenDuplicator
27 | {
28 | public:
29 |
30 | ScreenDuplicator(
31 | DesktopMonitor const& monitor,
32 | std::shared_ptr desktopPointer
33 | );
34 |
35 | winrt::com_ptr Device() const { return mDevice; }
36 |
37 | std::shared_ptr DesktopPointerPtr();
38 |
39 | winrt::com_ptr Output() const { return mOutput; }
40 |
41 | int OutputIndex() const { return mOutputIndex; }
42 |
43 | winrt::com_ptr Duplication() const { return mDupl; }
44 |
45 | std::shared_ptr> Buffer() const { return mRectBuffer; };
46 |
47 | ~ScreenDuplicator();
48 |
49 | private:
50 |
51 | winrt::com_ptr mDevice;
52 | winrt::com_ptr mOutput;
53 | winrt::com_ptr mDupl;
54 | std::wstring mOutputName;
55 | int mOutputIndex;
56 |
57 | // Holds move and dirty rects
58 | std::shared_ptr> mRectBuffer;
59 |
60 | std::shared_ptr mDesktopPointer;
61 | };
--------------------------------------------------------------------------------
/VideoLibrary/ScreenMediaSinkWriter.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "DxResource.h"
22 | #include "ScreenMediaSinkWriter.h"
23 |
24 | using namespace winrt::Windows::Media::MediaProperties;
25 |
26 | ScreenMediaSinkWriter::ScreenMediaSinkWriter(const EncodingContext& encodingContext)
27 | : mVideoInputMediaType{ encodingContext.videoInputMediaType }
28 | , mAudioInputMediaType{ encodingContext.audioInputMediaType }
29 | , mIsWriting{ false }
30 | , mWriteStartTime{ std::chrono::nanoseconds{ MAXLONGLONG } }
31 | , mDevice{ encodingContext.device }
32 | , mAudioStreamIndex { 0 }
33 | {
34 | auto mediaEncodingProfile = MediaEncodingProfile::CreateMp4(encodingContext.resolutionOption);
35 |
36 | auto videoProps = mediaEncodingProfile.Video();
37 | auto bitRate = videoProps.Bitrate();
38 | auto frameRate = videoProps.FrameRate();
39 | auto width = videoProps.Width();
40 | auto height = videoProps.Height();
41 | auto profile = videoProps.ProfileId();
42 |
43 | if (encodingContext.resolutionOption == ResolutionOption::Auto)
44 | {
45 | winrt::check_hresult(MFGetAttributeSize(mVideoInputMediaType.get(), MF_MT_FRAME_SIZE, &width, &height));
46 | }
47 |
48 | if (encodingContext.frameRate != 0)
49 | {
50 | frameRate.Numerator(encodingContext.frameRate);
51 | frameRate.Denominator(1);
52 | }
53 |
54 | if (encodingContext.bitRate != 0)
55 | {
56 | bitRate = encodingContext.bitRate;
57 | }
58 |
59 | mVideoFrameDuration = (10 * 1000 * 1000) / (frameRate.Numerator() / frameRate.Denominator());
60 |
61 | // init
62 | winrt::check_hresult(MFCreateDXGIDeviceManager(&mManagerResetToken, mDeviceManager.put()));
63 |
64 | winrt::check_hresult(mDeviceManager->ResetDevice(mDevice.get(), mManagerResetToken));
65 |
66 | // create attributes
67 | winrt::check_hresult(MFCreateAttributes(mSinkWriterAttributes.put(), 4));
68 | winrt::check_hresult(mSinkWriterAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, true));
69 | winrt::check_hresult(mSinkWriterAttributes->SetUINT32(MF_LOW_LATENCY, true));
70 | winrt::check_hresult(mSinkWriterAttributes->SetUINT32(MF_SINK_WRITER_DISABLE_THROTTLING, true));
71 | winrt::check_hresult(mSinkWriterAttributes->SetUnknown(MF_SINK_WRITER_D3D_MANAGER, mDeviceManager.get()));
72 |
73 | // create sink writer;
74 | winrt::check_hresult(MFCreateSinkWriterFromURL(
75 | encodingContext.fileName.c_str(),
76 | nullptr,
77 | mSinkWriterAttributes.get(),
78 | mSinkWriter.put()));
79 |
80 | // Create video output media type
81 | winrt::check_hresult(MFCreateMediaType(mVideoOutputMediaType.put()));
82 | winrt::check_hresult(mVideoOutputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video));
83 | winrt::check_hresult(mVideoOutputMediaType->SetUINT32(MF_MT_MPEG2_PROFILE, profile));
84 | winrt::check_hresult(mVideoOutputMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264));
85 |
86 | winrt::check_hresult(mVideoOutputMediaType->SetUINT32(MF_MT_AVG_BITRATE, bitRate));
87 | winrt::check_hresult(mVideoOutputMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive));
88 | winrt::check_hresult(MFSetAttributeSize(mVideoOutputMediaType.get(), MF_MT_FRAME_SIZE, width, height));
89 | winrt::check_hresult(MFSetAttributeRatio(mVideoOutputMediaType.get(), MF_MT_FRAME_RATE, frameRate.Numerator(), frameRate.Denominator()));
90 | winrt::check_hresult(mSinkWriter->AddStream(mVideoOutputMediaType.get(), &mVideoStreamIndex));
91 |
92 | // set video input media type
93 | winrt::check_hresult(mSinkWriter->SetInputMediaType(mVideoStreamIndex, mVideoInputMediaType.get(), nullptr));
94 |
95 | if (!mAudioInputMediaType)
96 | {
97 | return;
98 | }
99 |
100 | auto audioQuality = encodingContext.audioQuality;
101 |
102 | if (audioQuality == AudioQuality::Auto)
103 | {
104 | audioQuality = AudioQuality::Medium;
105 | }
106 |
107 | auto audioProps = MediaEncodingProfile::CreateM4a(audioQuality).Audio();
108 |
109 | // create audio output media type
110 |
111 | auto audioBitsPerSample = audioProps.BitsPerSample();
112 | auto audioSampleRate = audioProps.SampleRate();
113 | auto audioNumChannels = audioProps.ChannelCount();
114 | auto audioBitrate = audioProps.Bitrate() / 8;
115 |
116 | // AAC output media type https://msdn.microsoft.com/en-us/library/dd742785(v=vs.85).aspx
117 | winrt::check_hresult(MFCreateMediaType(mAudioOutputMediaType.put()));
118 | winrt::check_hresult(mAudioOutputMediaType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));
119 | winrt::check_hresult(mAudioOutputMediaType->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_AAC));
120 | winrt::check_hresult(mAudioOutputMediaType->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, audioBitsPerSample));
121 | winrt::check_hresult(mAudioOutputMediaType->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, audioSampleRate));
122 | winrt::check_hresult(mAudioOutputMediaType->SetUINT32(MF_MT_AUDIO_NUM_CHANNELS, audioNumChannels));
123 | winrt::check_hresult(mAudioOutputMediaType->SetUINT32(MF_MT_AUDIO_AVG_BYTES_PER_SECOND, audioBitrate));
124 |
125 | constexpr UINT32 aacPayloadType = 0; // stream contains raw_data_block elements only
126 | winrt::check_hresult(mAudioOutputMediaType->SetUINT32(MF_MT_AAC_PAYLOAD_TYPE, aacPayloadType));
127 |
128 | // value of the audioProfileLevelIndication field, as defined by ISO/IEC 14496-3
129 | constexpr UINT32 aacProfileLevel = 0x29;
130 | winrt::check_hresult(mAudioOutputMediaType->SetUINT32(MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, aacProfileLevel));
131 |
132 | winrt::check_hresult(mSinkWriter->AddStream(mAudioOutputMediaType.get(), &mAudioStreamIndex));
133 |
134 | // set audio input media type
135 | winrt::check_hresult(mSinkWriter->SetInputMediaType(mAudioStreamIndex, mAudioInputMediaType.get(), nullptr));
136 | }
137 |
138 | void ScreenMediaSinkWriter::Begin()
139 | {
140 | std::lock_guard lock{ mMutex };
141 | mIsWriting = true;
142 | try
143 | {
144 | winrt::check_hresult(mSinkWriter->BeginWriting());
145 | mWriteStartTime = std::chrono::high_resolution_clock::now();
146 | }
147 | catch (...)
148 | {
149 | mIsWriting = false;
150 | throw;
151 | }
152 | }
153 |
154 | void ScreenMediaSinkWriter::SignalGap()
155 | {
156 | std::lock_guard lock{ mMutex };
157 | if (!mIsWriting)
158 | {
159 | throw std::bad_function_call();
160 | }
161 |
162 | auto frameCaptureTime = std::chrono::high_resolution_clock::now();
163 | auto frameTime = (frameCaptureTime - mWriteStartTime).count() / 100;
164 |
165 | mSinkWriter->SendStreamTick(mVideoStreamIndex, frameTime);
166 | }
167 |
168 | void ScreenMediaSinkWriter::ResetDevice(winrt::com_ptr device)
169 | {
170 | std::lock_guard lock{ mMutex };
171 | if (!mIsWriting)
172 | {
173 | throw std::bad_function_call();
174 | }
175 |
176 | mDeviceManager->ResetDevice(device.get(), mManagerResetToken);
177 | mDevice = device;
178 | }
179 |
180 | void ScreenMediaSinkWriter::WriteSample(IMFSample* sample)
181 | {
182 | std::lock_guard lock{ mMutex };
183 | if (!mIsWriting)
184 | {
185 | throw std::bad_function_call();
186 | }
187 |
188 | GUID sampleType;
189 | winrt::check_hresult(sample->GetGUID(MF_MT_MAJOR_TYPE, &sampleType));
190 |
191 | if (sampleType == MFMediaType_Video)
192 | {
193 | auto frameCaptureTime = std::chrono::high_resolution_clock::now();
194 | auto frameTime = (frameCaptureTime - mWriteStartTime).count() / 100;
195 |
196 | winrt::check_hresult(sample->SetSampleTime(frameTime));
197 | winrt::check_hresult(sample->SetSampleDuration(mVideoFrameDuration));
198 |
199 | mSinkWriter->WriteSample(mVideoStreamIndex, sample);
200 | }
201 | else if (sampleType == MFMediaType_Audio)
202 | {
203 | LONGLONG sampleTime;
204 | sample->GetSampleTime(&sampleTime);
205 | auto startTime = mWriteStartTime.time_since_epoch().count() / 100;
206 | sampleTime = sampleTime - startTime;
207 | sample->SetSampleTime(sampleTime);
208 | mSinkWriter->WriteSample(mAudioStreamIndex, sample);
209 | }
210 | }
211 |
212 | void ScreenMediaSinkWriter::End()
213 | {
214 | std::lock_guard lock{ mMutex };
215 |
216 | if (!mIsWriting) {
217 | throw std::exception("End called when ScreenMediaSinkWriter was not writing");
218 | }
219 |
220 | winrt::check_hresult(mSinkWriter->Flush(mVideoStreamIndex));
221 | if (mAudioInputMediaType)
222 | {
223 | winrt::check_hresult(mSinkWriter->Flush(mAudioStreamIndex));
224 | }
225 | winrt::check_hresult(mSinkWriter->Finalize());
226 | mIsWriting = false;
227 | }
228 |
229 | ScreenMediaSinkWriter::~ScreenMediaSinkWriter()
230 | {
231 | if (mIsWriting)
232 | {
233 | this->End();
234 | }
235 | }
236 |
--------------------------------------------------------------------------------
/VideoLibrary/ScreenMediaSinkWriter.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 |
22 | #include
23 | #include "EncodingContext.h"
24 |
25 | class ScreenMediaSinkWriter
26 | {
27 | public:
28 |
29 | ScreenMediaSinkWriter(const EncodingContext& encodingContext);
30 |
31 | void Begin();
32 |
33 | void SignalGap();
34 |
35 | void ResetDevice(winrt::com_ptr device);
36 |
37 | void WriteSample(IMFSample* sample);
38 |
39 | void End();
40 |
41 | virtual ~ScreenMediaSinkWriter();
42 |
43 | private:
44 | winrt::com_ptr mSinkWriter;
45 | UINT mManagerResetToken;
46 | winrt::com_ptr mDeviceManager;
47 | winrt::com_ptr mSinkWriterAttributes;
48 | winrt::com_ptr mVideoInputMediaType;
49 | winrt::com_ptr mVideoOutputMediaType;
50 | winrt::com_ptr mAudioInputMediaType;
51 | winrt::com_ptr mAudioOutputMediaType;
52 | winrt::com_ptr mDevice;
53 | DWORD mVideoStreamIndex;
54 | DWORD mAudioStreamIndex;
55 | bool mIsWriting;
56 | std::chrono::high_resolution_clock::time_point mWriteStartTime;
57 | UINT32 mVideoFrameDuration;
58 |
59 | std::mutex mMutex;
60 | };
61 |
--------------------------------------------------------------------------------
/VideoLibrary/ShaderCache.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #include "pch.h"
21 | #include "ShaderCache.h"
22 | // pch.h MUST INCLUDE THE PIXEL SHADER AND VERTEX SHADER
23 |
24 | void ShaderCache::Initialize(winrt::com_ptr device)
25 | {
26 | // create vertex shader
27 | const auto vertexShaderArraySize = ARRAYSIZE(g_VertexShaderRawData);
28 | winrt::check_hresult(device->CreateVertexShader(
29 | g_VertexShaderRawData,
30 | vertexShaderArraySize,
31 | nullptr,
32 | mVertexShader.put())
33 | );
34 |
35 | // Create the input layout for the vertex shader
36 | // Name Index Mask Register SysValue Format Used
37 | // -------------------- ----- ------ -------- -------- ------- ------
38 | // POSITION 0 xyzw 0 NONE float xyzw
39 | // TEXCOORD 0 xy 1 NONE float xy
40 | //
41 | const std::vector inputDesc = {
42 | D3D11_INPUT_ELEMENT_DESC{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
43 | D3D11_INPUT_ELEMENT_DESC{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
44 | };
45 | winrt::com_ptr inputLayout;
46 | winrt::check_hresult(device->CreateInputLayout(
47 | inputDesc.data(),
48 | static_cast(inputDesc.size()),
49 | g_VertexShaderRawData,
50 | vertexShaderArraySize,
51 | mVertexShaderInputLayout.put()
52 | ));
53 | winrt::com_ptr context;
54 | device->GetImmediateContext(context.put());
55 | context->IASetInputLayout(mVertexShaderInputLayout.get());
56 |
57 | // create pixel shader
58 | auto pixelShaderArraySize = ARRAYSIZE(g_PixelShaderRawData);
59 | winrt::check_hresult(device->CreatePixelShader(
60 | g_PixelShaderRawData,
61 | pixelShaderArraySize,
62 | nullptr,
63 | mPixelShader.put())
64 | );
65 |
66 | // Set up sampler
67 | D3D11_SAMPLER_DESC SampDesc;
68 | RtlZeroMemory(&SampDesc, sizeof(SampDesc));
69 | SampDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;
70 | SampDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
71 | SampDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
72 | SampDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
73 | SampDesc.ComparisonFunc = D3D11_COMPARISON_NEVER;
74 | SampDesc.MinLOD = 0;
75 | SampDesc.MaxLOD = D3D11_FLOAT32_MAX;
76 | winrt::check_hresult(device->CreateSamplerState(&SampDesc, mLinearSampler.put()));
77 |
78 | // Blend state
79 | // Create the blend state
80 | D3D11_BLEND_DESC BlendStateDesc;
81 | BlendStateDesc.AlphaToCoverageEnable = FALSE;
82 | BlendStateDesc.IndependentBlendEnable = FALSE;
83 | BlendStateDesc.RenderTarget[0].BlendEnable = TRUE;
84 | BlendStateDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
85 | BlendStateDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
86 | BlendStateDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
87 | BlendStateDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
88 | BlendStateDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
89 | BlendStateDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
90 | BlendStateDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
91 | winrt::check_hresult(device->CreateBlendState(&BlendStateDesc, mBlendState.put()));
92 | }
93 |
94 | ShaderCache::ShaderCache(winrt::com_ptr device)
95 | {
96 | Initialize(device);
97 | }
98 |
99 | ShaderCache::~ShaderCache()
100 | {
101 | }
102 |
103 | winrt::com_ptr ShaderCache::VertexShader()
104 | {
105 | return mVertexShader;
106 | }
107 |
108 | winrt::com_ptr ShaderCache::VertexShaderInputLayout()
109 | {
110 | return mVertexShaderInputLayout;
111 | }
112 |
113 | winrt::com_ptr ShaderCache::PixelShader()
114 | {
115 | return mPixelShader;
116 | }
117 |
118 | winrt::com_ptr ShaderCache::LinearSampler()
119 | {
120 | return mLinearSampler;
121 | }
122 |
123 | winrt::com_ptr ShaderCache::BlendState()
124 | {
125 | return mBlendState;
126 | }
127 |
--------------------------------------------------------------------------------
/VideoLibrary/ShaderCache.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (C) 2022 by Julio Gutierrez (desktoprecorderapp@gmail.com)
3 |
4 | This file is part of DesktopRecorderLibrary.
5 |
6 | DesktopRecorderLibrary is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by the
8 | Free Software Foundation, either version 3 of the License,
9 | or (at your option) any later version.
10 |
11 | DesktopRecorderLibrary is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with DesktopRecorderLibrary. If not, see .
18 | */
19 |
20 | #pragma once
21 | class ShaderCache
22 | {
23 | public:
24 |
25 | ShaderCache(winrt::com_ptr device);
26 |
27 | virtual ~ShaderCache();
28 |
29 | winrt::com_ptr