File: video_recording_watcher.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (318 lines) | stat: -rw-r--r-- 14,218 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_CAPTURE_MODE_VIDEO_RECORDING_WATCHER_H_
#define ASH_CAPTURE_MODE_VIDEO_RECORDING_WATCHER_H_

#include <optional>

#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_behavior.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/display/cursor_window_controller.h"
#include "ash/wm/window_dimmer.h"
#include "base/memory/raw_ptr.h"
#include "base/timer/timer.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
#include "ui/aura/scoped_window_capture_request.h"
#include "ui/aura/window_observer.h"
#include "ui/base/cursor/cursor.h"
#include "ui/color/color_provider_source_observer.h"
#include "ui/compositor/layer_delegate.h"
#include "ui/compositor/layer_owner.h"
#include "ui/display/display_observer.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/wm/public/activation_change_observer.h"

namespace display {
enum class TabletState;
}  // namespace display

namespace wm {
class CursorManager;
}  // namespace wm

namespace ash {

class CaptureModeBehavior;
class CaptureModeController;
class CaptureModeDemoToolsController;
class RecordedWindowRootObserver;

// An instance of this class is created while video recording is in progress to
// watch for events that end video recording, such as a window being recorded
// gets closed or moved between displays, or a display being fullscreen-recorded
// gets disconnected.
// This also paints a dimming shield to distinguish the area being recorded, but
// only when recording a window or a partial region.
// Note that this object doesn't create a new layer, rather the controller makes
// it acquire and reuse the layer of the `CaptureModeSession` prior to the
// session ending.
// It also controls the overlay created on the video capturer to efficiently
// record the mouse cursor on top of the video frames.
class ASH_EXPORT VideoRecordingWatcher
    : public aura::WindowObserver,
      public ui::LayerOwner,
      public ui::LayerDelegate,
      public wm::ActivationChangeObserver,
      public display::DisplayObserver,
      public WindowDimmer::Delegate,
      public ui::EventHandler,
      public CursorWindowController::Observer,
      public ui::ColorProviderSourceObserver {
 public:
  VideoRecordingWatcher(
      CaptureModeController* controller,
      CaptureModeBehavior* active_behavior,
      aura::Window* window_being_recorded,
      mojo::PendingRemote<viz::mojom::FrameSinkVideoCaptureOverlay>
          cursor_capture_overlay,
      bool is_recording_audio);
  ~VideoRecordingWatcher() override;

  const CaptureModeBehavior* active_behavior() const {
    return active_behavior_;
  }
  aura::Window* window_being_recorded() const { return window_being_recorded_; }
  bool is_recording_audio() const { return is_recording_audio_; }
  bool should_paint_layer() const { return should_paint_layer_; }
  bool is_shutting_down() const { return is_shutting_down_; }
  CaptureModeSource recording_source() const { return recording_source_; }

  // Clean up prior to deletion.
  void ShutDown();

  // Returns the current parent window for the on-capture-surface widgets such
  // as `CaptureModeCameraController::camera_preview_widget_` and
  // `CaptureModeDemoToolsController::key_combo_widget_` when recording is in
  // progress.
  aura::Window* GetOnCaptureSurfaceWidgetParentWindow() const;

  // Returns the bounds within which the on-capture-surface widgets (such as
  // capture mode camera preview widget and key combo widget) will be confined
  // when recording is in progress.
  gfx::Rect GetCaptureSurfaceConfineBounds() const;

  // Returns the `partial_region_bounds_` clamped to the bounds of the
  // `current_root_`. It should only be called if `recording_source_` is
  // `kRegion`.
  gfx::Rect GetEffectivePartialRegionBounds() const;

  // Returns the `key_combo_widget_` if it is visible.
  const views::Widget* GetKeyComboWidgetIfVisible() const;

  // aura::WindowObserver:
  void OnWindowParentChanged(aura::Window* window,
                             aura::Window* parent) override;
  void OnWindowVisibilityChanged(aura::Window* window, bool visible) override;
  void OnWindowBoundsChanged(aura::Window* window,
                             const gfx::Rect& old_bounds,
                             const gfx::Rect& new_bounds,
                             ui::PropertyChangeReason reason) override;
  void OnWindowOpacitySet(aura::Window* window,
                          ui::PropertyChangeReason reason) override;
  void OnWindowStackingChanged(aura::Window* window) override;
  void OnWindowDestroying(aura::Window* window) override;
  void OnWindowDestroyed(aura::Window* window) override;
  void OnWindowRemovingFromRootWindow(aura::Window* window,
                                      aura::Window* new_root) override;

  // ui::LayerDelegate:
  void OnPaintLayer(const ui::PaintContext& context) override;
  void OnDeviceScaleFactorChanged(float old_device_scale_factor,
                                  float new_device_scale_factor) override {}

  // wm::ActivationChangeObserver:
  void OnWindowActivated(ActivationReason reason,
                         aura::Window* gained_active,
                         aura::Window* lost_active) override;

  // display::DisplayObserver:
  void OnDisplayTabletStateChanged(display::TabletState state) override;
  void OnDisplayMetricsChanged(const display::Display& display,
                               uint32_t metrics) override;

  // WindowDimmer::Delegate:
  void OnDimmedWindowDestroying(aura::Window* window) override;
  void OnDimmedWindowParentChanged(aura::Window* dimmed_window) override;

  // ui::EventHandler:
  void OnKeyEvent(ui::KeyEvent* event) override;
  void OnMouseEvent(ui::MouseEvent* event) override;
  void OnTouchEvent(ui::TouchEvent* event) override;

  // CursorWindowController::Observer:
  void OnCursorCompositingStateChanged(bool enabled) override;

  // ui::ColorProviderSourceObserver:
  void OnColorProviderChanged() override;

  bool IsWindowDimmedForTesting(aura::Window* window) const;

  void BindCursorOverlayForTesting(
      mojo::PendingRemote<viz::mojom::FrameSinkVideoCaptureOverlay> overlay);

  void FlushCursorOverlayForTesting();

  void SendThrottledWindowSizeChangedNowForTesting();

  CaptureModeDemoToolsController* demo_tools_controller_for_testing() const {
    return demo_tools_controller_.get();
  }

 protected:
  // ui::LayerOwner:
  void SetLayer(std::unique_ptr<ui::Layer> layer) override;

 private:
  friend class CaptureModeTestApi;
  friend class RecordedWindowRootObserver;

  // Called by |RecordedWindowRootObserver| to notify us with a hierarchy change
  // event received by the |current_root_| window. The |target| window is the
  // window that was added to or removed from the hierarchy.
  void OnRootHierarchyChanged(aura::Window* target);

  bool CalculateShouldPaintLayer() const;

  // Uses CalculateShouldPaintLayer() to update whether we should paint the
  // recording shield, and stores the value in |should_paint_layer_|. If the
  // value of |should_paint_layer_| is changed, it schedules painting on our
  // layer.
  void UpdateShouldPaintLayer();

  // Updates our layer's parent and stacking order within its parent. It also
  // determines whether some windows need to be dimmed individually because they
  // are above the shield layer in z-order.
  void UpdateLayerStackingAndDimmers();

  // Returns the current native cursor from |cursor_manager_|.
  gfx::NativeCursor GetCurrentCursor() const;

  // Updates the cursor overlay using the given |location| in the coordinates of
  // the |window_being_recorded_|. This is used for non-pressed/-released mouse
  // events which can be too frequent. Recording is performed at a rate of 30
  // FPS, so we don't need to send every mouse event to the capturer overlay on
  // Viz via mojo. Such events can be throttled using the
  // |cursor_events_throttle_timer_|.
  void UpdateOrThrottleCursorOverlay(const gfx::PointF& location);

  // As opposed to the above UpdateOrThrottleCursorOverlay(), this updates the
  // cursor capturer overlay immediately without throttling, if such an update
  // is needed (e.g. the cursor bitmap changed, or the location changed, or
  // both). This also cancels any pending throttled update, since this immediate
  // one is more recent.
  void UpdateCursorOverlayNow(const gfx::PointF& location);

  // Hides the cursor overlay in the video capturer. Note that this doesn't
  // necessarily mean that the video won't contain a cursor, since the software-
  // composited cursor might be enabled. See |force_cursor_overlay_hidden_|.
  void HideCursorOverlay();

  // Invoked when the |cursor_events_throttle_timer_| fires, in order to update
  // the cursor overlay with the pending most recently throttled mouse event
  // location in |throttled_cursor_location_| if any.
  void OnCursorThrottleTimerFiring();

  // Invoked when the |window_size_change_throttle_timer_| fires, in order to
  // push the current size of the window being recorded to the service.
  void OnWindowSizeChangeThrottleTimerFiring();

  const raw_ptr<CaptureModeController> controller_;

  // The currently active behavior which is passed from capture mode session.
  const raw_ptr<CaptureModeBehavior> active_behavior_;
  const raw_ptr<wm::CursorManager> cursor_manager_;
  const raw_ptr<aura::Window, DanglingUntriaged> window_being_recorded_;
  raw_ptr<aura::Window, DanglingUntriaged> current_root_;
  const CaptureModeSource recording_source_;

  // The end point of the overlay owned by the video capturer on Viz, which is
  // used to blit the mouse cursor onto the recorded video frames.
  mojo::Remote<viz::mojom::FrameSinkVideoCaptureOverlay>
      cursor_capture_overlay_remote_;

  // Observes the hierarchy changes of the root window of the recorded window.
  // Only constructed when performing a window recording (i.e.
  // |recording_source_| is |kWindow|).
  std::unique_ptr<RecordedWindowRootObserver> root_observer_;

  // The last cursor we used to update the cursor overlay. This is used to
  // determine whether we need to update the cursor bitmap.
  gfx::NativeCursor last_cursor_;

  // The last bounds we used to update the cursor overlay. This is used to skip
  // the update if the bounds didn't change.
  // Note that these bounds are relative within the bounds of the recorded frame
  // sink, i.e. in the range [0.f, 1.f) for both origin() and size().
  // See documentation of FrameSinkVideoCaptureOverlay for more details.
  gfx::RectF last_cursor_overlay_bounds_;

  // Since recording happens at a rate of 30 FPS, there's no need to send every
  // mouse move event (or equivalent events such as enter, exit, dragged, ...
  // etc.) to the cursor overlay. This timer is used to throttle such events
  // received while this timer is running, and their location will overwrite the
  // value of |throttled_cursor_location_|. Once the timer fires,
  // OnCursorThrottleTimerFiring() will be called to update the overlay with the
  // most recent received throttled event.
  base::OneShotTimer cursor_events_throttle_timer_;

  // Stores the location of the most recent throttled mouse event (i.e. received
  // while the |cursor_events_throttle_timer_| was running). The location is in
  // the |window_being_recorded_| coordinates.
  std::optional<gfx::PointF> throttled_cursor_location_;

  // Resizing a window can generate many intermediate steps, and it would be
  // inefficient to push all of them to the recording service, causing a
  // repeated reconfiguration of the video encoder. This timer is used to
  // throttle such events.
  base::OneShotTimer window_size_change_throttle_timer_;

  // True if this active recording session started with audio recording turned
  // on, and audio recording is being done by the recording service.
  const bool is_recording_audio_;

  // True if we force hiding the cursor overlay. This happens when we record a
  // fullscreen, or a partial screen region, and the software-composited cursor
  // gets enabled. The software-composited cursor is already part of the root
  // window's frame sink which we record, so we don't need to show the cursor
  // overlay. Otherwise, the video will end up with two overlapping cursors.
  bool force_cursor_overlay_hidden_ = false;

  // Whether or not to paint the layer content in OnPaintLayer(). The value of
  // this field is calculated and updated in UpdateShouldPaintLayer().
  bool should_paint_layer_ = false;

  // The user-selected region for the current ongoing recording. This is only
  // valid and non empty when the |recording_source_| is |kRegion|. Note that
  // this differs from |CaptureModeController::user_capture_region_| in that the
  // latter may change during the recording if the user opens capture mode again
  // to take a partial screenshot.
  gfx::Rect partial_region_bounds_;

  // Maintains window dimmers where each is mapped by the window it dims. These
  // are created for the windows that are above the |window_being_recorded_| in
  // z-order on the same display so as to clearly show they're not being
  // recorded. The ones that are below |window_being_recorded_| in z-order are
  // dimmed by the shield layer owned by |this|.
  base::flat_map<aura::Window*, std::unique_ptr<WindowDimmer>> dimmers_;

  // If |window_being_recorded_| is not a root window, we must make a request to
  // make it capturable by the |FrameSinkVideoCapturer|.
  aura::ScopedWindowCaptureRequest non_root_window_capture_request_;

  std::unique_ptr<CaptureModeDemoToolsController> demo_tools_controller_;

  // True if the shutting down process has been triggered. We want to keep
  // `is_shutting_down_` as the last member variable in this class.
  bool is_shutting_down_ = false;
};

}  // namespace ash

#endif  // ASH_CAPTURE_MODE_VIDEO_RECORDING_WATCHER_H_