1 // Copyright 2014 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 
5 #include "media/capture/content/android/thread_safe_capture_oracle.h"
6 
7 #include <stdint.h>
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "base/bind.h"
13 #include "base/bits.h"
14 #include "base/logging.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/time/time.h"
17 #include "base/trace_event/trace_event.h"
18 #include "media/base/video_frame.h"
19 #include "media/base/video_frame_metadata.h"
20 #include "media/base/video_util.h"
21 #include "media/capture/video_capture_types.h"
22 #include "ui/gfx/geometry/rect.h"
23 
24 namespace media {
25 
26 namespace {
27 
28 // The target maximum amount of the buffer pool to utilize.  Actual buffer pool
29 // utilization is attenuated by this amount before being reported to the
30 // VideoCaptureOracle.  This value takes into account the maximum number of
31 // buffer pool buffers and a desired safety margin.
32 const int kTargetMaxPoolUtilizationPercent = 60;
33 
34 }  // namespace
35 
36 struct ThreadSafeCaptureOracle::InFlightFrameCapture {
37   int frame_number;
38   VideoCaptureDevice::Client::Buffer buffer;
39   std::unique_ptr<VideoCaptureBufferHandle> buffer_access;
40   base::TimeTicks begin_time;
41   base::TimeDelta frame_duration;
42 };
43 
ThreadSafeCaptureOracle(std::unique_ptr<VideoCaptureDevice::Client> client,const VideoCaptureParams & params)44 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
45     std::unique_ptr<VideoCaptureDevice::Client> client,
46     const VideoCaptureParams& params)
47     : client_(std::move(client)), oracle_(false), params_(params) {
48   DCHECK_GE(params.requested_format.frame_rate, 1e-6f);
49   oracle_.SetMinCapturePeriod(base::TimeDelta::FromMicroseconds(
50       static_cast<int64_t>(1000000.0 / params.requested_format.frame_rate +
51                            0.5 /* to round to nearest int */)));
52   const auto constraints = params.SuggestConstraints();
53   oracle_.SetCaptureSizeConstraints(constraints.min_frame_size,
54                                     constraints.max_frame_size,
55                                     constraints.fixed_aspect_ratio);
56 }
57 
58 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() = default;
59 
ObserveEventAndDecideCapture(VideoCaptureOracle::Event event,const gfx::Rect & damage_rect,base::TimeTicks event_time,scoped_refptr<VideoFrame> * storage,CaptureFrameCallback * callback)60 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
61     VideoCaptureOracle::Event event,
62     const gfx::Rect& damage_rect,
63     base::TimeTicks event_time,
64     scoped_refptr<VideoFrame>* storage,
65     CaptureFrameCallback* callback) {
66   // Grab the current time before waiting to acquire the |lock_|.
67   const base::TimeTicks capture_begin_time = base::TimeTicks::Now();
68 
69   gfx::Size visible_size;
70   gfx::Size coded_size;
71   media::VideoCaptureDevice::Client::Buffer output_buffer;
72   double attenuated_utilization;
73   int frame_number;
74   base::TimeDelta estimated_frame_duration;
75   {
76     base::AutoLock guard(lock_);
77 
78     if (!client_)
79       return false;  // Capture is stopped.
80 
81     if (!oracle_.ObserveEventAndDecideCapture(event, damage_rect, event_time)) {
82       // This is a normal and acceptable way to drop a frame. We've hit our
83       // capture rate limit: for example, the content is animating at 60fps but
84       // we're capturing at 30fps.
85       TRACE_EVENT_INSTANT1("gpu.capture", "FpsRateLimited",
86                            TRACE_EVENT_SCOPE_THREAD, "trigger",
87                            VideoCaptureOracle::EventAsString(event));
88       return false;
89     }
90 
91     frame_number = oracle_.next_frame_number();
92     visible_size = oracle_.capture_size();
93     // TODO(miu): Clients should request exact padding, instead of this
94     // memory-wasting hack to make frames that are compatible with all HW
95     // encoders.  http://crbug.com/555911
96     coded_size.SetSize(base::bits::Align(visible_size.width(), 16),
97                        base::bits::Align(visible_size.height(), 16));
98 
99     const auto result_code = client_->ReserveOutputBuffer(
100         coded_size, params_.requested_format.pixel_format, frame_number,
101         &output_buffer);
102 
103     // Get the current buffer pool utilization and attenuate it: The utilization
104     // reported to the oracle is in terms of a maximum sustainable amount (not
105     // the absolute maximum).
106     attenuated_utilization = client_->GetBufferPoolUtilization() *
107                              (100.0 / kTargetMaxPoolUtilizationPercent);
108 
109     if (result_code != VideoCaptureDevice::Client::ReserveResult::kSucceeded) {
110       TRACE_EVENT_INSTANT2(
111           "gpu.capture", "PipelineLimited", TRACE_EVENT_SCOPE_THREAD, "trigger",
112           VideoCaptureOracle::EventAsString(event), "atten_util_percent",
113           base::saturated_cast<int>(attenuated_utilization * 100.0 + 0.5));
114       oracle_.RecordWillNotCapture(attenuated_utilization);
115       return false;
116     }
117 
118     oracle_.RecordCapture(attenuated_utilization);
119     estimated_frame_duration = oracle_.estimated_frame_duration();
120   }  // End of critical section.
121 
122   if (attenuated_utilization >= 1.0) {
123     TRACE_EVENT_INSTANT2(
124         "gpu.capture", "NearlyPipelineLimited", TRACE_EVENT_SCOPE_THREAD,
125         "trigger", VideoCaptureOracle::EventAsString(event),
126         "atten_util_percent",
127         base::saturated_cast<int>(attenuated_utilization * 100.0 + 0.5));
128   }
129 
130   TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", output_buffer.id,
131                            "frame_number", frame_number, "trigger",
132                            VideoCaptureOracle::EventAsString(event));
133 
134   std::unique_ptr<VideoCaptureBufferHandle> output_buffer_access =
135       output_buffer.handle_provider->GetHandleForInProcessAccess();
136   *storage = VideoFrame::WrapExternalData(
137       params_.requested_format.pixel_format, coded_size,
138       gfx::Rect(visible_size), visible_size, output_buffer_access->data(),
139       output_buffer_access->mapped_size(), base::TimeDelta());
140 
141   // Note: Passing the |output_buffer_access| in the callback is a bit of a
142   // hack. Really, the access should be owned by the VideoFrame so that access
143   // is released (unpinning the shared memory) when the VideoFrame goes
144   // out-of-scope. However, there is an an issue where, at browser shutdown, the
145   // callback below may never be run, and instead it self-deletes: If this
146   // happens, the VideoFrame will release access *after* the Buffer goes
147   // out-of-scope, which is an invalid sequence of steps. This could be fixed in
148   // upstream implementation, but it's not worth spending time tracking it down
149   // because all of this code (and upstream code) is about to be replaced.
150   // http://crbug.com/754872
151   //
152   // To be clear, this solution allows |output_buffer_access| to be deleted
153   // before |output_buffer| if the callback self-deletes rather than ever being
154   // run. The InFlightFrameCapture destructor ensures this.
155   std::unique_ptr<InFlightFrameCapture> capture(new InFlightFrameCapture{
156       frame_number, std::move(output_buffer), std::move(output_buffer_access),
157       capture_begin_time, estimated_frame_duration});
158 
159   // If creating the VideoFrame wrapper failed, call DidCaptureFrame() with
160   // !success to execute the required post-capture steps (tracing, notification
161   // of failure to VideoCaptureOracle, etc.).
162   if (!(*storage)) {
163     DidCaptureFrame(std::move(capture), *storage, event_time, false);
164     return false;
165   }
166 
167   *callback = base::BindOnce(&ThreadSafeCaptureOracle::DidCaptureFrame, this,
168                              std::move(capture));
169 
170   return true;
171 }
172 
GetCaptureSize() const173 gfx::Size ThreadSafeCaptureOracle::GetCaptureSize() const {
174   base::AutoLock guard(lock_);
175   return oracle_.capture_size();
176 }
177 
UpdateCaptureSize(const gfx::Size & source_size)178 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
179   base::AutoLock guard(lock_);
180   VLOG(1) << "Source size changed to " << source_size.ToString();
181   oracle_.SetSourceSize(source_size);
182 }
183 
Stop()184 void ThreadSafeCaptureOracle::Stop() {
185   base::AutoLock guard(lock_);
186   client_.reset();
187 }
188 
ReportError(media::VideoCaptureError error,const base::Location & from_here,const std::string & reason)189 void ThreadSafeCaptureOracle::ReportError(media::VideoCaptureError error,
190                                           const base::Location& from_here,
191                                           const std::string& reason) {
192   base::AutoLock guard(lock_);
193   if (client_)
194     client_->OnError(error, from_here, reason);
195 }
196 
ReportStarted()197 void ThreadSafeCaptureOracle::ReportStarted() {
198   base::AutoLock guard(lock_);
199   if (client_)
200     client_->OnStarted();
201 }
202 
DidCaptureFrame(std::unique_ptr<InFlightFrameCapture> capture,scoped_refptr<VideoFrame> frame,base::TimeTicks reference_time,bool success)203 void ThreadSafeCaptureOracle::DidCaptureFrame(
204     std::unique_ptr<InFlightFrameCapture> capture,
205     scoped_refptr<VideoFrame> frame,
206     base::TimeTicks reference_time,
207     bool success) {
208   // Release |buffer_access| now that nothing is accessing the memory via the
209   // VideoFrame data pointers anymore.
210   capture->buffer_access.reset();
211 
212   base::AutoLock guard(lock_);
213 
214   const bool should_deliver_frame =
215       oracle_.CompleteCapture(capture->frame_number, success, &reference_time);
216 
217   TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", capture->buffer.id,
218                          "success", should_deliver_frame, "timestamp",
219                          (reference_time - base::TimeTicks()).InMicroseconds());
220 
221   if (!should_deliver_frame || !client_)
222     return;
223 
224   frame->metadata()->frame_rate = params_.requested_format.frame_rate;
225   frame->metadata()->capture_begin_time = capture->begin_time;
226   frame->metadata()->capture_end_time = base::TimeTicks::Now();
227   frame->metadata()->frame_duration = capture->frame_duration;
228   frame->metadata()->reference_time = reference_time;
229 
230   media::VideoCaptureFormat format(frame->coded_size(),
231                                    params_.requested_format.frame_rate,
232                                    frame->format());
233   client_->OnIncomingCapturedBufferExt(
234       std::move(capture->buffer), format, frame->ColorSpace(), reference_time,
235       frame->timestamp(), frame->visible_rect(), *frame->metadata());
236 }
237 
OnConsumerReportingUtilization(int frame_number,const media::VideoFrameFeedback & feedback)238 void ThreadSafeCaptureOracle::OnConsumerReportingUtilization(
239     int frame_number,
240     const media::VideoFrameFeedback& feedback) {
241   base::AutoLock guard(lock_);
242   oracle_.RecordConsumerFeedback(frame_number, feedback);
243 }
244 
245 }  // namespace media
246