video_capture_params.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/services/recording/video_capture_params.h"
  5. #include "ash/services/recording/recording_service_constants.h"
  6. #include "base/check.h"
  7. #include "components/viz/common/surfaces/subtree_capture_id.h"
  8. #include "media/base/video_types.h"
  9. #include "services/viz/privileged/mojom/compositing/frame_sink_video_capture.mojom.h"
  10. #include "ui/gfx/geometry/dip_util.h"
  11. #include "ui/gfx/geometry/rect.h"
  12. #include "ui/gfx/geometry/rect_conversions.h"
  13. #include "ui/gfx/geometry/size_conversions.h"
  14. namespace recording {
  15. namespace {
  16. // Returns a rect that is the result of intersecting the given two rects.
  17. gfx::Rect GetIntersectionRect(const gfx::Rect& rect_a,
  18. const gfx::Rect& rect_b) {
  19. auto result = rect_a;
  20. result.Intersect(rect_b);
  21. return result;
  22. }
  23. inline gfx::Size GetSizeInPixels(const gfx::Size& size_dips, float dsf) {
  24. return gfx::ToFlooredSize(gfx::ConvertSizeToPixels(size_dips, dsf));
  25. }
  26. // -----------------------------------------------------------------------------
  27. // FullscreenCaptureParams:
  28. class FullscreenCaptureParams : public VideoCaptureParams {
  29. public:
  30. FullscreenCaptureParams(viz::FrameSinkId frame_sink_id,
  31. const gfx::Size& frame_sink_size_dip,
  32. float device_scale_factor)
  33. : VideoCaptureParams(frame_sink_id,
  34. viz::SubtreeCaptureId(),
  35. frame_sink_size_dip,
  36. device_scale_factor),
  37. initial_frame_sink_size_pixels_(current_frame_sink_size_pixels_) {}
  38. FullscreenCaptureParams(const FullscreenCaptureParams&) = delete;
  39. FullscreenCaptureParams& operator=(const FullscreenCaptureParams&) = delete;
  40. ~FullscreenCaptureParams() override = default;
  41. // VideoCaptureParams:
  42. gfx::Size GetVideoSize() const override {
  43. return initial_frame_sink_size_pixels_;
  44. }
  45. protected:
  46. void SetCapturerResolutionConstraints(
  47. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer)
  48. const override {
  49. DCHECK(capturer);
  50. capturer->SetResolutionConstraints(
  51. /*min_size=*/initial_frame_sink_size_pixels_,
  52. /*max_size=*/initial_frame_sink_size_pixels_,
  53. /*use_fixed_aspect_ratio=*/true);
  54. }
  55. bool OnVideoSizeMayHaveChanged(
  56. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) override {
  57. // We override the default behavior, as we want the video size to remain at
  58. // the original requested size. This gives a nice indication of display
  59. // rotations. The new video frames will letterbox to adhere to
  60. // the original requested resolution constraints.
  61. return false;
  62. }
  63. private:
  64. // We chose not to change the video size for fullscreen recording and let the
  65. // capturer letterbox the content in the initial size we gave it. This gives
  66. // a nice effect especially when rotating the display. See
  67. // OnVideoSizeMayHaveChanged() above.
  68. const gfx::Size initial_frame_sink_size_pixels_;
  69. };
  70. // -----------------------------------------------------------------------------
  71. // WindowCaptureParams:
  72. class WindowCaptureParams : public VideoCaptureParams {
  73. public:
  74. WindowCaptureParams(viz::FrameSinkId frame_sink_id,
  75. viz::SubtreeCaptureId subtree_capture_id,
  76. const gfx::Size& frame_sink_size_dip,
  77. float device_scale_factor,
  78. const gfx::Size& window_size_dip)
  79. : VideoCaptureParams(frame_sink_id,
  80. subtree_capture_id,
  81. frame_sink_size_dip,
  82. device_scale_factor),
  83. current_window_size_dips_(window_size_dip),
  84. current_window_size_pixels_(CalculateWindowSizeInPixels()) {}
  85. WindowCaptureParams(const WindowCaptureParams&) = delete;
  86. WindowCaptureParams& operator=(const WindowCaptureParams&) = delete;
  87. ~WindowCaptureParams() override = default;
  88. // VideoCaptureParams:
  89. gfx::Rect GetVideoFrameVisibleRect(
  90. const gfx::Rect& original_frame_visible_rect_pixels) const override {
  91. return GetIntersectionRect(original_frame_visible_rect_pixels,
  92. gfx::Rect(current_window_size_pixels_));
  93. }
  94. gfx::Size GetVideoSize() const override {
  95. return current_window_size_pixels_;
  96. }
  97. bool OnRecordedWindowChangingRoot(
  98. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
  99. viz::FrameSinkId new_frame_sink_id,
  100. const gfx::Size& new_frame_sink_size_dip,
  101. float new_device_scale_factor) override {
  102. DCHECK(new_frame_sink_id.is_valid());
  103. DCHECK_NE(frame_sink_id_, new_frame_sink_id);
  104. frame_sink_id_ = new_frame_sink_id;
  105. capturer->ChangeTarget(
  106. viz::VideoCaptureTarget(frame_sink_id_, subtree_capture_id_),
  107. /*crop_version=*/0);
  108. // If the movement to another display results in changes in the frame sink
  109. // size or DSF, OnVideoSizeMayHaveChanged() will be called by the below
  110. // OnFrameSinkSizeChanged().
  111. return OnFrameSinkSizeChanged(capturer, new_frame_sink_size_dip,
  112. new_device_scale_factor);
  113. }
  114. bool OnRecordedWindowSizeChanged(
  115. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
  116. const gfx::Size& new_window_size_dip) override {
  117. current_window_size_dips_ = new_window_size_dip;
  118. return OnVideoSizeMayHaveChanged(capturer);
  119. }
  120. protected:
  121. void SetCapturerResolutionConstraints(
  122. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer)
  123. const override {
  124. DCHECK(capturer);
  125. // To avoid receiving letterboxed video frames from the capturer, we ask it
  126. // to give us an exact resolution matching the window's size in pixels.
  127. capturer->SetResolutionConstraints(/*min_size=*/current_window_size_pixels_,
  128. /*max_size=*/current_window_size_pixels_,
  129. /*use_fixed_aspect_ratio=*/true);
  130. }
  131. bool OnVideoSizeMayHaveChanged(
  132. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) override {
  133. const auto new_window_size_pixels = CalculateWindowSizeInPixels();
  134. if (new_window_size_pixels == current_window_size_pixels_)
  135. return false;
  136. current_window_size_pixels_ = new_window_size_pixels;
  137. SetCapturerResolutionConstraints(capturer);
  138. return true;
  139. }
  140. private:
  141. gfx::Size CalculateWindowSizeInPixels() const {
  142. return GetSizeInPixels(current_window_size_dips_,
  143. current_device_scale_factor_);
  144. }
  145. gfx::Size current_window_size_dips_;
  146. gfx::Size current_window_size_pixels_;
  147. };
  148. // -----------------------------------------------------------------------------
  149. // RegionCaptureParams:
  150. class RegionCaptureParams : public VideoCaptureParams {
  151. public:
  152. RegionCaptureParams(viz::FrameSinkId frame_sink_id,
  153. const gfx::Size& frame_sink_size_dip,
  154. float device_scale_factor,
  155. const gfx::Rect& crop_region_dip)
  156. : VideoCaptureParams(frame_sink_id,
  157. viz::SubtreeCaptureId(),
  158. frame_sink_size_dip,
  159. device_scale_factor),
  160. crop_region_dips_(crop_region_dip),
  161. crop_region_pixels_(CalculateCropRegionInPixels()) {
  162. DCHECK(!crop_region_dips_.IsEmpty());
  163. DCHECK(!crop_region_pixels_.IsEmpty());
  164. }
  165. RegionCaptureParams(const RegionCaptureParams&) = delete;
  166. RegionCaptureParams& operator=(const RegionCaptureParams&) = delete;
  167. ~RegionCaptureParams() override = default;
  168. // VideoCaptureParams:
  169. gfx::Rect GetVideoFrameVisibleRect(
  170. const gfx::Rect& original_frame_visible_rect_pixels) const override {
  171. // We can't crop the video frame by an invalid bounds. The crop bounds must
  172. // be contained within the original frame bounds.
  173. gfx::Rect visible_rect = crop_region_pixels_;
  174. visible_rect.AdjustToFit(original_frame_visible_rect_pixels);
  175. return visible_rect;
  176. }
  177. gfx::Size GetVideoSize() const override {
  178. return GetVideoFrameVisibleRect(gfx::Rect(current_frame_sink_size_pixels_))
  179. .size();
  180. }
  181. protected:
  182. void SetCapturerResolutionConstraints(
  183. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer)
  184. const override {
  185. DCHECK(capturer);
  186. capturer->SetResolutionConstraints(
  187. /*min_size=*/current_frame_sink_size_pixels_,
  188. /*max_size=*/current_frame_sink_size_pixels_,
  189. /*use_fixed_aspect_ratio=*/true);
  190. }
  191. bool OnVideoSizeMayHaveChanged(
  192. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) override {
  193. SetCapturerResolutionConstraints(capturer);
  194. const auto new_crop_region_pixels = CalculateCropRegionInPixels();
  195. if (new_crop_region_pixels == crop_region_pixels_)
  196. return false;
  197. crop_region_pixels_ = new_crop_region_pixels;
  198. return true;
  199. }
  200. private:
  201. // Computes and returns the crop region bounds in pixels, according to the
  202. // |crop_region_dips_| and the |current_device_scale_factor_|.
  203. gfx::Rect CalculateCropRegionInPixels() const {
  204. return gfx::ToRoundedRect(gfx::ConvertRectToPixels(
  205. crop_region_dips_, current_device_scale_factor_));
  206. }
  207. const gfx::Rect crop_region_dips_;
  208. gfx::Rect crop_region_pixels_;
  209. };
  210. } // namespace
  211. // -----------------------------------------------------------------------------
  212. // VideoCaptureParams:
  213. // static
  214. std::unique_ptr<VideoCaptureParams>
  215. VideoCaptureParams::CreateForFullscreenCapture(
  216. viz::FrameSinkId frame_sink_id,
  217. const gfx::Size& frame_sink_size_dip,
  218. float device_scale_factor) {
  219. return std::make_unique<FullscreenCaptureParams>(
  220. frame_sink_id, frame_sink_size_dip, device_scale_factor);
  221. }
  222. // static
  223. std::unique_ptr<VideoCaptureParams> VideoCaptureParams::CreateForWindowCapture(
  224. viz::FrameSinkId frame_sink_id,
  225. viz::SubtreeCaptureId subtree_capture_id,
  226. const gfx::Size& frame_sink_size_dip,
  227. float device_scale_factor,
  228. const gfx::Size& window_size_dip) {
  229. return std::make_unique<WindowCaptureParams>(
  230. frame_sink_id, subtree_capture_id, frame_sink_size_dip,
  231. device_scale_factor, window_size_dip);
  232. }
  233. // static
  234. std::unique_ptr<VideoCaptureParams> VideoCaptureParams::CreateForRegionCapture(
  235. viz::FrameSinkId frame_sink_id,
  236. const gfx::Size& full_capture_size,
  237. float device_scale_factor,
  238. const gfx::Rect& crop_region_dip) {
  239. return std::make_unique<RegionCaptureParams>(
  240. frame_sink_id, full_capture_size, device_scale_factor, crop_region_dip);
  241. }
  242. void VideoCaptureParams::InitializeVideoCapturer(
  243. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer) const {
  244. DCHECK(capturer);
  245. capturer->SetMinCapturePeriod(kMinCapturePeriod);
  246. capturer->SetMinSizeChangePeriod(kMinPeriodForResizeThrottling);
  247. SetCapturerResolutionConstraints(capturer);
  248. capturer->SetAutoThrottlingEnabled(false);
  249. // TODO(afakhry): Discuss with //media/ team the implications of color space
  250. // conversions.
  251. capturer->SetFormat(media::PIXEL_FORMAT_I420);
  252. capturer->ChangeTarget(
  253. viz::VideoCaptureTarget(frame_sink_id_, subtree_capture_id_),
  254. /*crop_version=*/0);
  255. }
  256. gfx::Rect VideoCaptureParams::GetVideoFrameVisibleRect(
  257. const gfx::Rect& original_frame_visible_rect_pixels) const {
  258. return original_frame_visible_rect_pixels;
  259. }
  260. bool VideoCaptureParams::OnRecordedWindowChangingRoot(
  261. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
  262. viz::FrameSinkId new_frame_sink_id,
  263. const gfx::Size& new_frame_sink_size_dip,
  264. float new_device_scale_factor) {
  265. CHECK(false) << "This can only be called when recording a window";
  266. return false;
  267. }
  268. bool VideoCaptureParams::OnRecordedWindowSizeChanged(
  269. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
  270. const gfx::Size& new_window_size_dip) {
  271. CHECK(false) << "This can only be called when recording a window";
  272. return false;
  273. }
  274. bool VideoCaptureParams::OnFrameSinkSizeChanged(
  275. mojo::Remote<viz::mojom::FrameSinkVideoCapturer>& capturer,
  276. const gfx::Size& new_frame_sink_size_dip,
  277. float new_device_scale_factor) {
  278. if (current_frame_sink_size_dips_ == new_frame_sink_size_dip &&
  279. current_device_scale_factor_ == new_device_scale_factor) {
  280. return false;
  281. }
  282. current_frame_sink_size_dips_ = new_frame_sink_size_dip;
  283. current_device_scale_factor_ = new_device_scale_factor;
  284. current_frame_sink_size_pixels_ = CalculateFrameSinkSizeInPixels();
  285. return OnVideoSizeMayHaveChanged(capturer);
  286. }
  287. VideoCaptureParams::VideoCaptureParams(viz::FrameSinkId frame_sink_id,
  288. viz::SubtreeCaptureId subtree_capture_id,
  289. const gfx::Size& current_frame_sink_size,
  290. float device_scale_factor)
  291. : frame_sink_id_(frame_sink_id),
  292. subtree_capture_id_(subtree_capture_id),
  293. current_frame_sink_size_dips_(current_frame_sink_size),
  294. current_device_scale_factor_(device_scale_factor),
  295. current_frame_sink_size_pixels_(CalculateFrameSinkSizeInPixels()) {
  296. DCHECK(frame_sink_id_.is_valid());
  297. }
  298. gfx::Size VideoCaptureParams::CalculateFrameSinkSizeInPixels() const {
  299. return GetSizeInPixels(current_frame_sink_size_dips_,
  300. current_device_scale_factor_);
  301. }
  302. } // namespace recording