blob: 8e528472a729b3b972cd8888445993a1083d6815 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/services/recording/video_capture_params.h"
#include "ash/services/recording/recording_service_constants.h"
#include "base/check.h"
#include "base/time/time.h"
#include "components/viz/common/surfaces/subtree_capture_id.h"
#include "media/base/video_types.h"
#include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace recording {
namespace {
// -----------------------------------------------------------------------------
// FullscreenCaptureParams:
class FullscreenCaptureParams : public VideoCaptureParams {
public:
FullscreenCaptureParams(viz::FrameSinkId frame_sink_id,
const gfx::Size& video_size)
: VideoCaptureParams(frame_sink_id, viz::SubtreeCaptureId()),
video_size_(video_size) {}
FullscreenCaptureParams(const FullscreenCaptureParams&) = delete;
FullscreenCaptureParams& operator=(const FullscreenCaptureParams&) = delete;
~FullscreenCaptureParams() override = default;
// VideoCaptureParams:
void InitializeVideoCapturer(mojo::Remote<viz::mojom::FrameSinkVideoCapturer>&
capturer) const override {
VideoCaptureParams::InitializeVideoCapturer(capturer);
capturer->SetResolutionConstraints(video_size_, video_size_,
/*use_fixed_aspect_ratio=*/true);
capturer->SetAutoThrottlingEnabled(false);
}
gfx::Size GetCaptureSize() const override { return video_size_; }
private:
const gfx::Size video_size_;
};
// -----------------------------------------------------------------------------
// WindowCaptureParams:
class WindowCaptureParams : public VideoCaptureParams {
public:
WindowCaptureParams(viz::FrameSinkId frame_sink_id,
viz::SubtreeCaptureId subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size)
: VideoCaptureParams(frame_sink_id, subtree_capture_id),
initial_video_size_(initial_video_size),
max_video_size_(max_video_size) {}
WindowCaptureParams(const WindowCaptureParams&) = delete;
WindowCaptureParams& operator=(const WindowCaptureParams&) = delete;
~WindowCaptureParams() override = default;
// VideoCaptureParams:
void InitializeVideoCapturer(mojo::Remote<viz::mojom::FrameSinkVideoCapturer>&
capturer) const override {
VideoCaptureParams::InitializeVideoCapturer(capturer);
capturer->SetResolutionConstraints(initial_video_size_, max_video_size_,
/*use_fixed_aspect_ratio=*/false);
capturer->SetAutoThrottlingEnabled(true);
}
gfx::Size GetCaptureSize() const override {
// For now, the capturer sends us video frames whose sizes are equal to the
// size of the root on which the window resides. Therefore,
// |max_video_size_| should be used to initialize the video encoder.
// Otherwise, the pixels of the output video will be squished. With this
// approach, it's possible to resize the window within those bounds without
// having to change the size of the output video. However, this may not be
// a desired way.
// TODO(https://crbug.com/1165708): Investigate how to fix this in the
// capturer for M-89 or M-90.
return max_video_size_;
}
bool OnRecordedWindowChangingRoot(
mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
viz::FrameSinkId new_frame_sink_id,
const gfx::Size& new_max_video_size) override {
DCHECK(new_frame_sink_id.is_valid());
// The video encoder deals with video frames. Changing the frame sink ID
// doesn't affect the encoder. What affects it is a change in the video
// frames size.
const bool should_reconfigure_video_encoder =
max_video_size_ != new_max_video_size;
max_video_size_ = new_max_video_size;
frame_sink_id_ = new_frame_sink_id;
capturer->SetResolutionConstraints(initial_video_size_, max_video_size_,
/*use_fixed_aspect_ratio=*/false);
capturer->ChangeTarget(frame_sink_id_, subtree_capture_id_);
return should_reconfigure_video_encoder;
}
bool OnDisplaySizeChanged(
mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
const gfx::Size& new_display_size) override {
if (new_display_size == max_video_size_)
return false;
max_video_size_ = new_display_size;
capturer->SetResolutionConstraints(initial_video_size_, max_video_size_,
/*use_fixed_aspect_ratio=*/false);
return true;
}
private:
const gfx::Size initial_video_size_;
gfx::Size max_video_size_;
};
// -----------------------------------------------------------------------------
// RegionCaptureParams:
class RegionCaptureParams : public VideoCaptureParams {
public:
RegionCaptureParams(viz::FrameSinkId frame_sink_id,
const gfx::Size& full_capture_size,
const gfx::Rect& crop_region)
: VideoCaptureParams(frame_sink_id, viz::SubtreeCaptureId()),
full_capture_size_(full_capture_size),
crop_region_(crop_region) {}
RegionCaptureParams(const RegionCaptureParams&) = delete;
RegionCaptureParams& operator=(const RegionCaptureParams&) = delete;
~RegionCaptureParams() override = default;
// VideoCaptureParams:
void InitializeVideoCapturer(mojo::Remote<viz::mojom::FrameSinkVideoCapturer>&
capturer) const override {
VideoCaptureParams::InitializeVideoCapturer(capturer);
capturer->SetResolutionConstraints(full_capture_size_, full_capture_size_,
/*use_fixed_aspect_ratio=*/true);
capturer->SetAutoThrottlingEnabled(true);
}
gfx::Rect GetVideoFrameVisibleRect(
const gfx::Rect& original_frame_visible_rect) const override {
// We can't crop the video frame by an invalid bounds. The crop bounds must
// be contained within the original frame bounds.
gfx::Rect visible_rect = original_frame_visible_rect;
visible_rect.Intersect(crop_region_);
return visible_rect;
}
gfx::Size GetCaptureSize() const override {
return GetVideoFrameVisibleRect(gfx::Rect(full_capture_size_)).size();
}
bool OnDisplaySizeChanged(
mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
const gfx::Size& new_display_size) override {
if (new_display_size == full_capture_size_)
return false;
full_capture_size_ = new_display_size;
capturer->SetResolutionConstraints(full_capture_size_, full_capture_size_,
/*use_fixed_aspect_ratio=*/true);
return true;
}
private:
gfx::Size full_capture_size_;
const gfx::Rect crop_region_;
};
} // namespace
// -----------------------------------------------------------------------------
// VideoCaptureParams:
// static
std::unique_ptr<VideoCaptureParams>
VideoCaptureParams::CreateForFullscreenCapture(viz::FrameSinkId frame_sink_id,
const gfx::Size& video_size) {
return std::make_unique<FullscreenCaptureParams>(frame_sink_id, video_size);
}
// static
std::unique_ptr<VideoCaptureParams> VideoCaptureParams::CreateForWindowCapture(
viz::FrameSinkId frame_sink_id,
viz::SubtreeCaptureId subtree_capture_id,
const gfx::Size& initial_video_size,
const gfx::Size& max_video_size) {
return std::make_unique<WindowCaptureParams>(
frame_sink_id, subtree_capture_id, initial_video_size, max_video_size);
}
// static
std::unique_ptr<VideoCaptureParams> VideoCaptureParams::CreateForRegionCapture(
viz::FrameSinkId frame_sink_id,
const gfx::Size& full_capture_size,
const gfx::Rect& crop_region) {
return std::make_unique<RegionCaptureParams>(frame_sink_id, full_capture_size,
crop_region);
}
void VideoCaptureParams::InitializeVideoCapturer(
mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const {
DCHECK(capturer);
capturer->SetMinCapturePeriod(kMinCapturePeriod);
capturer->SetMinSizeChangePeriod(kMinPeriodForResizeThrottling);
// TODO(afakhry): Discuss with //media/ team the implications of color space
// conversions.
capturer->SetFormat(media::PIXEL_FORMAT_I420, kColorSpace);
capturer->ChangeTarget(frame_sink_id_, subtree_capture_id_);
}
gfx::Rect VideoCaptureParams::GetVideoFrameVisibleRect(
const gfx::Rect& original_frame_visible_rect) const {
return original_frame_visible_rect;
}
bool VideoCaptureParams::OnRecordedWindowChangingRoot(
mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
viz::FrameSinkId new_frame_sink_id,
const gfx::Size& new_max_video_size) {
CHECK(false) << "This can only be called when recording a window";
return false;
}
bool VideoCaptureParams::OnDisplaySizeChanged(
mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
const gfx::Size& new_display_size) {
CHECK(false)
<< "This can only be called when recording a window or a partial region";
return false;
}
VideoCaptureParams::VideoCaptureParams(viz::FrameSinkId frame_sink_id,
viz::SubtreeCaptureId subtree_capture_id)
: frame_sink_id_(frame_sink_id), subtree_capture_id_(subtree_capture_id) {
DCHECK(frame_sink_id_.is_valid());
}
} // namespace recording