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.
5 #include "media/capture/content/thread_safe_capture_oracle.h"
7 #include "base/basictypes.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/synchronization/lock.h"
12 #include "base/time/time.h"
13 #include "base/trace_event/trace_event.h"
14 #include "media/base/video_capture_types.h"
15 #include "media/base/video_frame.h"
16 #include "media/base/video_frame_metadata.h"
17 #include "media/base/video_util.h"
18 #include "ui/gfx/geometry/rect.h"
24 // The target maximum amount of the buffer pool to utilize. Actual buffer pool
25 // utilization is attenuated by this amount before being reported to the
26 // VideoCaptureOracle. This value takes into account the maximum number of
27 // buffer pool buffers and a desired safety margin.
28 const int kTargetMaxPoolUtilizationPercent
= 60;
32 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
33 scoped_ptr
<VideoCaptureDevice::Client
> client
,
34 const VideoCaptureParams
& params
,
35 bool enable_auto_throttling
)
36 : client_(client
.Pass()),
37 oracle_(base::TimeDelta::FromMicroseconds(static_cast<int64
>(
38 1000000.0 / params
.requested_format
.frame_rate
+
39 0.5 /* to round to nearest int */)),
40 params
.requested_format
.frame_size
,
41 params
.resolution_change_policy
,
42 enable_auto_throttling
),
46 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {
49 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
50 VideoCaptureOracle::Event event
,
51 const gfx::Rect
& damage_rect
,
52 base::TimeTicks event_time
,
53 scoped_refptr
<VideoFrame
>* storage
,
54 CaptureFrameCallback
* callback
) {
55 // Grab the current time before waiting to acquire the |lock_|.
56 const base::TimeTicks capture_begin_time
= base::TimeTicks::Now();
58 base::AutoLock
guard(lock_
);
61 return false; // Capture is stopped.
63 const bool should_capture
=
64 oracle_
.ObserveEventAndDecideCapture(event
, damage_rect
, event_time
);
65 const gfx::Size visible_size
= oracle_
.capture_size();
66 // Always round up the coded size to multiple of 16 pixels.
67 // See http://crbug.com/402151.
68 const gfx::Size
coded_size((visible_size
.width() + 15) & ~15,
69 (visible_size
.height() + 15) & ~15);
71 scoped_ptr
<media::VideoCaptureDevice::Client::Buffer
> output_buffer(
72 client_
->ReserveOutputBuffer(coded_size
,
73 (params_
.requested_format
.pixel_storage
!=
74 media::PIXEL_STORAGE_TEXTURE
)
75 ? media::PIXEL_FORMAT_I420
76 : media::PIXEL_FORMAT_ARGB
,
77 params_
.requested_format
.pixel_storage
));
78 // Get the current buffer pool utilization and attenuate it: The utilization
79 // reported to the oracle is in terms of a maximum sustainable amount (not the
81 const double attenuated_utilization
=
82 client_
->GetBufferPoolUtilization() *
83 (100.0 / kTargetMaxPoolUtilizationPercent
);
85 const char* event_name
=
86 (event
== VideoCaptureOracle::kTimerPoll
88 : (event
== VideoCaptureOracle::kCompositorUpdate
? "gpu"
91 // Consider the various reasons not to initiate a capture.
92 if (should_capture
&& !output_buffer
.get()) {
93 TRACE_EVENT_INSTANT1("gpu.capture", "PipelineLimited",
94 TRACE_EVENT_SCOPE_THREAD
, "trigger", event_name
);
95 oracle_
.RecordWillNotCapture(attenuated_utilization
);
97 } else if (!should_capture
&& output_buffer
.get()) {
98 if (event
== VideoCaptureOracle::kCompositorUpdate
) {
99 // This is a normal and acceptable way to drop a frame. We've hit our
100 // capture rate limit: for example, the content is animating at 60fps but
101 // we're capturing at 30fps.
102 TRACE_EVENT_INSTANT1("gpu.capture", "FpsRateLimited",
103 TRACE_EVENT_SCOPE_THREAD
, "trigger", event_name
);
106 } else if (!should_capture
&& !output_buffer
.get()) {
107 // We decided not to capture, but we wouldn't have been able to if we wanted
108 // to because no output buffer was available.
109 TRACE_EVENT_INSTANT1("gpu.capture", "NearlyPipelineLimited",
110 TRACE_EVENT_SCOPE_THREAD
, "trigger", event_name
);
113 const int frame_number
= oracle_
.RecordCapture(attenuated_utilization
);
114 TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", output_buffer
.get(),
115 "frame_number", frame_number
, "trigger", event_name
);
116 // Texture frames wrap a texture mailbox, which we don't have at the moment.
117 // We do not construct those frames.
118 if (params_
.requested_format
.pixel_storage
!= media::PIXEL_STORAGE_TEXTURE
) {
119 *storage
= VideoFrame::WrapExternalData(
120 media::PIXEL_FORMAT_I420
, coded_size
, gfx::Rect(visible_size
),
121 visible_size
, static_cast<uint8
*>(output_buffer
->data()),
122 output_buffer
->mapped_size(), base::TimeDelta());
126 base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame
, this, frame_number
,
127 base::Passed(&output_buffer
), capture_begin_time
,
128 oracle_
.estimated_frame_duration());
132 gfx::Size
ThreadSafeCaptureOracle::GetCaptureSize() const {
133 base::AutoLock
guard(lock_
);
134 return oracle_
.capture_size();
137 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size
& source_size
) {
138 base::AutoLock
guard(lock_
);
139 VLOG(1) << "Source size changed to " << source_size
.ToString();
140 oracle_
.SetSourceSize(source_size
);
143 void ThreadSafeCaptureOracle::Stop() {
144 base::AutoLock
guard(lock_
);
148 void ThreadSafeCaptureOracle::ReportError(const std::string
& reason
) {
149 base::AutoLock
guard(lock_
);
151 client_
->OnError(reason
);
154 void ThreadSafeCaptureOracle::DidCaptureFrame(
156 scoped_ptr
<VideoCaptureDevice::Client::Buffer
> buffer
,
157 base::TimeTicks capture_begin_time
,
158 base::TimeDelta estimated_frame_duration
,
159 const scoped_refptr
<VideoFrame
>& frame
,
160 base::TimeTicks timestamp
,
162 base::AutoLock
guard(lock_
);
163 TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", buffer
.get(), "success",
164 success
, "timestamp", timestamp
.ToInternalValue());
166 if (oracle_
.CompleteCapture(frame_number
, success
, ×tamp
)) {
167 TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded",
168 TRACE_EVENT_SCOPE_THREAD
);
171 return; // Capture is stopped.
173 frame
->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE
,
174 params_
.requested_format
.frame_rate
);
175 frame
->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME
,
177 frame
->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME
,
178 base::TimeTicks::Now());
179 frame
->metadata()->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION
,
180 estimated_frame_duration
);
182 frame
->AddDestructionObserver(
183 base::Bind(&ThreadSafeCaptureOracle::DidConsumeFrame
, this,
184 frame_number
, frame
->metadata()));
186 client_
->OnIncomingCapturedVideoFrame(buffer
.Pass(), frame
, timestamp
);
190 void ThreadSafeCaptureOracle::DidConsumeFrame(
192 const media::VideoFrameMetadata
* metadata
) {
193 // Note: This function may be called on any thread by the VideoFrame
194 // destructor. |metadata| is still valid for read-access at this point.
195 double utilization
= -1.0;
196 if (metadata
->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION
,
198 base::AutoLock
guard(lock_
);
199 oracle_
.RecordConsumerFeedback(frame_number
, utilization
);