File: background_thumbnail_video_capturer.cc

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 (200 lines) | stat: -rw-r--r-- 7,489 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
// 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.

#include "chrome/browser/ui/thumbnails/background_thumbnail_video_capturer.h"

#include <stdint.h>

#include <utility>

#include "base/metrics/histogram_macros.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_id_helper.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "media/capture/mojom/video_capture_buffer.mojom.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "ui/gfx/geometry/skia_conversions.h"

BackgroundThumbnailVideoCapturer::BackgroundThumbnailVideoCapturer(
    content::WebContents* contents,
    GotFrameCallback got_frame_callback)
    : contents_(contents), got_frame_callback_(std::move(got_frame_callback)) {
  DCHECK(contents_);
  DCHECK(got_frame_callback_);
}

BackgroundThumbnailVideoCapturer::~BackgroundThumbnailVideoCapturer() {
  if (video_capturer_) {
    Stop();
  }
}

void BackgroundThumbnailVideoCapturer::Start(
    const ThumbnailCaptureInfo& capture_info) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (video_capturer_) {
    return;
  }

  content::RenderWidgetHostView* const source_view =
      contents_->GetPrimaryMainFrame()
          ->GetRenderViewHost()
          ->GetWidget()
          ->GetView();
  if (!source_view) {
    return;
  }

  capture_info_ = capture_info;

  {
    // Assign IDs to each capture session for tracing. IDs are unique
    // throughout the lifetime of this process. Using a static here is
    // safe since this is only invoked from the UI thread.
    static uint64_t capture_num GUARDED_BY_CONTEXT(sequence_checker_) = 0;
    cur_capture_num_ = ++capture_num;
    TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("ui", "Tab.Preview.VideoCapture",
                                      TRACE_ID_LOCAL(cur_capture_num_));
  }

  start_time_ = base::TimeTicks::Now();
  num_received_frames_ = 0;

  constexpr int kMaxFrameRate = 2;
  video_capturer_ = source_view->CreateVideoCapturer();
  video_capturer_->SetResolutionConstraints(capture_info_.target_size,
                                            capture_info_.target_size, false);
  video_capturer_->SetAutoThrottlingEnabled(false);
  video_capturer_->SetMinSizeChangePeriod(base::TimeDelta());
  video_capturer_->SetFormat(media::PIXEL_FORMAT_ARGB);
  video_capturer_->SetMinCapturePeriod(base::Seconds(1) / kMaxFrameRate);
  video_capturer_->Start(this, viz::mojom::BufferFormatPreference::kDefault);
}

void BackgroundThumbnailVideoCapturer::Stop() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!video_capturer_) {
    return;
  }

  video_capturer_->Stop();
  video_capturer_.reset();

  TRACE_EVENT_NESTABLE_ASYNC_END0("ui", "Tab.Preview.VideoCapture",
                                  TRACE_ID_LOCAL(cur_capture_num_));
  start_time_ = base::TimeTicks();
  cur_capture_num_ = 0;
}

void BackgroundThumbnailVideoCapturer::OnFrameCaptured(
    ::media::mojom::VideoBufferHandlePtr data,
    ::media::mojom::VideoFrameInfoPtr info,
    const gfx::Rect& content_rect,
    mojo::PendingRemote<::viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
        callbacks) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  CHECK(video_capturer_);

  mojo::Remote<::viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
      callbacks_remote(std::move(callbacks));

  CHECK(data->is_read_only_shmem_region());
  base::ReadOnlySharedMemoryRegion& shmem_region =
      data->get_read_only_shmem_region();

  // The |data| parameter is not nullable and mojo type mapping for
  // `base::ReadOnlySharedMemoryRegion` defines that nullable version of it is
  // the same type, with null check being equivalent to IsValid() check. Given
  // the above, we should never be able to receive a read only shmem region that
  // is not valid - mojo will enforce it for us.
  DCHECK(shmem_region.IsValid());

  // Process captured image.
  base::ReadOnlySharedMemoryMapping mapping = shmem_region.Map();
  if (!mapping.IsValid()) {
    DLOG(ERROR) << "Shared memory mapping failed.";
    return;
  }
  if (mapping.size() <
      media::VideoFrame::AllocationSize(info->pixel_format, info->coded_size)) {
    DLOG(ERROR) << "Shared memory size was less than expected.";
    return;
  }

  TRACE_EVENT_INSTANT1("ui", "Tab.Preview.VideoCaptureFrameReceived",
                       TRACE_EVENT_SCOPE_THREAD, "frame_number",
                       num_received_frames_);
  ++num_received_frames_;

  uint64_t frame_id = base::trace_event::GetNextGlobalTraceId();
  TRACE_EVENT_WITH_FLOW0("ui", "Tab.Preview.ProcessVideoCaptureFrame", frame_id,
                         TRACE_EVENT_FLAG_FLOW_OUT);

  // The SkBitmap's pixels will be marked as immutable, but the installPixels()
  // API requires a non-const pointer. So, cast away the const.
  void* const pixels = const_cast<void*>(mapping.memory());

  // Call installPixels() with a |releaseProc| that: 1) notifies the capturer
  // that this consumer has finished with the frame, and 2) releases the shared
  // memory mapping.
  struct FramePinner {
    // Keeps the shared memory that backs |frame_| mapped.
    base::ReadOnlySharedMemoryMapping mapping;
    // Prevents FrameSinkVideoCapturer from recycling the shared memory that
    // backs |frame_|.
    mojo::PendingRemote<viz::mojom::FrameSinkVideoConsumerFrameCallbacks>
        releaser;
  };

  // Subtract back out the scroll bars if we decided there was enough canvas to
  // account for them and still have a decent preview image.
  const float scale_ratio = static_cast<float>(content_rect.width()) /
                            capture_info_.copy_rect.width();

  const gfx::Insets original_scroll_insets = capture_info_.scrollbar_insets;
  const auto scroll_insets = gfx::Insets::TLBR(
      0, 0, std::round(original_scroll_insets.width() * scale_ratio),
      std::round(original_scroll_insets.height() * scale_ratio));
  gfx::Rect effective_content_rect = content_rect;
  effective_content_rect.Inset(scroll_insets);

  const gfx::Size bitmap_size(content_rect.right(), content_rect.bottom());
  SkBitmap frame;
  frame.installPixels(
      SkImageInfo::MakeN32(bitmap_size.width(), bitmap_size.height(),
                           kPremul_SkAlphaType,
                           info->color_space.ToSkColorSpace()),
      pixels,
      media::VideoFrame::RowBytes(media::VideoFrame::Plane::kARGB,
                                  info->pixel_format, info->coded_size.width()),
      [](void* addr, void* context) {
        delete static_cast<FramePinner*>(context);
      },
      new FramePinner{std::move(mapping), callbacks_remote.Unbind()});
  frame.setImmutable();

  SkBitmap cropped_frame;
  if (!frame.extractSubset(&cropped_frame,
                           gfx::RectToSkIRect(effective_content_rect))) {
    return;
  }

  got_frame_callback_.Run(cropped_frame, frame_id);
}

void BackgroundThumbnailVideoCapturer::OnNewSubCaptureTargetVersion(
    uint32_t sub_capture_target_version) {}

void BackgroundThumbnailVideoCapturer::OnFrameWithEmptyRegionCapture() {}

void BackgroundThumbnailVideoCapturer::OnStopped() {}

void BackgroundThumbnailVideoCapturer::OnLog(const std::string& /*message*/) {}