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
201
202
203
|
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/capture/content/thread_safe_capture_oracle.h"
#include "base/basictypes.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "media/base/video_capture_types.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_metadata.h"
#include "media/base/video_util.h"
#include "ui/gfx/geometry/rect.h"
namespace media {
namespace {
// The target maximum amount of the buffer pool to utilize. Actual buffer pool
// utilization is attenuated by this amount before being reported to the
// VideoCaptureOracle. This value takes into account the maximum number of
// buffer pool buffers and a desired safety margin.
const int kTargetMaxPoolUtilizationPercent = 60;
} // namespace
ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
scoped_ptr<VideoCaptureDevice::Client> client,
const VideoCaptureParams& params,
bool enable_auto_throttling)
: client_(client.Pass()),
oracle_(base::TimeDelta::FromMicroseconds(static_cast<int64>(
1000000.0 / params.requested_format.frame_rate +
0.5 /* to round to nearest int */)),
params.requested_format.frame_size,
params.resolution_change_policy,
enable_auto_throttling),
params_(params) {
}
ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {
}
bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
VideoCaptureOracle::Event event,
const gfx::Rect& damage_rect,
base::TimeTicks event_time,
scoped_refptr<VideoFrame>* storage,
CaptureFrameCallback* callback) {
// Grab the current time before waiting to acquire the |lock_|.
const base::TimeTicks capture_begin_time = base::TimeTicks::Now();
base::AutoLock guard(lock_);
if (!client_)
return false; // Capture is stopped.
const bool should_capture =
oracle_.ObserveEventAndDecideCapture(event, damage_rect, event_time);
const gfx::Size visible_size = oracle_.capture_size();
// Always round up the coded size to multiple of 16 pixels.
// See http://crbug.com/402151.
const gfx::Size coded_size((visible_size.width() + 15) & ~15,
(visible_size.height() + 15) & ~15);
scoped_ptr<media::VideoCaptureDevice::Client::Buffer> output_buffer(
client_->ReserveOutputBuffer(coded_size,
(params_.requested_format.pixel_storage !=
media::PIXEL_STORAGE_TEXTURE)
? media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
: media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB,
params_.requested_format.pixel_storage));
// Get the current buffer pool utilization and attenuate it: The utilization
// reported to the oracle is in terms of a maximum sustainable amount (not the
// absolute maximum).
const double attenuated_utilization =
client_->GetBufferPoolUtilization() *
(100.0 / kTargetMaxPoolUtilizationPercent);
const char* event_name =
(event == VideoCaptureOracle::kTimerPoll
? "poll"
: (event == VideoCaptureOracle::kCompositorUpdate ? "gpu"
: "unknown"));
// Consider the various reasons not to initiate a capture.
if (should_capture && !output_buffer.get()) {
TRACE_EVENT_INSTANT1("gpu.capture", "PipelineLimited",
TRACE_EVENT_SCOPE_THREAD, "trigger", event_name);
oracle_.RecordWillNotCapture(attenuated_utilization);
return false;
} else if (!should_capture && output_buffer.get()) {
if (event == VideoCaptureOracle::kCompositorUpdate) {
// This is a normal and acceptable way to drop a frame. We've hit our
// capture rate limit: for example, the content is animating at 60fps but
// we're capturing at 30fps.
TRACE_EVENT_INSTANT1("gpu.capture", "FpsRateLimited",
TRACE_EVENT_SCOPE_THREAD, "trigger", event_name);
}
return false;
} else if (!should_capture && !output_buffer.get()) {
// We decided not to capture, but we wouldn't have been able to if we wanted
// to because no output buffer was available.
TRACE_EVENT_INSTANT1("gpu.capture", "NearlyPipelineLimited",
TRACE_EVENT_SCOPE_THREAD, "trigger", event_name);
return false;
}
const int frame_number = oracle_.RecordCapture(attenuated_utilization);
TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", output_buffer.get(),
"frame_number", frame_number, "trigger", event_name);
// Texture frames wrap a texture mailbox, which we don't have at the moment.
// We do not construct those frames.
if (params_.requested_format.pixel_storage != media::PIXEL_STORAGE_TEXTURE) {
*storage = VideoFrame::WrapExternalData(
media::PIXEL_FORMAT_I420, coded_size, gfx::Rect(visible_size),
visible_size, static_cast<uint8*>(output_buffer->data()),
output_buffer->mapped_size(), base::TimeDelta());
DCHECK(*storage);
}
*callback =
base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame, this, frame_number,
base::Passed(&output_buffer), capture_begin_time,
oracle_.estimated_frame_duration());
return true;
}
gfx::Size ThreadSafeCaptureOracle::GetCaptureSize() const {
base::AutoLock guard(lock_);
return oracle_.capture_size();
}
void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
base::AutoLock guard(lock_);
VLOG(1) << "Source size changed to " << source_size.ToString();
oracle_.SetSourceSize(source_size);
}
void ThreadSafeCaptureOracle::Stop() {
base::AutoLock guard(lock_);
client_.reset();
}
void ThreadSafeCaptureOracle::ReportError(const std::string& reason) {
base::AutoLock guard(lock_);
if (client_)
client_->OnError(reason);
}
void ThreadSafeCaptureOracle::DidCaptureFrame(
int frame_number,
scoped_ptr<VideoCaptureDevice::Client::Buffer> buffer,
base::TimeTicks capture_begin_time,
base::TimeDelta estimated_frame_duration,
const scoped_refptr<VideoFrame>& frame,
base::TimeTicks timestamp,
bool success) {
base::AutoLock guard(lock_);
TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", buffer.get(), "success",
success, "timestamp", timestamp.ToInternalValue());
if (oracle_.CompleteCapture(frame_number, success, ×tamp)) {
TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded",
TRACE_EVENT_SCOPE_THREAD);
if (!client_)
return; // Capture is stopped.
frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE,
params_.requested_format.frame_rate);
frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME,
capture_begin_time);
frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME,
base::TimeTicks::Now());
frame->metadata()->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION,
estimated_frame_duration);
frame->AddDestructionObserver(
base::Bind(&ThreadSafeCaptureOracle::DidConsumeFrame, this,
frame_number, frame->metadata()));
client_->OnIncomingCapturedVideoFrame(buffer.Pass(), frame, timestamp);
}
}
void ThreadSafeCaptureOracle::DidConsumeFrame(
int frame_number,
const media::VideoFrameMetadata* metadata) {
// Note: This function may be called on any thread by the VideoFrame
// destructor. |metadata| is still valid for read-access at this point.
double utilization = -1.0;
if (metadata->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION,
&utilization)) {
base::AutoLock guard(lock_);
oracle_.RecordConsumerFeedback(frame_number, utilization);
}
}
} // namespace media
|