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 435 436 437 438 439 440 441 442 443
|
// 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_;
}
bool did_user_ever_change_camera() const {
return did_user_ever_change_camera_;
}
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. `by_user` is true if the selection was made
// explicitly by the user, false otherwise.
void SetSelectedCamera(CameraId camera_id, bool by_user = false);
// 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,
video_capture::mojom::VideoSourceProvider::GetSourceInfosResult,
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> 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> 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.
std::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;
// True if the user ever made an explicit camera selection (i.e. from the
// capture mode settings menu).
bool did_user_ever_change_camera_ = false;
base::WeakPtrFactory<CaptureModeCameraController> weak_ptr_factory_{this};
};
} // namespace ash
#endif // ASH_CAPTURE_MODE_CAPTURE_MODE_CAMERA_CONTROLLER_H_
|