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/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"
22 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
23 scoped_ptr
<VideoCaptureDevice::Client
> client
,
24 const VideoCaptureParams
& params
)
25 : client_(client
.Pass()),
26 oracle_(base::TimeDelta::FromMicroseconds(
27 static_cast<int64
>(1000000.0 / params
.requested_format
.frame_rate
+
28 0.5 /* to round to nearest int */))),
30 resolution_chooser_(params
.requested_format
.frame_size
,
31 params
.resolution_change_policy
) {}
33 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
35 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
36 VideoCaptureOracle::Event event
,
37 const gfx::Rect
& damage_rect
,
38 base::TimeTicks event_time
,
39 scoped_refptr
<VideoFrame
>* storage
,
40 CaptureFrameCallback
* callback
) {
41 // Grab the current time before waiting to acquire the |lock_|.
42 const base::TimeTicks capture_begin_time
= base::TimeTicks::Now();
44 base::AutoLock
guard(lock_
);
47 return false; // Capture is stopped.
49 const gfx::Size visible_size
= resolution_chooser_
.capture_size();
50 // Always round up the coded size to multiple of 16 pixels.
51 // See http://crbug.com/402151.
52 const gfx::Size
coded_size((visible_size
.width() + 15) & ~15,
53 (visible_size
.height() + 15) & ~15);
55 scoped_ptr
<VideoCaptureDevice::Client::Buffer
> output_buffer(
56 client_
->ReserveOutputBuffer(params_
.requested_format
.pixel_format
,
58 // TODO(miu): Use current buffer pool utilization to drive automatic video
59 // resolution changes. http://crbug.com/156767.
60 VLOG(2) << "Current buffer pool utilization is "
61 << (client_
->GetBufferPoolUtilization() * 100.0) << '%';
63 const bool should_capture
=
64 oracle_
.ObserveEventAndDecideCapture(event
, damage_rect
, event_time
);
66 const char* event_name
=
67 (event
== VideoCaptureOracle::kTimerPoll
? "poll" :
68 (event
== VideoCaptureOracle::kCompositorUpdate
? "gpu" :
71 // Consider the various reasons not to initiate a capture.
72 if (should_capture
&& !output_buffer
.get()) {
73 TRACE_EVENT_INSTANT1("gpu.capture",
75 TRACE_EVENT_SCOPE_THREAD
,
79 } else if (!should_capture
&& output_buffer
.get()) {
80 if (event
== VideoCaptureOracle::kCompositorUpdate
) {
81 // This is a normal and acceptable way to drop a frame. We've hit our
82 // capture rate limit: for example, the content is animating at 60fps but
83 // we're capturing at 30fps.
84 TRACE_EVENT_INSTANT1("gpu.capture", "FpsRateLimited",
85 TRACE_EVENT_SCOPE_THREAD
,
86 "trigger", event_name
);
89 } else if (!should_capture
&& !output_buffer
.get()) {
90 // We decided not to capture, but we wouldn't have been able to if we wanted
91 // to because no output buffer was available.
92 TRACE_EVENT_INSTANT1("gpu.capture", "NearlyPipelineLimited",
93 TRACE_EVENT_SCOPE_THREAD
,
94 "trigger", event_name
);
97 int frame_number
= oracle_
.RecordCapture();
98 TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", output_buffer
.get(),
99 "frame_number", frame_number
,
100 "trigger", event_name
);
101 // NATIVE_TEXTURE frames wrap a texture mailbox, which we don't have at the
102 // moment. We do not construct those frames.
103 if (params_
.requested_format
.pixel_format
!= PIXEL_FORMAT_TEXTURE
) {
104 *storage
= VideoFrame::WrapExternalData(
107 gfx::Rect(visible_size
),
109 static_cast<uint8
*>(output_buffer
->data()),
110 output_buffer
->size(),
114 *callback
= base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame
,
117 base::Passed(&output_buffer
),
119 oracle_
.estimated_frame_duration());
123 gfx::Size
ThreadSafeCaptureOracle::GetCaptureSize() const {
124 base::AutoLock
guard(lock_
);
125 return resolution_chooser_
.capture_size();
128 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size
& source_size
) {
129 base::AutoLock
guard(lock_
);
130 resolution_chooser_
.SetSourceSize(source_size
);
131 VLOG(1) << "Source size changed to " << source_size
.ToString()
132 << " --> Capture size is now "
133 << resolution_chooser_
.capture_size().ToString();
136 void ThreadSafeCaptureOracle::Stop() {
137 base::AutoLock
guard(lock_
);
141 void ThreadSafeCaptureOracle::ReportError(const std::string
& reason
) {
142 base::AutoLock
guard(lock_
);
144 client_
->OnError(reason
);
147 void ThreadSafeCaptureOracle::DidCaptureFrame(
149 scoped_ptr
<VideoCaptureDevice::Client::Buffer
> buffer
,
150 base::TimeTicks capture_begin_time
,
151 base::TimeDelta estimated_frame_duration
,
152 const scoped_refptr
<VideoFrame
>& frame
,
153 base::TimeTicks timestamp
,
155 base::AutoLock
guard(lock_
);
156 TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", buffer
.get(),
158 "timestamp", timestamp
.ToInternalValue());
160 if (oracle_
.CompleteCapture(frame_number
, success
, ×tamp
)) {
161 TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded",
162 TRACE_EVENT_SCOPE_THREAD
);
165 return; // Capture is stopped.
167 frame
->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE
,
168 params_
.requested_format
.frame_rate
);
169 frame
->metadata()->SetTimeTicks(
170 VideoFrameMetadata::CAPTURE_BEGIN_TIME
, capture_begin_time
);
171 frame
->metadata()->SetTimeTicks(
172 VideoFrameMetadata::CAPTURE_END_TIME
, base::TimeTicks::Now());
173 frame
->metadata()->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION
,
174 estimated_frame_duration
);
176 frame
->AddDestructionObserver(base::Bind(
177 &ThreadSafeCaptureOracle::DidConsumeFrame
,
182 client_
->OnIncomingCapturedVideoFrame(buffer
.Pass(), frame
, timestamp
);
186 void ThreadSafeCaptureOracle::DidConsumeFrame(
188 const media::VideoFrameMetadata
* metadata
) {
189 // Note: This function may be called on any thread by the VideoFrame
190 // destructor. |metadata| is still valid for read-access at this point.
191 double utilization
= -1.0;
192 if (metadata
->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION
,
194 utilization
>= 0.0) {
195 VLOG(2) << "Consumer resource utilization for frame " << frame_number
196 << ": " << utilization
;
199 // TODO(miu): Use |utilization| to drive automatic video resolution changes.
200 // http://crbug.com/156767.