1 // Copyright (c) 2012 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/renderer/media/video_capture_impl.h" 6 7 #include "base/bind.h" 8 #include "base/stl_util.h" 9 #include "content/child/child_process.h" 10 #include "content/common/media/video_capture_messages.h" 11 #include "media/base/bind_to_loop.h" 12 #include "media/base/limits.h" 13 #include "media/base/video_frame.h" 14 15 namespace content { 16 17 class VideoCaptureImpl::ClientBuffer 18 : public base::RefCountedThreadSafe<ClientBuffer> { 19 public: 20 ClientBuffer(scoped_ptr<base::SharedMemory> buffer, 21 size_t buffer_size) 22 : buffer(buffer.Pass()), 23 buffer_size(buffer_size) {} 24 const scoped_ptr<base::SharedMemory> buffer; 25 const size_t buffer_size; 26 27 private: 28 friend class base::RefCountedThreadSafe<ClientBuffer>; 29 30 virtual ~ClientBuffer() {} 31 32 DISALLOW_COPY_AND_ASSIGN(ClientBuffer); 33 }; 34 35 bool VideoCaptureImpl::CaptureStarted() { 36 return state_ == VIDEO_CAPTURE_STATE_STARTED; 37 } 38 39 int VideoCaptureImpl::CaptureFrameRate() { 40 return last_frame_format_.frame_rate; 41 } 42 43 VideoCaptureImpl::VideoCaptureImpl( 44 const media::VideoCaptureSessionId session_id, 45 base::MessageLoopProxy* capture_message_loop_proxy, 46 VideoCaptureMessageFilter* filter) 47 : VideoCapture(), 48 message_filter_(filter), 49 capture_message_loop_proxy_(capture_message_loop_proxy), 50 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()), 51 device_id_(0), 52 session_id_(session_id), 53 suspended_(false), 54 state_(VIDEO_CAPTURE_STATE_STOPPED), 55 weak_this_factory_(this) { 56 DCHECK(filter); 57 } 58 59 VideoCaptureImpl::~VideoCaptureImpl() {} 60 61 void VideoCaptureImpl::Init() { 62 if (!io_message_loop_proxy_->BelongsToCurrentThread()) { 63 io_message_loop_proxy_->PostTask(FROM_HERE, 64 base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread, 65 base::Unretained(this))); 66 } else { 67 AddDelegateOnIOThread(); 68 } 69 } 70 71 void VideoCaptureImpl::DeInit(base::Closure task) { 72 capture_message_loop_proxy_->PostTask(FROM_HERE, 73 base::Bind(&VideoCaptureImpl::DoDeInitOnCaptureThread, 74 base::Unretained(this), task)); 75 } 76 77 void VideoCaptureImpl::StartCapture( 78 media::VideoCapture::EventHandler* handler, 79 const media::VideoCaptureParams& params) { 80 capture_message_loop_proxy_->PostTask(FROM_HERE, 81 base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread, 82 base::Unretained(this), handler, params)); 83 } 84 85 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) { 86 capture_message_loop_proxy_->PostTask(FROM_HERE, 87 base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread, 88 base::Unretained(this), handler)); 89 } 90 91 void VideoCaptureImpl::OnBufferCreated( 92 base::SharedMemoryHandle handle, 93 int length, int buffer_id) { 94 capture_message_loop_proxy_->PostTask(FROM_HERE, 95 base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread, 96 base::Unretained(this), handle, length, buffer_id)); 97 } 98 99 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) { 100 capture_message_loop_proxy_->PostTask(FROM_HERE, 101 base::Bind(&VideoCaptureImpl::DoBufferDestroyedOnCaptureThread, 102 base::Unretained(this), buffer_id)); 103 } 104 105 void VideoCaptureImpl::OnBufferReceived( 106 int buffer_id, 107 base::Time timestamp, 108 const media::VideoCaptureFormat& format) { 109 capture_message_loop_proxy_->PostTask(FROM_HERE, 110 base::Bind(&VideoCaptureImpl::DoBufferReceivedOnCaptureThread, 111 base::Unretained(this), buffer_id, timestamp, format)); 112 } 113 114 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) { 115 capture_message_loop_proxy_->PostTask(FROM_HERE, 116 base::Bind(&VideoCaptureImpl::DoStateChangedOnCaptureThread, 117 base::Unretained(this), state)); 118 } 119 120 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) { 121 capture_message_loop_proxy_->PostTask(FROM_HERE, 122 base::Bind(&VideoCaptureImpl::DoDelegateAddedOnCaptureThread, 123 base::Unretained(this), device_id)); 124 } 125 126 void VideoCaptureImpl::SuspendCapture(bool suspend) { 127 capture_message_loop_proxy_->PostTask(FROM_HERE, 128 base::Bind(&VideoCaptureImpl::DoSuspendCaptureOnCaptureThread, 129 base::Unretained(this), suspend)); 130 } 131 132 void VideoCaptureImpl::DoDeInitOnCaptureThread(base::Closure task) { 133 if (state_ == VIDEO_CAPTURE_STATE_STARTED) 134 Send(new VideoCaptureHostMsg_Stop(device_id_)); 135 136 io_message_loop_proxy_->PostTask(FROM_HERE, 137 base::Bind(&VideoCaptureImpl::RemoveDelegateOnIOThread, 138 base::Unretained(this), task)); 139 } 140 141 void VideoCaptureImpl::DoStartCaptureOnCaptureThread( 142 media::VideoCapture::EventHandler* handler, 143 const media::VideoCaptureParams& params) { 144 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 145 146 if (state_ == VIDEO_CAPTURE_STATE_ERROR) { 147 handler->OnError(this, 1); 148 handler->OnRemoved(this); 149 } else if ((clients_pending_on_filter_.find(handler) != 150 clients_pending_on_filter_.end()) || 151 (clients_pending_on_restart_.find(handler) != 152 clients_pending_on_restart_.end()) || 153 clients_.find(handler) != clients_.end() ) { 154 // This client has started. 155 } else if (!device_id_) { 156 clients_pending_on_filter_[handler] = params; 157 } else { 158 handler->OnStarted(this); 159 if (state_ == VIDEO_CAPTURE_STATE_STARTED) { 160 clients_[handler] = params; 161 } else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) { 162 clients_pending_on_restart_[handler] = params; 163 DVLOG(1) << "StartCapture: Got new resolution " 164 << params.requested_format.frame_size.ToString() 165 << " during stopping."; 166 } else { 167 // TODO(sheu): Allowing resolution change will require that all 168 // outstanding clients of a capture session support resolution change. 169 DCHECK(!params.allow_resolution_change); 170 clients_[handler] = params; 171 DCHECK_EQ(1ul, clients_.size()); 172 params_ = params; 173 if (params_.requested_format.frame_rate > 174 media::limits::kMaxFramesPerSecond) { 175 params_.requested_format.frame_rate = 176 media::limits::kMaxFramesPerSecond; 177 } 178 DVLOG(1) << "StartCapture: starting with first resolution " 179 << params_.requested_format.frame_size.ToString(); 180 181 StartCaptureInternal(); 182 } 183 } 184 } 185 186 void VideoCaptureImpl::DoStopCaptureOnCaptureThread( 187 media::VideoCapture::EventHandler* handler) { 188 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 189 190 // A handler can be in only one client list. 191 // If this handler is in any client list, we can just remove it from 192 // that client list and don't have to run the other following RemoveClient(). 193 RemoveClient(handler, &clients_pending_on_filter_) || 194 RemoveClient(handler, &clients_pending_on_restart_) || 195 RemoveClient(handler, &clients_); 196 197 if (clients_.empty()) { 198 DVLOG(1) << "StopCapture: No more client, stopping ..."; 199 StopDevice(); 200 client_buffers_.clear(); 201 weak_this_factory_.InvalidateWeakPtrs(); 202 } 203 } 204 205 void VideoCaptureImpl::DoBufferCreatedOnCaptureThread( 206 base::SharedMemoryHandle handle, 207 int length, int buffer_id) { 208 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 209 210 // In case client calls StopCapture before the arrival of created buffer, 211 // just close this buffer and return. 212 if (state_ != VIDEO_CAPTURE_STATE_STARTED) { 213 base::SharedMemory::CloseHandle(handle); 214 return; 215 } 216 217 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false)); 218 if (!shm->Map(length)) { 219 DLOG(ERROR) << "DoBufferCreatedOnCaptureThread: Map() failed."; 220 return; 221 } 222 223 bool inserted = 224 client_buffers_.insert(std::make_pair( 225 buffer_id, 226 new ClientBuffer(shm.Pass(), 227 length))).second; 228 DCHECK(inserted); 229 } 230 231 void VideoCaptureImpl::DoBufferDestroyedOnCaptureThread(int buffer_id) { 232 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 233 234 ClientBufferMap::iterator iter = client_buffers_.find(buffer_id); 235 if (iter == client_buffers_.end()) 236 return; 237 238 DCHECK(!iter->second || iter->second->HasOneRef()) 239 << "Instructed to delete buffer we are still using."; 240 client_buffers_.erase(iter); 241 } 242 243 void VideoCaptureImpl::DoBufferReceivedOnCaptureThread( 244 int buffer_id, 245 base::Time timestamp, 246 const media::VideoCaptureFormat& format) { 247 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 248 249 if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) { 250 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id)); 251 return; 252 } 253 254 last_frame_format_ = format; 255 256 ClientBufferMap::iterator iter = client_buffers_.find(buffer_id); 257 DCHECK(iter != client_buffers_.end()); 258 scoped_refptr<ClientBuffer> buffer = iter->second; 259 scoped_refptr<media::VideoFrame> frame = 260 media::VideoFrame::WrapExternalPackedMemory( 261 media::VideoFrame::I420, 262 last_frame_format_.frame_size, 263 gfx::Rect(last_frame_format_.frame_size), 264 last_frame_format_.frame_size, 265 reinterpret_cast<uint8*>(buffer->buffer->memory()), 266 buffer->buffer_size, 267 buffer->buffer->handle(), 268 // TODO(sheu): convert VideoCaptureMessageFilter::Delegate to use 269 // base::TimeTicks instead of base::Time. http://crbug.com/249215 270 timestamp - base::Time::UnixEpoch(), 271 media::BindToLoop( 272 capture_message_loop_proxy_, 273 base::Bind( 274 &VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread, 275 weak_this_factory_.GetWeakPtr(), 276 buffer_id, 277 buffer))); 278 279 for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it) 280 it->first->OnFrameReady(this, frame); 281 } 282 283 void VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread( 284 int buffer_id, 285 const scoped_refptr<ClientBuffer>& buffer) { 286 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 287 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id)); 288 } 289 290 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state) { 291 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 292 293 switch (state) { 294 case VIDEO_CAPTURE_STATE_STARTED: 295 break; 296 case VIDEO_CAPTURE_STATE_STOPPED: 297 state_ = VIDEO_CAPTURE_STATE_STOPPED; 298 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_; 299 client_buffers_.clear(); 300 weak_this_factory_.InvalidateWeakPtrs(); 301 if (!clients_.empty() || !clients_pending_on_restart_.empty()) 302 RestartCapture(); 303 break; 304 case VIDEO_CAPTURE_STATE_PAUSED: 305 for (ClientInfo::iterator it = clients_.begin(); 306 it != clients_.end(); ++it) { 307 it->first->OnPaused(this); 308 } 309 break; 310 case VIDEO_CAPTURE_STATE_ERROR: 311 DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_; 312 for (ClientInfo::iterator it = clients_.begin(); 313 it != clients_.end(); ++it) { 314 // TODO(wjia): browser process would send error code. 315 it->first->OnError(this, 1); 316 it->first->OnRemoved(this); 317 } 318 clients_.clear(); 319 state_ = VIDEO_CAPTURE_STATE_ERROR; 320 break; 321 case VIDEO_CAPTURE_STATE_ENDED: 322 DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_; 323 for (ClientInfo::iterator it = clients_.begin(); 324 it != clients_.end(); ++it) { 325 it->first->OnRemoved(this); 326 } 327 clients_.clear(); 328 state_ = VIDEO_CAPTURE_STATE_ENDED; 329 break; 330 default: 331 break; 332 } 333 } 334 335 void VideoCaptureImpl::DoDelegateAddedOnCaptureThread(int32 device_id) { 336 DVLOG(1) << "DoDelegateAdded: device_id " << device_id; 337 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 338 339 device_id_ = device_id; 340 for (ClientInfo::iterator it = clients_pending_on_filter_.begin(); 341 it != clients_pending_on_filter_.end(); ) { 342 media::VideoCapture::EventHandler* handler = it->first; 343 const media::VideoCaptureParams params = it->second; 344 clients_pending_on_filter_.erase(it++); 345 StartCapture(handler, params); 346 } 347 } 348 349 void VideoCaptureImpl::DoSuspendCaptureOnCaptureThread(bool suspend) { 350 DVLOG(1) << "DoSuspendCapture: suspend " << (suspend ? "yes" : "no"); 351 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 352 353 suspended_ = suspend; 354 } 355 356 void VideoCaptureImpl::StopDevice() { 357 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 358 359 if (state_ == VIDEO_CAPTURE_STATE_STARTED) { 360 state_ = VIDEO_CAPTURE_STATE_STOPPING; 361 Send(new VideoCaptureHostMsg_Stop(device_id_)); 362 params_.requested_format.frame_size.SetSize(0, 0); 363 } 364 } 365 366 void VideoCaptureImpl::RestartCapture() { 367 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 368 DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED); 369 370 int width = 0; 371 int height = 0; 372 for (ClientInfo::iterator it = clients_.begin(); 373 it != clients_.end(); ++it) { 374 width = std::max(width, it->second.requested_format.frame_size.width()); 375 height = std::max(height, it->second.requested_format.frame_size.height()); 376 } 377 for (ClientInfo::iterator it = clients_pending_on_restart_.begin(); 378 it != clients_pending_on_restart_.end(); ) { 379 width = std::max(width, it->second.requested_format.frame_size.width()); 380 height = std::max(height, it->second.requested_format.frame_size.height()); 381 clients_[it->first] = it->second; 382 clients_pending_on_restart_.erase(it++); 383 } 384 params_.requested_format.frame_size.SetSize(width, height); 385 DVLOG(1) << "RestartCapture, " 386 << params_.requested_format.frame_size.ToString(); 387 StartCaptureInternal(); 388 } 389 390 void VideoCaptureImpl::StartCaptureInternal() { 391 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 392 DCHECK(device_id_); 393 394 Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, params_)); 395 state_ = VIDEO_CAPTURE_STATE_STARTED; 396 } 397 398 void VideoCaptureImpl::AddDelegateOnIOThread() { 399 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); 400 message_filter_->AddDelegate(this); 401 } 402 403 void VideoCaptureImpl::RemoveDelegateOnIOThread(base::Closure task) { 404 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); 405 message_filter_->RemoveDelegate(this); 406 capture_message_loop_proxy_->PostTask(FROM_HERE, task); 407 } 408 409 void VideoCaptureImpl::Send(IPC::Message* message) { 410 io_message_loop_proxy_->PostTask(FROM_HERE, 411 base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send), 412 message_filter_.get(), message)); 413 } 414 415 bool VideoCaptureImpl::RemoveClient( 416 media::VideoCapture::EventHandler* handler, 417 ClientInfo* clients) { 418 DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread()); 419 bool found = false; 420 421 ClientInfo::iterator it = clients->find(handler); 422 if (it != clients->end()) { 423 handler->OnStopped(this); 424 handler->OnRemoved(this); 425 clients->erase(it); 426 found = true; 427 } 428 return found; 429 } 430 431 } // namespace content 432