forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
capture_mode_camera_controller.h
434 lines (352 loc) · 18.1 KB
/
capture_mode_camera_controller.h
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
// Copyright 2022 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_CAPTURE_MODE_CAMERA_CONTROLLER_H_
#define ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_CONTROLLER_H_
#include <string>
#include <vector>
#include "ash/ash_export.h"
#include "ash/capture_mode/capture_mode_behavior.h"
#include "ash/capture_mode/capture_mode_types.h"
#include "ash/system/tray/system_tray_observer.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/system/system_monitor.h"
#include "base/timer/timer.h"
#include "media/base/video_facing.h"
#include "media/capture/video/video_capture_device_info.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/video_capture/public/mojom/video_source_provider.mojom.h"
#include "ui/views/widget/unique_widget_ptr.h"
namespace gfx {
class Rect;
} // namespace gfx
namespace ash {
class CameraPreviewView;
class CaptureModeBehavior;
class CaptureModeDelegate;
// The ID used internally in capture mode to identify the camera.
class ASH_EXPORT CameraId {
public:
CameraId() = default;
CameraId(std::string model_id, int number);
CameraId(const CameraId&) = default;
CameraId(CameraId&&) = default;
CameraId& operator=(const CameraId&) = default;
CameraId& operator=(CameraId&&) = default;
~CameraId() = default;
bool is_valid() const { return !model_id_or_display_name_.empty(); }
const std::string& model_id_or_display_name() const {
return model_id_or_display_name_;
}
int number() const { return number_; }
bool operator==(const CameraId& rhs) const {
return model_id_or_display_name_ == rhs.model_id_or_display_name_ &&
number_ == rhs.number_;
}
bool operator!=(const CameraId& rhs) const { return !(*this == rhs); }
bool operator<(const CameraId& rhs) const;
std::string ToString() const;
private:
// A unique hardware ID of the camera device in the form of
// "[Vendor ID]:[Product ID]" (e.g. "0c45:6713"). Note that if multiple
// cameras from the same vendor and of the same model are connected to the
// device, they will all have the same `model_id`.
// Note that in some cases, `media::VideoCaptureDeviceDescriptor::model_id`
// may not be present. In this case, this will be filled by the camera's
// display name.
std::string model_id_or_display_name_;
// A number that disambiguates cameras of the same type. For example if we
// have two connected cameras of the same type, the first one will have
// `number` set to 1, and the second's will be 2.
int number_ = 0;
};
struct CameraInfo {
CameraInfo(CameraId camera_id,
std::string device_id,
std::string display_name,
const media::VideoCaptureFormats& supported_formats,
media::VideoFacingMode camera_facing_mode);
CameraInfo(CameraInfo&&);
CameraInfo& operator=(CameraInfo&&);
~CameraInfo();
// The ID used to identify the camera device internally to the capture mode
// code, which should be more stable than the below `device_id` which may
// change multiple times for the same camera.
CameraId camera_id;
// The ID of the camera device given to it by the system in its current
// connection instance (e.g. "/dev/video2"). Note that the same camera device
// can disconnect and reconnect with a different `device_id` (e.g. when the
// cable is flaky). This ID is used to identify the camera to the video source
// provider in the video capture service.
std::string device_id;
// The name of the camera device as shown to the end user (e.g. "Integrated
// Webcam").
std::string display_name;
// A list of supported capture formats by this camera. This list is sorted
// (See `media::VideoCaptureSystemImpl::DevicesInfoReady()`) by the frame size
// area, then by frame width, then by the *largest* frame rate.
media::VideoCaptureFormats supported_formats;
// Whether the camera is facing the user (e.g. for internal front cameras), or
// the environment (e.g. internal rear cameras), or unknown (e.g. usually for
// external USB cameras).
media::VideoFacingMode camera_facing_mode;
};
using CameraInfoList = std::vector<CameraInfo>;
// Controls detecting camera devices additions and removals and keeping a list
// of all currently connected cameras to the device. It also tracks all the
// capture mode selfie camera settings.
class ASH_EXPORT CaptureModeCameraController
: public base::SystemMonitor::DevicesChangedObserver,
public SystemTrayObserver {
public:
class Observer : public base::CheckedObserver {
public:
// Called to notify the observer that the list of `available_cameras_` has
// changed, and provides that list as `cameras`.
virtual void OnAvailableCamerasChanged(const CameraInfoList& cameras) = 0;
// Called to notify the observer that a camera with `camera_id` was selected
// and will be used to show a camera preview when possible.
// Note that when `camera_id.is_valid()` is false, it means no camera is
// currently selected.
virtual void OnSelectedCameraChanged(const CameraId& camera_id) = 0;
protected:
~Observer() override = default;
};
explicit CaptureModeCameraController(CaptureModeDelegate* delegate);
CaptureModeCameraController(const CaptureModeCameraController&) = delete;
CaptureModeCameraController& operator=(const CaptureModeCameraController&) =
delete;
~CaptureModeCameraController() override;
const CameraInfoList& available_cameras() const { return available_cameras_; }
const CameraId& selected_camera() const { return selected_camera_; }
views::Widget* camera_preview_widget() const {
return camera_preview_widget_.get();
}
CameraPreviewView* camera_preview_view() const {
return camera_preview_view_;
}
bool should_show_preview() const { return should_show_preview_; }
CameraPreviewSnapPosition camera_preview_snap_position() const {
return camera_preview_snap_position_;
}
bool is_drag_in_progress() const { return is_drag_in_progress_; }
bool is_camera_preview_collapsed() const {
return is_camera_preview_collapsed_;
}
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
// Selects the first camera in the `available_cameras_` list (if any), and
// only if no other camera is already selected.
void MaybeSelectFirstCamera();
// Reverts the automatic selection of the first available camera if one was
// made by calling the `MaybeSelectFirstCamera()`.
void MaybeRevertAutoCameraSelection();
// Returns true if camera support is disabled by admins via
// the `SystemFeaturesDisableList` policy, false otherwise.
bool IsCameraDisabledByPolicy() const;
// Returns the display name of `selected_camera_`. Returns an empty string if
// the selected camera is not set.
std::string GetDisplayNameOfSelectedCamera() const;
// Sets the currently selected camera to the whose ID is the given
// `camera_id`. If `camera_id` is invalid (see CameraId::is_valid()), this
// clears the selected camera.
void SetSelectedCamera(CameraId camera_id);
// Sets `should_show_preview_` to the given `value`, and refreshes the state
// of the camera preview.
void SetShouldShowPreview(bool value);
// Updates the parent of the `camera_preview_widget_` when necessary. E.g,
// capture source type changes, selected recording window changes etc.
void MaybeReparentPreviewWidget();
// Sets `camera_preview_snap_position_` and updates the preview widget's
// bounds accordingly. If `animate` is set to true, the camera preview will
// animate to its new snap position.
void SetCameraPreviewSnapPosition(CameraPreviewSnapPosition value,
bool animate = false);
// Updates the bounds and visibility of `camera_preview_widget_` according to
// the current state of the capture surface within which the camera preview
// is confined and snapped to one of its corners. If `animate` is set to true,
// the widget will animate to the new target bounds.
void MaybeUpdatePreviewWidget(bool animate = false);
// Handles drag events forwarded from `camera_preview_view_`.
void StartDraggingPreview(const gfx::PointF& screen_location);
void ContinueDraggingPreview(const gfx::PointF& screen_location);
void EndDraggingPreview(const gfx::PointF& screen_location, bool is_touch);
// Updates the bounds of the preview widget and the value of
// `is_camera_preview_collapsed_` when the resize button is pressed.
void ToggleCameraPreviewSize();
// Called when a capture session gets started so we can refresh the cameras
// list, since the cros-camera service might have not been running when we
// tried to refresh the cameras at the beginning. (See
// http://b/230917107#comment12 for more details).
void OnCaptureSessionStarted();
void OnRecordingStarted(const CaptureModeBehavior* active_behavior);
void OnRecordingEnded();
// Called when the `CameraVideoFrameHandler` of the current
// `camera_preview_widget_` encounters a fatal error. This is considered a
// camera disconnection, and sometimes doesn't get reported via
// `OnDevicesChanged()` below, or may get delayed a lot. We manually remove
// the current camera from `available_cameras_`, delete its preview, and
// request a new list of cameras from the video capture service.
// https://crbug/1316230.
void OnFrameHandlerFatalError();
// Called when the device is shutting down. After this call, we don't do any
// operations that interacts with the video capture service.
void OnShuttingDown();
// As `camera_preview_view_` is a
// CaptureModeSessionFocusCycler::HighlightableView. This will show the focus
// ring and trigger setting a11y focus on the camera preview. Note, this is
// only for focusing the preview while recording is in progress.
void PseudoFocusCameraPreview();
void OnActiveUserSessionChanged();
// base::SystemMonitor::DevicesChangedObserver:
void OnDevicesChanged(base::SystemMonitor::DeviceType device_type) override;
// SystemTrayObserver:
void OnSystemTrayBubbleShown() override;
void OnFocusLeavingSystemTray(bool reverse) override {}
void OnStatusAreaAnchoredBubbleVisibilityChanged(TrayBubbleView* tray_bubble,
bool visible) override;
void SetOnCameraListReceivedForTesting(base::OnceClosure callback) {
on_camera_list_received_for_test_ = std::move(callback);
}
base::OneShotTimer* camera_reconnect_timer_for_test() {
return &camera_reconnect_timer_;
}
private:
friend class CaptureModeTestApi;
// Called to connect to the video capture services's video source provider for
// the first time, or when the connection to it is lost. It also queries the
// list of currently available cameras by calling the below
// GetCameraDevices().
void ReconnectToVideoSourceProvider();
// Retrieves the list of currently available cameras from the video source
// provider.
void GetCameraDevices();
// Called back asynchronously by the video source provider to give us the list
// of currently available camera `devices`. The ID used to make the request to
// which this reply belongs is `request_id`. We will ignore any replies for
// any older requests than the `most_recent_request_id_`.
using RequestId = size_t;
void OnCameraDevicesReceived(
RequestId request_id,
const std::vector<media::VideoCaptureDeviceInfo>& devices);
// Shows or hides a preview of the currently selected camera depending on
// whether it's currently allowed and whether one is currently selected.
void RefreshCameraPreview();
// Triggered when the `camera_reconnect_timer_` fires, indicating that a
// previously `selected_camera_` remained disconnected for longer than the
// allowed grace period, and therefore it will be cleared.
void OnSelectedCameraDisconnected();
// Returns the bounds of the preview widget which doesn't intersect with
// system tray, which should be confined within the given `confine_bounds`,
// and have the given `preview_size`. Always tries the current
// `camera_preview_snap_position_` first. Once a snap position with which the
// preview has no collisions is found, it will be set in
// `camera_preview_snap_position_`. If the camera preview at all possible snap
// positions intersects with system tray, returns the bounds for the current
// `camera_preview_snap_position_`.
gfx::Rect CalculatePreviewWidgetTargetBounds(const gfx::Rect& confine_bounds,
const gfx::Size& preview_size);
// Called by `CalculatePreviewWidgetTargetBounds` above. Returns the bounds of
// the preview widget that matches the coordinate system of the given
// `confine_bounds` with the given `preview_size` at the given
// `snap_position`.
gfx::Rect GetPreviewWidgetBoundsForSnapPosition(
const gfx::Rect& confine_bounds,
const gfx::Size& preview_size,
CameraPreviewSnapPosition snap_position) const;
// Returns the new snap position of the camera preview on drag ended.
CameraPreviewSnapPosition CalculateSnapPositionOnDragEnded() const;
// Returns the current bounds of camemra preview widget that match the
// coordinate system of the confine bounds.
gfx::Rect GetCurrentBoundsMatchingConfineBoundsCoordinates() const;
// Does post works for camera preview after RefreshCameraPreview(). It
// triggers a11y alert based on `was_preview_visible_before` and the current
// visibility of `camera_preview_widget_`. `was_preview_visible_before` is the
// visibility of the camera preview when RefreshCameraPreview() was called.
// It also triggers floating windows bounds update to avoid overlap between
// camera preview and floating windows, such as PIP windows and some a11y
// panels.
void RunPostRefreshCameraPreview(bool was_preview_visible_before);
// Sets the given `target_bounds` on the camera preview widget, potentially
// animating to it if `animate` is true. Returns true if the bounds actually
// changed from the current.
bool SetCameraPreviewBounds(const gfx::Rect& target_bounds, bool animate);
// Owned by CaptureModeController and guaranteed to be not null and to outlive
// `this`.
const raw_ptr<CaptureModeDelegate, ExperimentalAsh> delegate_;
// The remote end to the video source provider that exists in the video
// capture service.
mojo::Remote<video_capture::mojom::VideoSourceProvider>
video_source_provider_remote_;
CameraInfoList available_cameras_;
// The currently selected camera. If its `is_valid()` is false, then no camera
// is currently selected.
CameraId selected_camera_;
base::ObserverList<Observer> observers_;
// If bound, will be invoked at the end of the scope of
// `OnCameraDevicesReceived()` regardless of whether there was a change in the
// available cameras or not, which is different from the behavior of
// `Observer::OnAvailableCamerasChanged()` which is called only when there is
// a change.
base::OnceClosure on_camera_list_received_for_test_;
// The camera preview widget and its contents view.
views::UniqueWidgetPtr camera_preview_widget_;
raw_ptr<CameraPreviewView, ExperimentalAsh> camera_preview_view_ = nullptr;
// A timer used to give a `selected_camera_` that got disconnected a grace
// period, so if it reconnects again within this period, its ID is kept around
// in `selected_camera_`, otherwise the ID is cleared, effectively resetting
// back the camera setting to "Off".
base::OneShotTimer camera_reconnect_timer_;
// Set to true when a preview of the currently selected camera (if any) should
// be shown. This happens when CaptureModeSession is started and switched to
// a video recording mode before recording starts. It is reset back to false
// when:
// - Video recording ends.
// - The selected camera is disconnected for longer than a grace period during
// recording.
// - The capture mode session ends without starting any recording.
// - The capture mode session is switched to an image capture mode.
bool should_show_preview_ = false;
// The ID used for the most recent request made to the video source provider
// to get the list of cameras in GetCameraDevices(). More recent requests will
// have a larger value IDs than older requests.
RequestId most_recent_request_id_ = 0;
CameraPreviewSnapPosition camera_preview_snap_position_ =
CameraPreviewSnapPosition::kBottomRight;
// The location of the previous drag event in screen coordinate.
gfx::PointF previous_location_in_screen_;
// True when the dragging for `camera_preview_view_` is in progress.
bool is_drag_in_progress_ = false;
// True if the camera preview is collapsed. Its value will be updated when
// the resize button is clicked. The size of the preview widget and the icon
// of the resize button will be updated based on it.
bool is_camera_preview_collapsed_ = false;
// True if it's the first time to update the camera preview's bounds after
// it's created.
bool is_first_bounds_update_ = false;
// True when the device is shutting down, and we should no longer make any
// requests to the video capture service.
bool is_shutting_down_ = false;
// Valid only during recording to track the number of camera disconnections
// while recording is in progress.
absl::optional<int> in_recording_camera_disconnections_;
// Will be set to true the first time the number of connected cameras is
// reported.
bool did_report_number_of_cameras_before_ = false;
// Will be set to true the first user logs in. And we should only request the
// camera devices after the first user logs in.
bool did_first_user_login_ = false;
// True if the first available camera was auto-selected by calling
// `MaybeSelectFirstCamera()`, false otherwise or if
// `MaybeRevertAutoCameraSelection()` was called to revert back this automatic
// selection.
bool did_make_camera_auto_selection_ = false;
base::WeakPtrFactory<CaptureModeCameraController> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_CONTROLLER_H_