wgc_capture_session.h (9688B)
1 /* 2 * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ 12 #define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_ 13 14 #include <d3d11.h> 15 #include <shellscalingapi.h> 16 #include <windows.graphics.capture.h> 17 #include <windows.graphics.h> 18 #include <wrl/client.h> 19 #include <wrl/implements.h> 20 21 #include <cstdint> 22 #include <memory> 23 #include <optional> 24 25 #include "api/scoped_refptr.h" 26 #include "api/sequence_checker.h" 27 #include "modules/desktop_capture/desktop_capture_options.h" 28 #include "modules/desktop_capture/desktop_frame.h" 29 #include "modules/desktop_capture/desktop_region.h" 30 #include "modules/desktop_capture/screen_capture_frame_queue.h" 31 #include "modules/desktop_capture/shared_desktop_frame.h" 32 #include "rtc_base/event.h" 33 34 namespace webrtc { 35 36 class WgcCaptureSession final { 37 public: 38 // WgcCaptureSession supports capturing a window as well as a screen. 39 // If it is a window, `source_id` is the HWND of the window to be 40 // captured, which is never `0`'. If it is a screen, `source_id` is a number 41 // in a 0-based monitor index. 42 WgcCaptureSession( 43 intptr_t source_id, 44 Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device, 45 Microsoft::WRL::ComPtr< 46 ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item, 47 ABI::Windows::Graphics::SizeInt32 size); 48 49 // Disallow copy and assign. 50 WgcCaptureSession(const WgcCaptureSession&) = delete; 51 WgcCaptureSession& operator=(const WgcCaptureSession&) = delete; 52 53 ~WgcCaptureSession(); 54 55 HRESULT StartCapture(const DesktopCaptureOptions& options); 56 57 // Returns a frame from the local frame queue, if any are present. 58 bool GetFrame(std::unique_ptr<DesktopFrame>* output_frame, 59 bool source_should_be_capturable); 60 61 bool IsCaptureStarted() const { 62 RTC_DCHECK_RUN_ON(&sequence_checker_); 63 return is_capture_started_; 64 } 65 66 // We keep 2 buffers in the frame pool since it results in a good compromise 67 // between latency/capture-rate and the rate at which 68 // Direct3D11CaptureFramePool.TryGetNextFrame returns NULL and we have to fall 69 // back to providing a copy from our external queue instead. 70 // We make this public for tests. 71 static constexpr int kNumBuffers = 2; 72 73 private: 74 class RefCountedEvent : public RefCountedNonVirtual<RefCountedEvent>, 75 public Event { 76 public: 77 RefCountedEvent(bool manual_reset, bool initially_signaled); 78 79 private: 80 friend class RefCountedNonVirtual<RefCountedEvent>; 81 ~RefCountedEvent(); 82 }; 83 84 // Handles the arrival of new frames in the Direct3D11CaptureFramePool. 85 // Whenever `Direct3D11CaptureFramePool.FrameArrived` is called, 86 // `AgileFrameArrivedHandler::Invoke` will also be called. This class needs to 87 // implement the IAgileObject interface so that we can create a WGC frame pool 88 // with `Direct3D11CaptureFramePool::CreateFreeThreaded` and be able to call 89 // `Invoke` on a thread different from the one that created this class' 90 // instance. See more: 91 class AgileFrameArrivedHandler 92 : public Microsoft::WRL::RuntimeClass< 93 Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, 94 ABI::Windows::Foundation::ITypedEventHandler< 95 ABI::Windows::Graphics::Capture::Direct3D11CaptureFramePool*, 96 IInspectable*>, 97 IAgileObject> { 98 public: 99 AgileFrameArrivedHandler(scoped_refptr<RefCountedEvent> event); 100 101 IFACEMETHODIMP Invoke( 102 ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool* sender, 103 IInspectable* args) override; 104 105 private: 106 scoped_refptr<RefCountedEvent> frame_arrived_event_; 107 }; 108 109 // Initializes `mapped_texture_` with the properties of the `src_texture`, 110 // overrides the values of some necessary properties like the 111 // D3D11_CPU_ACCESS_READ flag. Also has optional parameters for what size 112 // `mapped_texture_` should be, if they aren't provided we will use the size 113 // of `src_texture`. 114 HRESULT CreateMappedTexture( 115 Microsoft::WRL::ComPtr<ID3D11Texture2D> src_texture, 116 UINT width = 0, 117 UINT height = 0); 118 119 // Event handler for `item_`'s Closed event. 120 HRESULT OnItemClosed( 121 ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* sender, 122 IInspectable* event_args); 123 124 // Waits for the first frame to arrive in the `frame_pool_`. We should wait 125 // for a frame if either this is the first frame ever obtained from the 126 // `frame_pool_` or if this is the first frame obtained after a capture 127 // interruption - e.g. when a captured window is brought back after being 128 // minimized. 129 bool WaitForFirstFrame(); 130 131 // Wraps calls to ProcessFrame and deals with the uniqe start-up phase 132 // ensuring that we always have one captured frame available. 133 void EnsureFrame(); 134 135 // Process the captured frame and copy it to the `queue_`. 136 HRESULT ProcessFrame(); 137 138 void RemoveEventHandlers(); 139 void RemoveItemClosedEventHandler(); 140 void RemoveFrameArrivedEventHandler(); 141 HRESULT AddFrameArrivedEventHandler(); 142 143 bool FrameContentCanBeCompared(); 144 145 bool allow_zero_hertz() const { return allow_zero_hertz_; } 146 147 std::unique_ptr<EventRegistrationToken> item_closed_token_; 148 std::unique_ptr<EventRegistrationToken> frame_arrived_token_; 149 150 // A Direct3D11 Device provided by the caller. We use this to create an 151 // IDirect3DDevice, and also to create textures that will hold the image data. 152 Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; 153 154 // This item represents what we are capturing, we use it to create the 155 // capture session, and also to listen for the Closed event. 156 Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> 157 item_; 158 159 // The IDirect3DDevice is necessary to instantiate the frame pool. 160 Microsoft::WRL::ComPtr< 161 ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice> 162 direct3d_device_; 163 164 // The frame pool is where frames are deposited during capture, we retrieve 165 // them from here with TryGetNextFrame(). 166 Microsoft::WRL::ComPtr< 167 ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool> 168 frame_pool_; 169 170 // This texture holds the final image data. We made it a member so we can 171 // reuse it, instead of having to create a new texture every time we grab a 172 // frame. 173 Microsoft::WRL::ComPtr<ID3D11Texture2D> mapped_texture_; 174 175 // This is the size of `mapped_texture_` and the buffers in `frame_pool_`. We 176 // store this as a member so we can compare it to the size of incoming frames 177 // and resize if necessary. 178 ABI::Windows::Graphics::SizeInt32 size_; 179 180 // The capture session lets us set properties about the capture before it 181 // starts such as whether to capture the mouse cursor, and it lets us tell WGC 182 // to start capturing frames. 183 Microsoft::WRL::ComPtr< 184 ABI::Windows::Graphics::Capture::IGraphicsCaptureSession> 185 session_; 186 187 // Queue of captured video frames. The queue holds 2 frames and it avoids 188 // alloc/dealloc per captured frame. Incoming frames from the internal frame 189 // pool are copied to this queue after required processing in ProcessFrame(). 190 ScreenCaptureFrameQueue<SharedDesktopFrame> queue_; 191 192 bool item_closed_ = false; 193 bool is_capture_started_ = false; 194 195 // Caches the value of DesktopCaptureOptions.allow_wgc_zero_hertz() in 196 // StartCapture(). Adds 0Hz detection in ProcessFrame() when enabled which 197 // adds complexity since memcmp() is performed on two successive frames. 198 bool allow_zero_hertz_ = false; 199 200 // Tracks damage region updates that were reported since the last time a frame 201 // was captured. Currently only supports either the complete rect being 202 // captured or an empty region. Will always be empty if `allow_zero_hertz_` is 203 // false. 204 DesktopRegion damage_region_; 205 206 // The unique id to represent a Source of current DesktopCapturer. 207 intptr_t source_id_; 208 209 // The monitor that is being captured when the target source_id is a 210 // screen. For window sources, it can't be used because the window can move 211 // around around the different monitors. 212 std::optional<HMONITOR> monitor_; 213 214 // The source type of the capture session. It can be either a window or a 215 // screen. 216 bool is_window_source_; 217 218 // To be shared between `WgcCaptureSession` and `AgileFrameHandler`. 219 // AgileFrameHandler will set this event in a WGC working thread and 220 // `WgcCaptureSession` will check its state in desktopCaptureThread. This is 221 // necessary to avoid race conditions where the desktopCaptureThread preempts 222 // the WGC worker thread and destroys the `WgcCaptureSession` while a new 223 // frame is being processed In this situation, the `AgileFrameHandler` would 224 // end accessing invalid memory, which was previously owned by 225 // `WgcCaptureSession`. 226 // 227 // Will be signaled when the first frame is available in the `frame_pool_` and 228 // should not reset for the lifetime of `WgcCaptureSession`. 229 scoped_refptr<RefCountedEvent> has_first_frame_arrived_event_; 230 231 // Records if the first frame arrived in a stream arrived. Will be reset if a 232 // source becomes momentarilly non-capturable - e.g. a window that gets 233 // minimized. 234 bool has_first_frame_arrived_ = false; 235 236 SequenceChecker sequence_checker_; 237 }; 238 239 } // namespace webrtc 240 241 #endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_