tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_