| OLD | NEW |
| (Empty) |
| 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/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/synchronization/lock.h" | |
| 17 #include "base/time/time.h" | |
| 18 #include "base/trace_event/trace_event.h" | |
| 19 #include "media/base/video_capture_types.h" | |
| 20 #include "media/base/video_frame.h" | |
| 21 #include "media/base/video_frame_metadata.h" | |
| 22 #include "media/base/video_util.h" | |
| 23 #include "ui/gfx/geometry/rect.h" | |
| 24 | |
| 25 namespace media { | |
| 26 | |
| 27 namespace { | |
| 28 | |
| 29 // The target maximum amount of the buffer pool to utilize. Actual buffer pool | |
| 30 // utilization is attenuated by this amount before being reported to the | |
| 31 // VideoCaptureOracle. This value takes into account the maximum number of | |
| 32 // buffer pool buffers and a desired safety margin. | |
| 33 const int kTargetMaxPoolUtilizationPercent = 60; | |
| 34 | |
| 35 } // namespace | |
| 36 | |
| 37 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle( | |
| 38 std::unique_ptr<VideoCaptureDevice::Client> client, | |
| 39 const VideoCaptureParams& params, | |
| 40 bool enable_auto_throttling) | |
| 41 : client_(std::move(client)), | |
| 42 oracle_(base::TimeDelta::FromMicroseconds(static_cast<int64_t>( | |
| 43 1000000.0 / params.requested_format.frame_rate + | |
| 44 0.5 /* to round to nearest int */)), | |
| 45 params.requested_format.frame_size, | |
| 46 params.resolution_change_policy, | |
| 47 enable_auto_throttling), | |
| 48 params_(params) {} | |
| 49 | |
| 50 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() { | |
| 51 } | |
| 52 | |
| 53 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture( | |
| 54 VideoCaptureOracle::Event event, | |
| 55 const gfx::Rect& damage_rect, | |
| 56 base::TimeTicks event_time, | |
| 57 scoped_refptr<VideoFrame>* storage, | |
| 58 CaptureFrameCallback* callback) { | |
| 59 // Grab the current time before waiting to acquire the |lock_|. | |
| 60 const base::TimeTicks capture_begin_time = base::TimeTicks::Now(); | |
| 61 | |
| 62 gfx::Size visible_size; | |
| 63 gfx::Size coded_size; | |
| 64 std::unique_ptr<media::VideoCaptureDevice::Client::Buffer> output_buffer; | |
| 65 double attenuated_utilization; | |
| 66 int frame_number; | |
| 67 base::TimeDelta estimated_frame_duration; | |
| 68 { | |
| 69 base::AutoLock guard(lock_); | |
| 70 | |
| 71 if (!client_) | |
| 72 return false; // Capture is stopped. | |
| 73 | |
| 74 if (!oracle_.ObserveEventAndDecideCapture(event, damage_rect, event_time)) { | |
| 75 // This is a normal and acceptable way to drop a frame. We've hit our | |
| 76 // capture rate limit: for example, the content is animating at 60fps but | |
| 77 // we're capturing at 30fps. | |
| 78 TRACE_EVENT_INSTANT1("gpu.capture", "FpsRateLimited", | |
| 79 TRACE_EVENT_SCOPE_THREAD, "trigger", | |
| 80 VideoCaptureOracle::EventAsString(event)); | |
| 81 return false; | |
| 82 } | |
| 83 | |
| 84 visible_size = oracle_.capture_size(); | |
| 85 // TODO(miu): Clients should request exact padding, instead of this | |
| 86 // memory-wasting hack to make frames that are compatible with all HW | |
| 87 // encoders. http://crbug.com/555911 | |
| 88 coded_size.SetSize(base::bits::Align(visible_size.width(), 16), | |
| 89 base::bits::Align(visible_size.height(), 16)); | |
| 90 | |
| 91 if (event == VideoCaptureOracle::kPassiveRefreshRequest) { | |
| 92 output_buffer = client_->ResurrectLastOutputBuffer( | |
| 93 coded_size, params_.requested_format.pixel_format, | |
| 94 params_.requested_format.pixel_storage); | |
| 95 if (!output_buffer) { | |
| 96 TRACE_EVENT_INSTANT0("gpu.capture", "ResurrectionFailed", | |
| 97 TRACE_EVENT_SCOPE_THREAD); | |
| 98 return false; | |
| 99 } | |
| 100 } else { | |
| 101 output_buffer = client_->ReserveOutputBuffer( | |
| 102 coded_size, params_.requested_format.pixel_format, | |
| 103 params_.requested_format.pixel_storage); | |
| 104 } | |
| 105 | |
| 106 // Get the current buffer pool utilization and attenuate it: The utilization | |
| 107 // reported to the oracle is in terms of a maximum sustainable amount (not | |
| 108 // the absolute maximum). | |
| 109 attenuated_utilization = client_->GetBufferPoolUtilization() * | |
| 110 (100.0 / kTargetMaxPoolUtilizationPercent); | |
| 111 | |
| 112 if (!output_buffer) { | |
| 113 TRACE_EVENT_INSTANT2( | |
| 114 "gpu.capture", "PipelineLimited", TRACE_EVENT_SCOPE_THREAD, "trigger", | |
| 115 VideoCaptureOracle::EventAsString(event), "atten_util_percent", | |
| 116 base::saturated_cast<int>(attenuated_utilization * 100.0 + 0.5)); | |
| 117 oracle_.RecordWillNotCapture(attenuated_utilization); | |
| 118 return false; | |
| 119 } | |
| 120 | |
| 121 frame_number = oracle_.RecordCapture(attenuated_utilization); | |
| 122 estimated_frame_duration = oracle_.estimated_frame_duration(); | |
| 123 } // End of critical section. | |
| 124 | |
| 125 if (attenuated_utilization >= 1.0) { | |
| 126 TRACE_EVENT_INSTANT2( | |
| 127 "gpu.capture", "NearlyPipelineLimited", TRACE_EVENT_SCOPE_THREAD, | |
| 128 "trigger", VideoCaptureOracle::EventAsString(event), | |
| 129 "atten_util_percent", | |
| 130 base::saturated_cast<int>(attenuated_utilization * 100.0 + 0.5)); | |
| 131 } | |
| 132 | |
| 133 TRACE_EVENT_ASYNC_BEGIN2("gpu.capture", "Capture", output_buffer.get(), | |
| 134 "frame_number", frame_number, "trigger", | |
| 135 VideoCaptureOracle::EventAsString(event)); | |
| 136 | |
| 137 DCHECK_EQ(media::PIXEL_STORAGE_CPU, params_.requested_format.pixel_storage); | |
| 138 *storage = VideoFrame::WrapExternalSharedMemory( | |
| 139 params_.requested_format.pixel_format, coded_size, | |
| 140 gfx::Rect(visible_size), visible_size, | |
| 141 static_cast<uint8_t*>(output_buffer->data()), | |
| 142 output_buffer->mapped_size(), base::SharedMemory::NULLHandle(), 0u, | |
| 143 base::TimeDelta()); | |
| 144 // If creating the VideoFrame wrapper failed, call DidCaptureFrame() with | |
| 145 // !success to execute the required post-capture steps (tracing, notification | |
| 146 // of failure to VideoCaptureOracle, etc.). | |
| 147 if (!(*storage)) { | |
| 148 DidCaptureFrame(frame_number, std::move(output_buffer), capture_begin_time, | |
| 149 estimated_frame_duration, *storage, event_time, false); | |
| 150 return false; | |
| 151 } | |
| 152 | |
| 153 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame, this, | |
| 154 frame_number, base::Passed(&output_buffer), | |
| 155 capture_begin_time, estimated_frame_duration); | |
| 156 | |
| 157 return true; | |
| 158 } | |
| 159 | |
| 160 bool ThreadSafeCaptureOracle::AttemptPassiveRefresh() { | |
| 161 const base::TimeTicks refresh_time = base::TimeTicks::Now(); | |
| 162 | |
| 163 scoped_refptr<VideoFrame> frame; | |
| 164 CaptureFrameCallback capture_callback; | |
| 165 if (!ObserveEventAndDecideCapture(VideoCaptureOracle::kPassiveRefreshRequest, | |
| 166 gfx::Rect(), refresh_time, &frame, | |
| 167 &capture_callback)) { | |
| 168 return false; | |
| 169 } | |
| 170 | |
| 171 capture_callback.Run(frame, refresh_time, true); | |
| 172 return true; | |
| 173 } | |
| 174 | |
| 175 gfx::Size ThreadSafeCaptureOracle::GetCaptureSize() const { | |
| 176 base::AutoLock guard(lock_); | |
| 177 return oracle_.capture_size(); | |
| 178 } | |
| 179 | |
| 180 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) { | |
| 181 base::AutoLock guard(lock_); | |
| 182 VLOG(1) << "Source size changed to " << source_size.ToString(); | |
| 183 oracle_.SetSourceSize(source_size); | |
| 184 } | |
| 185 | |
| 186 void ThreadSafeCaptureOracle::Stop() { | |
| 187 base::AutoLock guard(lock_); | |
| 188 client_.reset(); | |
| 189 } | |
| 190 | |
| 191 void ThreadSafeCaptureOracle::ReportError( | |
| 192 const tracked_objects::Location& from_here, | |
| 193 const std::string& reason) { | |
| 194 base::AutoLock guard(lock_); | |
| 195 if (client_) | |
| 196 client_->OnError(from_here, reason); | |
| 197 } | |
| 198 | |
| 199 void ThreadSafeCaptureOracle::DidCaptureFrame( | |
| 200 int frame_number, | |
| 201 std::unique_ptr<VideoCaptureDevice::Client::Buffer> buffer, | |
| 202 base::TimeTicks capture_begin_time, | |
| 203 base::TimeDelta estimated_frame_duration, | |
| 204 const scoped_refptr<VideoFrame>& frame, | |
| 205 base::TimeTicks reference_time, | |
| 206 bool success) { | |
| 207 TRACE_EVENT_ASYNC_END2("gpu.capture", "Capture", buffer.get(), "success", | |
| 208 success, "timestamp", | |
| 209 reference_time.ToInternalValue()); | |
| 210 | |
| 211 base::AutoLock guard(lock_); | |
| 212 | |
| 213 if (oracle_.CompleteCapture(frame_number, success, &reference_time)) { | |
| 214 TRACE_EVENT_INSTANT0("gpu.capture", "CaptureSucceeded", | |
| 215 TRACE_EVENT_SCOPE_THREAD); | |
| 216 | |
| 217 if (!client_) | |
| 218 return; // Capture is stopped. | |
| 219 | |
| 220 frame->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE, | |
| 221 params_.requested_format.frame_rate); | |
| 222 frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_BEGIN_TIME, | |
| 223 capture_begin_time); | |
| 224 frame->metadata()->SetTimeTicks(VideoFrameMetadata::CAPTURE_END_TIME, | |
| 225 base::TimeTicks::Now()); | |
| 226 frame->metadata()->SetTimeDelta(VideoFrameMetadata::FRAME_DURATION, | |
| 227 estimated_frame_duration); | |
| 228 frame->metadata()->SetTimeTicks(VideoFrameMetadata::REFERENCE_TIME, | |
| 229 reference_time); | |
| 230 | |
| 231 frame->AddDestructionObserver( | |
| 232 base::Bind(&ThreadSafeCaptureOracle::DidConsumeFrame, this, | |
| 233 frame_number, frame->metadata())); | |
| 234 | |
| 235 client_->OnIncomingCapturedVideoFrame(std::move(buffer), frame); | |
| 236 } | |
| 237 } | |
| 238 | |
| 239 void ThreadSafeCaptureOracle::DidConsumeFrame( | |
| 240 int frame_number, | |
| 241 const media::VideoFrameMetadata* metadata) { | |
| 242 // Note: This function may be called on any thread by the VideoFrame | |
| 243 // destructor. |metadata| is still valid for read-access at this point. | |
| 244 double utilization = -1.0; | |
| 245 if (metadata->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION, | |
| 246 &utilization)) { | |
| 247 base::AutoLock guard(lock_); | |
| 248 oracle_.RecordConsumerFeedback(frame_number, utilization); | |
| 249 } | |
| 250 } | |
| 251 | |
| 252 } // namespace media | |
| OLD | NEW |