├── .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 VertexShader(); 30 | 31 | winrt::com_ptr VertexShaderInputLayout(); 32 | 33 | winrt::com_ptr PixelShader(); 34 | 35 | winrt::com_ptr LinearSampler(); 36 | 37 | winrt::com_ptr BlendState(); 38 | 39 | private: 40 | 41 | void Initialize(winrt::com_ptr device); 42 | 43 | winrt::com_ptr mVertexShader; 44 | winrt::com_ptr mVertexShaderInputLayout; 45 | winrt::com_ptr mPixelShader; 46 | winrt::com_ptr mLinearSampler; 47 | winrt::com_ptr mBlendState; 48 | }; 49 | -------------------------------------------------------------------------------- /VideoLibrary/SharedSurface.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 "SharedSurface.h" 22 | 23 | SharedSurface::SharedSurface(winrt::com_ptr device, int width, int height) 24 | : mDevice{ device } 25 | , mSharedSurface{ nullptr } 26 | , mMutex { nullptr } 27 | , mRotatingKeys { new RotatingKeys } 28 | , mWidth{ width } 29 | , mHeight{ height } 30 | { 31 | D3D11_TEXTURE2D_DESC desc; 32 | RtlZeroMemory(&desc, sizeof(D3D11_TEXTURE2D_DESC)); 33 | desc.Width = static_cast(width); 34 | desc.Height = static_cast(height); 35 | desc.MipLevels = 1; 36 | desc.ArraySize = 1; 37 | desc.SampleDesc.Count = 1; 38 | desc.CPUAccessFlags = 0; 39 | desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 40 | desc.Usage = D3D11_USAGE_DEFAULT; 41 | desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; 42 | desc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; 43 | 44 | winrt::check_hresult(mDevice->CreateTexture2D(&desc, nullptr, mSharedSurface.put())); 45 | mMutex = mSharedSurface.as(); 46 | winrt::check_pointer(mMutex.get()); 47 | mSharedSurface->GetDesc(&mDesc); 48 | } 49 | 50 | SharedSurface::SharedSurface( 51 | winrt::com_ptr device, 52 | winrt::com_ptr texture, 53 | std::shared_ptr rotatingKeys, 54 | int width, 55 | int height) 56 | : mDevice{ device } 57 | , mSharedSurface{ texture } 58 | , mMutex{ mSharedSurface.as() } 59 | , mRotatingKeys { rotatingKeys } 60 | , mWidth{ width } 61 | , mHeight{ height } 62 | { 63 | winrt::check_pointer(mDevice.get()); 64 | winrt::check_pointer(mMutex.get()); 65 | winrt::check_pointer(mSharedSurface.get()); 66 | mSharedSurface->GetDesc(&mDesc); 67 | } 68 | 69 | std::unique_ptr SharedSurface::Lock() const 70 | { 71 | return std::make_unique(mSharedSurface, mMutex, mRotatingKeys); 72 | } 73 | 74 | std::shared_ptr SharedSurface::OpenSharedSurfaceWithDevice(winrt::com_ptr device) const 75 | { 76 | HANDLE handle = nullptr; 77 | winrt::com_ptr resource = mSharedSurface.as(); 78 | winrt::check_hresult(resource->GetSharedHandle(&handle)); 79 | 80 | winrt::com_ptr sharedSurface; 81 | winrt::check_hresult(device->OpenSharedResource(handle, __uuidof(ID3D11Texture2D), sharedSurface.put_void())); 82 | return std::make_shared(device, sharedSurface, mRotatingKeys, mWidth, mHeight); 83 | } 84 | 85 | -------------------------------------------------------------------------------- /VideoLibrary/SharedSurface.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 "KeyedMutexLock.h" 23 | 24 | class SharedSurface 25 | { 26 | public: 27 | SharedSurface(winrt::com_ptr device, int width, int height); 28 | SharedSurface(winrt::com_ptr device, winrt::com_ptr texture, std::shared_ptr rotatingKeys, int width, int height); 29 | 30 | winrt::com_ptr Device() const { return mDevice; } 31 | std::unique_ptr Lock() const; 32 | std::shared_ptr OpenSharedSurfaceWithDevice(winrt::com_ptr device) const; 33 | D3D11_TEXTURE2D_DESC Desc() const { return mDesc; } 34 | private: 35 | 36 | winrt::com_ptr mSharedSurface; 37 | std::shared_ptr mRotatingKeys; 38 | winrt::com_ptr mMutex; 39 | winrt::com_ptr mDevice; 40 | D3D11_TEXTURE2D_DESC mDesc; 41 | int mWidth; 42 | int mHeight; 43 | }; 44 | 45 | -------------------------------------------------------------------------------- /VideoLibrary/SourceState.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 | enum class SourceState 23 | { 24 | Invalid, // Initial state. Have not started opening the stream. 25 | Opening, // BeginOpen is in progress. 26 | Stopped, 27 | Started, 28 | Recovering, 29 | Shutdown 30 | }; 31 | -------------------------------------------------------------------------------- /VideoLibrary/TexturePool.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 "RecordingStep.h" 23 | #include "CaptureFrameStep.h" 24 | #include "VirtualDesktop.h" 25 | #include "TexturePool.h" 26 | 27 | TexturePool::TexturePool(winrt::com_ptr device, const D3D11_TEXTURE2D_DESC desc) 28 | : mDevice{ device } 29 | , mTextureDesc{ desc } 30 | , m_refCount{ 1 } 31 | { 32 | } 33 | 34 | winrt::com_ptr TexturePool::Acquire() 35 | { 36 | std::lock_guard lock{ mMutex }; 37 | if (mTexturePool.empty()) { 38 | return CreateTexture(); 39 | } 40 | 41 | auto texture = mTexturePool.front(); 42 | mTexturePool.pop(); 43 | return texture; 44 | } 45 | 46 | HRESULT __stdcall TexturePool::GetParameters(DWORD * pdwFlags, DWORD * pdwQueue) 47 | { 48 | UNREFERENCED_PARAMETER(pdwFlags); 49 | UNREFERENCED_PARAMETER(pdwQueue); 50 | return E_NOTIMPL; 51 | } 52 | 53 | HRESULT __stdcall TexturePool::Invoke(IMFAsyncResult * pAsyncResult) 54 | { 55 | winrt::com_ptr unknown; 56 | winrt::check_hresult(pAsyncResult->GetObjectW(unknown.put())); 57 | 58 | auto sample = unknown.as(); 59 | 60 | winrt::com_ptr mediaBuffer; 61 | winrt::check_hresult(sample->GetBufferByIndex(0, mediaBuffer.put())); 62 | 63 | auto dxgiBuffer = mediaBuffer.as(); 64 | 65 | winrt::com_ptr texture; 66 | winrt::check_hresult(dxgiBuffer->GetResource(IID_PPV_ARGS(texture.put()))); 67 | 68 | { 69 | std::lock_guard lock{ mMutex }; 70 | mTexturePool.push(texture); 71 | } 72 | 73 | return S_OK; 74 | } 75 | 76 | winrt::com_ptr TexturePool::CreateTexture() 77 | { 78 | D3D11_TEXTURE2D_DESC moveDesc = mTextureDesc; 79 | moveDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; 80 | moveDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED; 81 | winrt::com_ptr texture; 82 | mDevice->CreateTexture2D(&moveDesc, nullptr, texture.put()); 83 | return texture; 84 | } 85 | 86 | HRESULT TexturePool::QueryInterface(REFIID riid, void** ppv) noexcept 87 | { 88 | static const QITAB qit[] = 89 | { 90 | QITABENT(TexturePool, IMFAsyncCallback), 91 | { 0 } 92 | }; 93 | return QISearch(this, qit, riid, ppv); 94 | } 95 | 96 | TexturePool::~TexturePool() 97 | { 98 | assert(m_refCount == 0); 99 | } 100 | -------------------------------------------------------------------------------- /VideoLibrary/TexturePool.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 "DesktopMonitor.h" 23 | 24 | class TexturePool : public IMFAsyncCallback 25 | { 26 | public: 27 | 28 | TexturePool(winrt::com_ptr device, D3D11_TEXTURE2D_DESC desc); 29 | 30 | winrt::com_ptr Acquire(); 31 | 32 | virtual HRESULT STDMETHODCALLTYPE GetParameters(DWORD* pdwFlags, DWORD* pdwQueue) override; 33 | 34 | virtual HRESULT STDMETHODCALLTYPE Invoke(IMFAsyncResult* pAsyncResult) override; 35 | 36 | STDMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_refCount); } 37 | STDMETHODIMP_(ULONG) Release() 38 | { 39 | assert(m_refCount > 0); 40 | ULONG uCount = InterlockedDecrement(&m_refCount); 41 | if (uCount == 0) 42 | { 43 | delete this; 44 | } 45 | return uCount; 46 | } 47 | virtual HRESULT QueryInterface(REFIID riid, void** ppv) noexcept override; 48 | 49 | virtual ~TexturePool(); 50 | 51 | private: 52 | 53 | winrt::com_ptr CreateTexture(); 54 | 55 | winrt::com_ptr mDevice; 56 | const D3D11_TEXTURE2D_DESC mTextureDesc; 57 | std::queue> mTexturePool; 58 | std::mutex mMutex; 59 | volatile long m_refCount; 60 | 61 | }; 62 | -------------------------------------------------------------------------------- /VideoLibrary/TextureToMediaSampleStep.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 "TexturePool.h" 22 | #include "TextureToMediaSampleStep.h" 23 | 24 | TextureToMediaSampleStep::TextureToMediaSampleStep( 25 | winrt::com_ptr sourceTexture, 26 | winrt::com_ptr texturePool) 27 | : mSourceTexture{ sourceTexture } 28 | , mTexturePool{ texturePool } 29 | { 30 | winrt::check_pointer(sourceTexture.get()); 31 | winrt::check_pointer(texturePool.get()); 32 | } 33 | 34 | TextureToMediaSampleStep::~TextureToMediaSampleStep() 35 | { 36 | } 37 | 38 | void TextureToMediaSampleStep::Perform() 39 | { 40 | winrt::com_ptr mediaBuffer; 41 | winrt::check_hresult(MFCreateDXGISurfaceBuffer( 42 | __uuidof(ID3D11Texture2D), mSourceTexture.get(), 0, true, mediaBuffer.put())); 43 | 44 | auto mediaBuffer2D{ mediaBuffer.as() }; 45 | 46 | DWORD length = 0; 47 | 48 | winrt::check_hresult(mediaBuffer2D->GetContiguousLength(&length)); 49 | winrt::check_hresult(mediaBuffer->SetCurrentLength(length)); 50 | 51 | winrt::check_hresult(MFCreateVideoSampleFromSurface(nullptr, mSample.put())); 52 | 53 | auto trackedSample = mSample.as(); 54 | 55 | winrt::check_hresult(trackedSample->SetAllocator(mTexturePool.get(), trackedSample.get())); 56 | 57 | winrt::check_hresult(mSample->AddBuffer(mediaBuffer.get())); 58 | } 59 | 60 | winrt::com_ptr TextureToMediaSampleStep::Result() 61 | { 62 | return mSample; 63 | } 64 | -------------------------------------------------------------------------------- /VideoLibrary/TextureToMediaSampleStep.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 | 24 | class TextureToMediaSampleStep : public RecordingStep 25 | { 26 | public: 27 | TextureToMediaSampleStep( 28 | winrt::com_ptr sourceTexture, 29 | winrt::com_ptr texturePool); 30 | 31 | virtual ~TextureToMediaSampleStep(); 32 | 33 | // Inherited via RecordingStep 34 | virtual void Perform() override; 35 | 36 | winrt::com_ptr Result(); 37 | 38 | private: 39 | winrt::com_ptr mSourceTexture; 40 | winrt::com_ptr mTexturePool; 41 | winrt::com_ptr mSample; 42 | }; 43 | -------------------------------------------------------------------------------- /VideoLibrary/Vertex.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 Position 23 | { 24 | float x, y, z; 25 | }; 26 | 27 | struct TexCoord 28 | { 29 | float u, v; 30 | }; 31 | 32 | struct Vertex 33 | { 34 | Position pos; 35 | TexCoord texCoord; 36 | }; 37 | 38 | constexpr int g_VerticesPerRect = 6; 39 | -------------------------------------------------------------------------------- /VideoLibrary/VertexShader.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 | struct VS_INPUT 21 | { 22 | float4 Pos : POSITION; 23 | float2 Tex : TEXCOORD; 24 | }; 25 | 26 | struct VS_OUTPUT 27 | { 28 | float4 Pos : SV_POSITION; 29 | float2 Tex : TEXCOORD; 30 | }; 31 | 32 | 33 | //-------------------------------------------------------------------------------------- 34 | // Vertex Shader 35 | //-------------------------------------------------------------------------------------- 36 | VS_OUTPUT main(VS_INPUT input) 37 | { 38 | return input; 39 | } -------------------------------------------------------------------------------- /VideoLibrary/VideoLibrary.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 | {765a2b4c-d921-4320-ac55-00a5cb151103} 14 | 15 | 16 | 17 | 18 | Header Files 19 | 20 | 21 | Header Files 22 | 23 | 24 | Header Files 25 | 26 | 27 | Recording Steps 28 | 29 | 30 | Header Files 31 | 32 | 33 | Recording Steps 34 | 35 | 36 | Header Files 37 | 38 | 39 | Recording Steps 40 | 41 | 42 | Header Files 43 | 44 | 45 | Recording Steps 46 | 47 | 48 | Header Files 49 | 50 | 51 | Header Files 52 | 53 | 54 | Header Files 55 | 56 | 57 | Header Files 58 | 59 | 60 | Header Files 61 | 62 | 63 | Header Files 64 | 65 | 66 | Header Files 67 | 68 | 69 | Recording Steps 70 | 71 | 72 | Recording Steps 73 | 74 | 75 | Recording Steps 76 | 77 | 78 | Header Files 79 | 80 | 81 | Header Files 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | 100 | 101 | Source Files 102 | 103 | 104 | Source Files 105 | 106 | 107 | Source Files 108 | 109 | 110 | Recording Steps 111 | 112 | 113 | Source Files 114 | 115 | 116 | Recording Steps 117 | 118 | 119 | Source Files 120 | 121 | 122 | Recording Steps 123 | 124 | 125 | Recording Steps 126 | 127 | 128 | Source Files 129 | 130 | 131 | Source Files 132 | 133 | 134 | Source Files 135 | 136 | 137 | Source Files 138 | 139 | 140 | Source Files 141 | 142 | 143 | Recording Steps 144 | 145 | 146 | Recording Steps 147 | 148 | 149 | Recording Steps 150 | 151 | 152 | Source Files 153 | 154 | 155 | Source Files 156 | 157 | 158 | Source Files 159 | 160 | 161 | Source Files 162 | 163 | 164 | Source Files 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Source Files 173 | 174 | 175 | Source Files 176 | 177 | 178 | -------------------------------------------------------------------------------- /VideoLibrary/VirtualDesktop.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 "DxResource.h" 23 | #include "DisplayAdapter.h" 24 | #include "VirtualDesktop.h" 25 | 26 | VirtualDesktop::VirtualDesktop() 27 | : mDesktopMonitors { VirtualDesktop::GetAllDesktopMonitors() } 28 | , mVirtualDesktopBounds { VirtualDesktop::CalculateDesktopMonitorBounds(mDesktopMonitors) } 29 | { 30 | } 31 | 32 | VirtualDesktop::~VirtualDesktop() 33 | { 34 | } 35 | 36 | std::vector VirtualDesktop::GetAllDesktopMonitors() 37 | { 38 | std::vector desktopMonitors; 39 | auto factory = DxResource::MakeDxgiFactory(); 40 | 41 | winrt::com_ptr adapter = nullptr; 42 | for (int adapterIndex = 0; 43 | DXGI_ERROR_NOT_FOUND != factory->EnumAdapters1(adapterIndex, adapter.put()); 44 | adapter = nullptr, adapterIndex++) { 45 | 46 | DXGI_ADAPTER_DESC1 adapterDesc; 47 | winrt::check_hresult(adapter->GetDesc1(&adapterDesc)); 48 | 49 | // software adapter; skip 50 | if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { 51 | continue; 52 | } 53 | 54 | auto displayAdapter = std::make_shared(adapter); 55 | winrt::com_ptr output = nullptr; 56 | for (int outputIndex = 0; 57 | DXGI_ERROR_NOT_FOUND != adapter->EnumOutputs(outputIndex, output.put()); 58 | output = nullptr, outputIndex++) { 59 | 60 | winrt::com_ptr output1{ output.as() }; 61 | DXGI_OUTPUT_DESC outputDesc; 62 | winrt::check_hresult(output1->GetDesc(&outputDesc)); 63 | 64 | if (outputDesc.AttachedToDesktop) { 65 | DesktopMonitor desktopMonitor{ displayAdapter, output1, outputIndex }; 66 | desktopMonitors.push_back(desktopMonitor); 67 | } 68 | } 69 | } 70 | 71 | return desktopMonitors; 72 | } 73 | 74 | RECT VirtualDesktop::CalculateDesktopMonitorBounds(const std::vector& desktopMonitors) 75 | { 76 | // determine desktop bounds 77 | auto bounds = RECT{ INT_MAX, INT_MAX, INT_MIN, INT_MIN }; 78 | for (const auto& monitor : desktopMonitors) 79 | { 80 | RECT monitorBounds = monitor.DesktopMonitorBounds(); 81 | bounds.left = std::min(bounds.left, monitorBounds.left); 82 | bounds.top = std::min(bounds.top, monitorBounds.top); 83 | bounds.right = std::max(bounds.right, monitorBounds.right); 84 | bounds.bottom = std::max(bounds.bottom, monitorBounds.bottom); 85 | } 86 | 87 | return bounds; 88 | } 89 | 90 | RECT VirtualDesktop::VirtualDesktopBounds() const 91 | { 92 | return mVirtualDesktopBounds; 93 | } 94 | 95 | std::vector VirtualDesktop::DesktopMonitors() const 96 | { 97 | return mDesktopMonitors; 98 | } -------------------------------------------------------------------------------- /VideoLibrary/VirtualDesktop.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 "DesktopMonitor.h" 23 | #include "ScreenDuplicator.h" 24 | #include "KeyedMutexLock.h" 25 | 26 | class VirtualDesktop 27 | { 28 | public: 29 | VirtualDesktop(); 30 | 31 | ~VirtualDesktop(); 32 | 33 | static std::vector GetAllDesktopMonitors(); 34 | 35 | std::vector DesktopMonitors() const; 36 | RECT VirtualDesktopBounds() const; 37 | 38 | static RECT CalculateDesktopMonitorBounds(const std::vector& desktopMonitors); 39 | 40 | private: 41 | 42 | std::vector mDesktopMonitors; 43 | RECT mVirtualDesktopBounds; 44 | }; 45 | 46 | -------------------------------------------------------------------------------- /VideoLibrary/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /VideoLibrary/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 | -------------------------------------------------------------------------------- /VideoLibrary/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 | #define NOMINMAX // Don't let windows headers define min and max macros 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #include "VertexShader.h" 51 | #include "PixelShader.h" 52 | #include "Vertex.h" 53 | -------------------------------------------------------------------------------- /VideoLibraryTests/AudioTests.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 "stdafx.h" 21 | #include "CppUnitTest.h" 22 | 23 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 24 | 25 | #include "..\VideoLibrary\DxResource.h" 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | namespace VideoLibraryTests 33 | { 34 | class CBaseMediaBuffer : public IMediaBuffer { 35 | public: 36 | CBaseMediaBuffer() {} 37 | CBaseMediaBuffer(BYTE *pData, ULONG ulSize, ULONG ulData) : 38 | m_pData(pData), m_ulSize(ulSize), m_ulData(ulData), m_cRef(1) {} 39 | STDMETHODIMP_(ULONG) AddRef() { 40 | return InterlockedIncrement((long*)&m_cRef); 41 | } 42 | STDMETHODIMP_(ULONG) Release() { 43 | long l = InterlockedDecrement((long*)&m_cRef); 44 | if (l == 0) 45 | delete this; 46 | return l; 47 | } 48 | STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { 49 | if (riid == IID_IUnknown) { 50 | AddRef(); 51 | *ppv = (IUnknown*)this; 52 | return NOERROR; 53 | } 54 | else if (riid == IID_IMediaBuffer) { 55 | AddRef(); 56 | *ppv = (IMediaBuffer*)this; 57 | return NOERROR; 58 | } 59 | else 60 | return E_NOINTERFACE; 61 | } 62 | STDMETHODIMP SetLength(DWORD ulLength) { m_ulData = ulLength; return NOERROR; } 63 | STDMETHODIMP GetMaxLength(DWORD *pcbMaxLength) { *pcbMaxLength = m_ulSize; return NOERROR; } 64 | STDMETHODIMP GetBufferAndLength(BYTE **ppBuffer, DWORD *pcbLength) { 65 | if (ppBuffer) *ppBuffer = m_pData; 66 | if (pcbLength) *pcbLength = m_ulData; 67 | return NOERROR; 68 | } 69 | protected: 70 | BYTE *m_pData; 71 | ULONG m_ulSize; 72 | ULONG m_ulData; 73 | ULONG m_cRef; 74 | }; 75 | 76 | class CStaticMediaBuffer : public CBaseMediaBuffer { 77 | public: 78 | STDMETHODIMP_(ULONG) AddRef() { return 2; } 79 | STDMETHODIMP_(ULONG) Release() { return 1; } 80 | void Init(BYTE *pData, ULONG ulSize, ULONG ulData) { 81 | m_pData = pData; 82 | m_ulSize = ulSize; 83 | m_ulData = ulData; 84 | } 85 | }; 86 | 87 | TEST_CLASS(AudioTests) 88 | { 89 | public: 90 | 91 | TEST_METHOD(TestAECAudioCapture) 92 | { 93 | // https://github.com/microsoft/Windows-classic-samples/blob/27ffb0811ca761741502feaefdb591aebf592193/Samples/Win7Samples/multimedia/audio/aecmicarray/AecSDKDemo.cpp 94 | winrt::check_hresult(MFStartup(MF_VERSION)); 95 | CoInitialize(NULL); 96 | //winrt::init_apartment(); 97 | 98 | winrt::com_ptr voiceCaptureDsp; 99 | winrt::check_hresult(CoCreateInstance(CLSID_CWMAudioAEC, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(voiceCaptureDsp.put()))); 100 | 101 | auto properties = voiceCaptureDsp.as(); 102 | 103 | // set Audio echo cancellation and Microphone array processing modes 104 | /* 105 | * For ARC modes (0 and 4), must play audio from speaker, as if 106 | * to simulate a conference call where the application will play 107 | * audio output via the speakers as others in the call are speaking 108 | AEC only mode: SINGLE_CHANNEL_AEC (0) 109 | MicArray only mode: OPTIBEAM_ARRAY_ONLY (2) 110 | AEC + MicArray mode: OPTIBEAM_ARRAY_AND_AEC (4) 111 | No AEC or MicArray: SINGLE_CHANNEL_NSAGC (5) 112 | */ 113 | PROPVARIANT prop{}; 114 | PropVariantInit(&prop); 115 | prop.vt = VT_I4; 116 | prop.lVal = OPTIBEAM_ARRAY_AND_AEC; 117 | winrt::check_hresult(properties->SetValue(MFPKEY_WMAAECMA_SYSTEM_MODE, prop)); 118 | PropVariantClear(&prop); 119 | 120 | PROPVARIANT pvDeviceId; 121 | PropVariantInit(&pvDeviceId); 122 | pvDeviceId.vt = VT_I4; 123 | pvDeviceId.lVal = (unsigned long)(0 << 16) + (unsigned long)(0x0000ffff & 0); 124 | winrt::check_hresult(properties->SetValue(MFPKEY_WMAAECMA_DEVICE_INDEXES, pvDeviceId)); 125 | winrt::check_hresult(properties->GetValue(MFPKEY_WMAAECMA_DEVICE_INDEXES, &pvDeviceId)); 126 | PropVariantClear(&pvDeviceId); 127 | 128 | // Turn on feature modes 129 | /* 130 | PROPVARIANT pvFeatrModeOn; 131 | PropVariantInit(&pvFeatrModeOn); 132 | pvFeatrModeOn.vt = VT_BOOL; 133 | pvFeatrModeOn.boolVal = (VARIANT_BOOL)-1; 134 | winrt::check_hresult(properties->SetValue(MFPKEY_WMAAECMA_FEATURE_MODE, pvFeatrModeOn)); 135 | PropVariantClear(&pvFeatrModeOn); 136 | 137 | // Turn on/off noise suppression 138 | PROPVARIANT pvNoiseSup; 139 | PropVariantInit(&pvNoiseSup); 140 | pvNoiseSup.vt = VT_I4; 141 | pvNoiseSup.lVal = (LONG)1; 142 | winrt::check_hresult(properties->SetValue(MFPKEY_WMAAECMA_FEATR_NS, pvNoiseSup)); 143 | PropVariantClear(&pvNoiseSup); 144 | 145 | // Turn on/off AGC 146 | PROPVARIANT pvAGC; 147 | PropVariantInit(&pvAGC); 148 | pvAGC.vt = VT_BOOL; 149 | pvAGC.boolVal = (VARIANT_BOOL)-1; 150 | winrt::check_hresult(properties->SetValue(MFPKEY_WMAAECMA_FEATR_AGC, pvAGC)); 151 | PropVariantClear(&pvAGC); 152 | 153 | // Turn on/off center clip 154 | PROPVARIANT pvCntrClip; 155 | PropVariantInit(&pvCntrClip); 156 | pvCntrClip.vt = VT_BOOL; 157 | pvCntrClip.boolVal = (VARIANT_BOOL)-1; 158 | winrt::check_hresult(properties->SetValue(MFPKEY_WMAAECMA_FEATR_CENTER_CLIP, pvCntrClip)); 159 | PropVariantClear(&pvCntrClip); 160 | */ 161 | 162 | WAVEFORMATEX wfxOut = { WAVE_FORMAT_PCM, 1, 16000, 32000, 2, 16, 0 }; 163 | DMO_MEDIA_TYPE mt{ 0 }; // Media type. 164 | 165 | // Allocate the format block to hold the WAVEFORMATEX structure. 166 | winrt::check_hresult(MoInitMediaType(&mt, sizeof(WAVEFORMATEX))); 167 | 168 | mt.majortype = MEDIATYPE_Audio; 169 | mt.subtype = MEDIASUBTYPE_PCM; 170 | mt.lSampleSize = 0; 171 | mt.bFixedSizeSamples = TRUE; 172 | mt.bTemporalCompression = FALSE; 173 | mt.formattype = FORMAT_WaveFormatEx; 174 | memcpy(mt.pbFormat, &wfxOut, sizeof(WAVEFORMATEX)); 175 | 176 | 177 | // Set the output type. 178 | winrt::check_hresult(voiceCaptureDsp->SetOutputType(0, &mt, 0)); 179 | // Free the format block. 180 | MoFreeMediaType(&mt); 181 | 182 | winrt::check_hresult(voiceCaptureDsp->AllocateStreamingResources()); 183 | 184 | CStaticMediaBuffer outputBuffer; 185 | DMO_OUTPUT_DATA_BUFFER OutputBufferStruct = { 0 }; 186 | OutputBufferStruct.pBuffer = &outputBuffer; 187 | DWORD status = 0; 188 | 189 | auto cOutputBufLen = wfxOut.nSamplesPerSec * wfxOut.nBlockAlign; 190 | BYTE* pbOutputBuffer = new BYTE[cOutputBufLen]; 191 | 192 | /* 193 | play with: 194 | vlc --demux=rawaud --rawaud-channels 1 --rawaud-samplerate 16000 out.pcm 195 | */ 196 | std::basic_ofstream outPcm{ "out.pcm", std::ios::binary }; 197 | 198 | // capture 199 | int i = 10 * 100; 200 | while (i-- > 0) 201 | { 202 | Sleep(10); 203 | 204 | do 205 | { 206 | outputBuffer.Init((byte*)pbOutputBuffer, cOutputBufLen, 0); 207 | OutputBufferStruct.dwStatus = 0; 208 | auto hr = voiceCaptureDsp->ProcessOutput(0, 1, &OutputBufferStruct, &status); 209 | DWORD c = 0; 210 | 211 | if (hr != S_FALSE) { 212 | winrt::check_hresult(outputBuffer.GetBufferAndLength(nullptr, &c)); 213 | } 214 | printf("%d %d", c, status); 215 | 216 | outPcm.write(pbOutputBuffer, c); 217 | } while (OutputBufferStruct.dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE); 218 | 219 | } 220 | delete[] (pbOutputBuffer); 221 | 222 | } 223 | }; 224 | } -------------------------------------------------------------------------------- /VideoLibraryTests/DesktopMonitorTests.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 "stdafx.h" 21 | #include "CppUnitTest.h" 22 | 23 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 24 | 25 | #include "..\VideoLibrary\VirtualDesktop.h" 26 | #include "..\VideoLibrary\DesktopMonitor.h" 27 | 28 | namespace VideoLibraryTests 29 | { 30 | TEST_CLASS(DesktopMonitorTests) 31 | { 32 | public: 33 | TEST_METHOD(MonitorsCanBeFound) 34 | { 35 | auto virtualDesktop = std::make_shared(); 36 | std::vector monitors = virtualDesktop->GetAllDesktopMonitors(); 37 | Assert::IsFalse(monitors.empty()); 38 | } 39 | }; 40 | } 41 | -------------------------------------------------------------------------------- /VideoLibraryTests/RecordingStepsTests.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 "stdafx.h" 21 | #include "CppUnitTest.h" 22 | 23 | using namespace Microsoft::VisualStudio::CppUnitTestFramework; 24 | 25 | #include "..\VideoLibrary\CaptureFrameStep.h" 26 | #include "..\VideoLibrary\RenderPointerTextureStep.h" 27 | #include "..\VideoLibrary\VirtualDesktop.h" 28 | #include 29 | #include 30 | 31 | namespace VideoLibraryTests 32 | { 33 | TEST_CLASS(RecordingStepsTests) 34 | { 35 | public: 36 | TEST_METHOD(RecordDesktop) 37 | { 38 | auto virtualDesktop = std::make_shared(); 39 | 40 | auto monitors = virtualDesktop->DesktopMonitors(); 41 | RotatingKeys keys{ 0, 1 }; 42 | std::shared_ptr desktopPointer = std::make_shared(virtualDesktop->VirtualDesktopBounds()); 43 | std::vector> duplicators; 44 | for (auto monitor : monitors) 45 | { 46 | std::shared_ptr duplicator = std::make_shared( 47 | monitor, 48 | desktopPointer 49 | ); 50 | duplicators.push_back(duplicator); 51 | } 52 | 53 | std::unique_ptr recordingStep; 54 | int i = 0; 55 | for (auto& duplicator : duplicators) { 56 | 57 | for (int j = 0; j < 100; ++i, ++j) { 58 | recordingStep.reset(new CaptureFrameStep{ *duplicator }); 59 | recordingStep->Perform(); 60 | } 61 | } 62 | 63 | // TODO fix 64 | /* 65 | auto step = dynamic_cast(recordingStep.get()); 66 | Assert::IsNotNull(step); 67 | step->Perform(); 68 | auto texture = step->Result(); 69 | winrt::com_ptr device; 70 | winrt::com_ptr context; 71 | texture->GetDevice(device.put()); 72 | device->GetImmediateContext(context.put()); 73 | 74 | //DirectX::SaveWICTextureToFile(context.get(), virtualDesktop->DesktopPointer().Texture().get(), GUID_ContainerFormatPng, L"pointer.jpg"); 75 | DirectX::SaveWICTextureToFile(context.get(), texture.get(), GUID_ContainerFormatJpeg, L"test.jpg"); 76 | */ 77 | } 78 | 79 | 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /VideoLibraryTests/VideoLibraryTests.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Debug 7 | Win32 8 | 9 | 10 | Release 11 | Win32 12 | 13 | 14 | Debug 15 | x64 16 | 17 | 18 | Release 19 | x64 20 | 21 | 22 | 23 | 15.0 24 | {348EF9C9-BBF4-4985-8784-4B47AC4E9F0A} 25 | Win32Proj 26 | VideoLibraryTests 27 | 10.0 28 | NativeUnitTestProject 29 | true 30 | 31 | 32 | 33 | DynamicLibrary 34 | true 35 | v141 36 | Unicode 37 | false 38 | false 39 | 40 | 41 | DynamicLibrary 42 | false 43 | v141 44 | true 45 | Unicode 46 | false 47 | false 48 | 49 | 50 | DynamicLibrary 51 | true 52 | v143 53 | Unicode 54 | false 55 | false 56 | 57 | 58 | DynamicLibrary 59 | false 60 | v141 61 | true 62 | Unicode 63 | false 64 | false 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | true 86 | 87 | 88 | true 89 | 90 | 91 | false 92 | 93 | 94 | false 95 | 96 | 97 | 98 | Use 99 | Level3 100 | Disabled 101 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) 102 | _DEBUG;%(PreprocessorDefinitions) 103 | true 104 | 105 | 106 | Windows 107 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 108 | Msdmo.lib;dmoguids.lib;uuid.lib;amstrmid.lib;wmcodecdspuuid.lib;mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;msdmo.lib;ole32.lib 109 | 110 | 111 | 112 | 113 | Use 114 | Level3 115 | Disabled 116 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) 117 | WIN32;_DEBUG;%(PreprocessorDefinitions) 118 | true 119 | 120 | 121 | Windows 122 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 123 | Msdmo.lib;dmoguids.lib;uuid.lib;amstrmid.lib;wmcodecdspuuid.lib;mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;msdmo.lib;ole32.lib 124 | 125 | 126 | 127 | 128 | Level3 129 | Use 130 | MaxSpeed 131 | true 132 | true 133 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) 134 | WIN32;NDEBUG;%(PreprocessorDefinitions) 135 | true 136 | 137 | 138 | Windows 139 | true 140 | true 141 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 142 | Msdmo.lib;dmoguids.lib;uuid.lib;amstrmid.lib;wmcodecdspuuid.lib;mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;msdmo.lib;ole32.lib 143 | 144 | 145 | 146 | 147 | Level3 148 | Use 149 | MaxSpeed 150 | true 151 | true 152 | $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories) 153 | NDEBUG;%(PreprocessorDefinitions) 154 | true 155 | 156 | 157 | Windows 158 | true 159 | true 160 | $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) 161 | Msdmo.lib;dmoguids.lib;uuid.lib;amstrmid.lib;wmcodecdspuuid.lib;mfuuid.lib;mfplat.lib;shlwapi.lib;mf.lib;mfreadwrite.lib;evr.lib;msdmo.lib;ole32.lib 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | Create 173 | Create 174 | Create 175 | Create 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | {3ce469f1-0be6-4aad-abd3-e49a9e270cd0} 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 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}. 195 | 196 | 197 | 198 | 199 | 200 | -------------------------------------------------------------------------------- /VideoLibraryTests/VideoLibraryTests.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;ipp;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 | 26 | 27 | Source Files 28 | 29 | 30 | Source Files 31 | 32 | 33 | Source Files 34 | 35 | 36 | Source Files 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /VideoLibraryTests/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /VideoLibraryTests/stdafx.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 | // stdafx.cpp : source file that includes just the standard includes 21 | // VideoLibraryTests.pch will be the pre-compiled header 22 | // stdafx.obj will contain the pre-compiled type information 23 | 24 | #include "stdafx.h" 25 | 26 | // TODO: reference any additional headers you need in STDAFX.H 27 | // and not in this file 28 | -------------------------------------------------------------------------------- /VideoLibraryTests/stdafx.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 | // stdafx.h : include file for standard system include files, 21 | // or project specific include files that are used frequently, but 22 | // are changed infrequently 23 | // 24 | 25 | #pragma once 26 | 27 | #include "targetver.h" 28 | 29 | // Headers for CppUnitTest 30 | #include "CppUnitTest.h" 31 | 32 | // TODO: reference additional headers your program requires here 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | #include 46 | #include 47 | 48 | #include 49 | #include 50 | #include 51 | -------------------------------------------------------------------------------- /VideoLibraryTests/targetver.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 | // Including SDKDDKVer.h defines the highest available Windows platform. 23 | 24 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and 25 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. 26 | 27 | #include 28 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Desktop Recorder Library 2 | 3 | This is the C++ library for a desktop recorder app I worked on for a couple of 4 | years as a hobby project. The app reached 10K monthly active users on the Windows Store. 5 | Here is a demo of what the app looked like, powered by this library: 6 | 7 | [![Watch the demo](https://img.youtube.com/vi/GGDT2mmUgYg/maxresdefault.jpg)](https://youtu.be/GGDT2mmUgYg) 8 | 9 | ## Features 10 | 11 | * GPU Accelerated video encoding to MP4 12 | * On-the-fly, GPU-based texture resizing 13 | * Multiple monitor support 14 | * Monitor rotation support 15 | * Microphone Audio Capture 16 | * Mouse capture and rendering 17 | 18 | ## Dependencies 19 | 20 | * Windows Desktop Duplication 21 | * Windows Media Foundation 22 | * DirectX 23 | * Win32 24 | 25 | ## Minimal example 26 | 27 | ```cpp 28 | winrt::check_hresult(MFStartup(MF_VERSION)); 29 | winrt::init_apartment(); 30 | 31 | // VirtualDesktop is an abstraction for multimonitor support 32 | auto virtualDesktop = std::make_shared(); 33 | 34 | // Pick a monitor to record 35 | std::vector desktopMonitors = virtualDesktop->GetAllDesktopMonitors(); 36 | DesktopMonitor monitor = desktopMonitors[0]; 37 | 38 | // Create a duplicator for the monitor 39 | std::unique_ptr duplicator = 40 | virtualDesktop->RecordMonitor(monitor); 41 | 42 | // The pipeline encapsulates texture rendering 43 | std::unique_ptr duplicationPipeline = std::make_unique(duplicator); 44 | duplicationPipeline->Perform(); 45 | 46 | // The IMFSample that can be used with the ScreenMediaSinkWriter class 47 | winrt::com_ptr = duplicationPipeline->Sample(); 48 | 49 | // Get the media type - redacted for brevity 50 | winrt::com_ptr videoMediaType = GetMediaTypeFromDuplicator(*duplicator); 51 | 52 | // Encoding options for the video 53 | EncodingContext encodingContext{}; 54 | encodingContext.fileName = L"test.mp4"; 55 | encodingContext.resolutionOption = ResolutionOption::Auto; 56 | encodingContext.audioQuality = AudioQuality::Auto; 57 | encodingContext.frameRate = 30; 58 | encodingContext.bitRate = 9000000; 59 | encodingContext.videoInputMediaType = videoMediaType; 60 | // See SampleApp for audio recording 61 | encodingContext.audioInputMediaType = nullptr; 62 | encodingContext.device = duplicator->Device(); 63 | 64 | // Encapsulates the Media Foundation sink writer 65 | auto writer = std::make_unique(encodingContext); 66 | writer->Begin(); 67 | 68 | // Write the sample 69 | sample->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); 70 | writer->WriteSample(sample); 71 | 72 | // End finishes recording 73 | writer->End(); 74 | writer.reset(nullptr); 75 | 76 | winrt::check_hresult(MFShutdown()); 77 | ``` 78 | 79 | --------------------------------------------------------------------------------