1 // Copyright 2013 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 "content/browser/renderer_host/media/video_capture_device_impl.h" 6 7 #include "base/basictypes.h" 8 #include "base/bind.h" 9 #include "base/callback_forward.h" 10 #include "base/callback_helpers.h" 11 #include "base/debug/trace_event.h" 12 #include "base/logging.h" 13 #include "base/memory/scoped_ptr.h" 14 #include "base/memory/weak_ptr.h" 15 #include "base/message_loop/message_loop_proxy.h" 16 #include "base/metrics/histogram.h" 17 #include "base/sequenced_task_runner.h" 18 #include "base/strings/stringprintf.h" 19 #include "base/synchronization/lock.h" 20 #include "base/threading/thread.h" 21 #include "base/threading/thread_checker.h" 22 #include "base/time/time.h" 23 #include "content/public/browser/browser_thread.h" 24 #include "media/base/bind_to_loop.h" 25 #include "media/base/video_frame.h" 26 #include "media/base/video_util.h" 27 #include "media/video/capture/video_capture_types.h" 28 #include "ui/gfx/rect.h" 29 30 namespace content { 31 32 namespace { 33 34 void DeleteCaptureMachineOnUIThread( 35 scoped_ptr<VideoCaptureMachine> capture_machine) { 36 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 37 if (capture_machine) { 38 capture_machine->Stop(); 39 capture_machine.reset(); 40 } 41 } 42 43 } // namespace 44 45 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle( 46 scoped_ptr<media::VideoCaptureDevice::Client> client, 47 scoped_ptr<VideoCaptureOracle> oracle, 48 const media::VideoCaptureParams& params) 49 : client_(client.Pass()), 50 oracle_(oracle.Pass()), 51 params_(params), 52 capture_size_updated_(false) { 53 // Frame dimensions must each be an even integer since the client wants (or 54 // will convert to) YUV420. 55 capture_size_ = gfx::Size( 56 MakeEven(params.requested_format.frame_size.width()), 57 MakeEven(params.requested_format.frame_size.height())); 58 frame_rate_ = params.requested_format.frame_rate; 59 } 60 61 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {} 62 63 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture( 64 VideoCaptureOracle::Event event, 65 base::Time event_time, 66 scoped_refptr<media::VideoFrame>* storage, 67 CaptureFrameCallback* callback) { 68 base::AutoLock guard(lock_); 69 70 if (!client_) 71 return false; // Capture is stopped. 72 73 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer = 74 client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_); 75 const bool should_capture = 76 oracle_->ObserveEventAndDecideCapture(event, event_time); 77 const bool content_is_dirty = 78 (event == VideoCaptureOracle::kCompositorUpdate || 79 event == VideoCaptureOracle::kSoftwarePaint); 80 const char* event_name = 81 (event == VideoCaptureOracle::kTimerPoll ? "poll" : 82 (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" : 83 "paint")); 84 85 // Consider the various reasons not to initiate a capture. 86 if (should_capture && !output_buffer) { 87 TRACE_EVENT_INSTANT1("mirroring", 88 "EncodeLimited", 89 TRACE_EVENT_SCOPE_THREAD, 90 "trigger", 91 event_name); 92 return false; 93 } else if (!should_capture && output_buffer) { 94 if (content_is_dirty) { 95 // This is a normal and acceptable way to drop a frame. We've hit our 96 // capture rate limit: for example, the content is animating at 60fps but 97 // we're capturing at 30fps. 98 TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited", 99 TRACE_EVENT_SCOPE_THREAD, 100 "trigger", event_name); 101 } 102 return false; 103 } else if (!should_capture && !output_buffer) { 104 // We decided not to capture, but we wouldn't have been able to if we wanted 105 // to because no output buffer was available. 106 TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited", 107 TRACE_EVENT_SCOPE_THREAD, 108 "trigger", event_name); 109 return false; 110 } 111 int frame_number = oracle_->RecordCapture(); 112 TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(), 113 "frame_number", frame_number, 114 "trigger", event_name); 115 *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame, 116 this, 117 output_buffer, 118 frame_number); 119 *storage = media::VideoFrame::WrapExternalPackedMemory( 120 media::VideoFrame::I420, 121 capture_size_, 122 gfx::Rect(capture_size_), 123 capture_size_, 124 static_cast<uint8*>(output_buffer->data()), 125 output_buffer->size(), 126 base::SharedMemory::NULLHandle(), 127 base::TimeDelta(), 128 base::Closure()); 129 return true; 130 } 131 132 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) { 133 base::AutoLock guard(lock_); 134 135 // If this is the first call to UpdateCaptureSize(), or the receiver supports 136 // variable resolution, then determine the capture size by treating the 137 // requested width and height as maxima. 138 if (!capture_size_updated_ || params_.allow_resolution_change) { 139 // The capture resolution should not exceed the source frame size. 140 // In other words it should downscale the image but not upscale it. 141 if (source_size.width() > params_.requested_format.frame_size.width() || 142 source_size.height() > params_.requested_format.frame_size.height()) { 143 gfx::Rect capture_rect = media::ComputeLetterboxRegion( 144 gfx::Rect(params_.requested_format.frame_size), source_size); 145 capture_size_ = gfx::Size(MakeEven(capture_rect.width()), 146 MakeEven(capture_rect.height())); 147 } else { 148 capture_size_ = gfx::Size(MakeEven(source_size.width()), 149 MakeEven(source_size.height())); 150 } 151 capture_size_updated_ = true; 152 } 153 } 154 155 void ThreadSafeCaptureOracle::Stop() { 156 base::AutoLock guard(lock_); 157 client_.reset(); 158 } 159 160 void ThreadSafeCaptureOracle::ReportError() { 161 base::AutoLock guard(lock_); 162 if (client_) 163 client_->OnError(); 164 } 165 166 void ThreadSafeCaptureOracle::DidCaptureFrame( 167 scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer, 168 int frame_number, 169 base::Time timestamp, 170 bool success) { 171 base::AutoLock guard(lock_); 172 TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(), 173 "success", success, 174 "timestamp", timestamp.ToInternalValue()); 175 176 if (!client_) 177 return; // Capture is stopped. 178 179 if (success) { 180 if (oracle_->CompleteCapture(frame_number, timestamp)) { 181 client_->OnIncomingCapturedBuffer(buffer, 182 media::VideoFrame::I420, 183 capture_size_, 184 timestamp, 185 frame_rate_); 186 } 187 } 188 } 189 190 void VideoCaptureDeviceImpl::AllocateAndStart( 191 const media::VideoCaptureParams& params, 192 scoped_ptr<media::VideoCaptureDevice::Client> client) { 193 DCHECK(thread_checker_.CalledOnValidThread()); 194 195 if (state_ != kIdle) { 196 DVLOG(1) << "Allocate() invoked when not in state Idle."; 197 return; 198 } 199 200 if (params.requested_format.frame_rate <= 0) { 201 DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate; 202 client->OnError(); 203 return; 204 } 205 206 if (params.requested_format.frame_size.width() < kMinFrameWidth || 207 params.requested_format.frame_size.height() < kMinFrameHeight) { 208 DVLOG(1) << "invalid frame size: " 209 << params.requested_format.frame_size.ToString(); 210 client->OnError(); 211 return; 212 } 213 214 base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds( 215 1000000.0 / params.requested_format.frame_rate + 0.5); 216 217 scoped_ptr<VideoCaptureOracle> oracle( 218 new VideoCaptureOracle(capture_period, 219 kAcceleratedSubscriberIsSupported)); 220 oracle_proxy_ = 221 new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), params); 222 223 // Starts the capture machine asynchronously. 224 BrowserThread::PostTaskAndReplyWithResult( 225 BrowserThread::UI, FROM_HERE, 226 base::Bind(&VideoCaptureMachine::Start, 227 base::Unretained(capture_machine_.get()), 228 oracle_proxy_), 229 base::Bind(&VideoCaptureDeviceImpl::CaptureStarted, 230 AsWeakPtr())); 231 232 TransitionStateTo(kCapturing); 233 } 234 235 void VideoCaptureDeviceImpl::StopAndDeAllocate() { 236 DCHECK(thread_checker_.CalledOnValidThread()); 237 238 if (state_ != kCapturing) 239 return; 240 241 oracle_proxy_->Stop(); 242 oracle_proxy_ = NULL; 243 244 TransitionStateTo(kIdle); 245 246 // Stops the capture machine asynchronously. 247 BrowserThread::PostTask( 248 BrowserThread::UI, FROM_HERE, base::Bind( 249 &VideoCaptureMachine::Stop, 250 base::Unretained(capture_machine_.get()))); 251 } 252 253 void VideoCaptureDeviceImpl::CaptureStarted(bool success) { 254 DCHECK(thread_checker_.CalledOnValidThread()); 255 if (!success) { 256 DVLOG(1) << "Failed to start capture machine."; 257 Error(); 258 } 259 } 260 261 VideoCaptureDeviceImpl::VideoCaptureDeviceImpl( 262 scoped_ptr<VideoCaptureMachine> capture_machine) 263 : state_(kIdle), 264 capture_machine_(capture_machine.Pass()) {} 265 266 VideoCaptureDeviceImpl::~VideoCaptureDeviceImpl() { 267 // If capture_machine is not NULL, then we need to return to the UI thread to 268 // safely stop the capture machine. 269 if (capture_machine_) { 270 BrowserThread::PostTask( 271 BrowserThread::UI, FROM_HERE, base::Bind( 272 &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_))); 273 } 274 DVLOG(1) << "VideoCaptureDeviceImpl@" << this << " destroying."; 275 } 276 277 void VideoCaptureDeviceImpl::TransitionStateTo(State next_state) { 278 DCHECK(thread_checker_.CalledOnValidThread()); 279 280 #ifndef NDEBUG 281 static const char* kStateNames[] = { 282 "Idle", "Allocated", "Capturing", "Error" 283 }; 284 DVLOG(1) << "State change: " << kStateNames[state_] 285 << " --> " << kStateNames[next_state]; 286 #endif 287 288 state_ = next_state; 289 } 290 291 void VideoCaptureDeviceImpl::Error() { 292 DCHECK(thread_checker_.CalledOnValidThread()); 293 294 if (state_ == kIdle) 295 return; 296 297 if (oracle_proxy_) 298 oracle_proxy_->ReportError(); 299 300 StopAndDeAllocate(); 301 TransitionStateTo(kError); 302 } 303 304 } // namespace content 305