Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / capture / content / thread_safe_capture_oracle.cc
blob22b1dcfc022fc235afad1dfc1ad7d232d2a8f9a5
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"
8 #include "base/bind.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"
20 namespace media {
22 namespace {
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;
30 } // namespace
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),
43 params_(params) {
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_);
60 if (!client_)
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
80 // absolute maximum).
81 const double attenuated_utilization =
82 client_->GetBufferPoolUtilization() *
83 (100.0 / kTargetMaxPoolUtilizationPercent);
85 const char* event_name =
86 (event == VideoCaptureOracle::kTimerPoll
87 ? "poll"
88 : (event == VideoCaptureOracle::kCompositorUpdate ? "gpu"
89 : "unknown"));
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);
96 return false;
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);
105 return false;
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);
111 return false;
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());
123 DCHECK(*storage);
125 *callback =
126 base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame, this, frame_number,
127 base::Passed(&output_buffer), capture_begin_time,
128 oracle_.estimated_frame_duration());
129 return true;
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_);
145 client_.reset();
148 void ThreadSafeCaptureOracle::ReportError(const std::string& reason) {
149 base::AutoLock guard(lock_);
150 if (client_)
151 client_->OnError(reason);
154 void ThreadSafeCaptureOracle::DidCaptureFrame(
155 int frame_number,
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,
161 bool success) {
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, &timestamp)) {
167 TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded",
168 TRACE_EVENT_SCOPE_THREAD);
170 if (!client_)
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,
176 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(
191 int frame_number,
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,
197 &utilization)) {
198 base::AutoLock guard(lock_);
199 oracle_.RecordConsumerFeedback(frame_number, utilization);
203 } // namespace media