video_capture_controller.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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/browser/renderer_host/media/video_capture_controller.h" 6 7#include <set> 8 9#include "base/bind.h" 10#include "base/debug/trace_event.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/stl_util.h" 13#include "content/browser/renderer_host/media/media_stream_manager.h" 14#include "content/browser/renderer_host/media/video_capture_manager.h" 15#include "content/public/browser/browser_thread.h" 16#include "media/base/video_frame.h" 17#include "media/base/video_util.h" 18#include "media/base/yuv_convert.h" 19 20#if !defined(OS_IOS) && !defined(OS_ANDROID) 21#include "third_party/libyuv/include/libyuv.h" 22#endif 23 24namespace { 25 26#if defined(OS_IOS) || defined(OS_ANDROID) 27// TODO(wjia): Support stride. 28void RotatePackedYV12Frame( 29 const uint8* src, 30 uint8* dest_yplane, 31 uint8* dest_uplane, 32 uint8* dest_vplane, 33 int width, 34 int height, 35 int rotation, 36 bool flip_vert, 37 bool flip_horiz) { 38 media::RotatePlaneByPixels( 39 src, dest_yplane, width, height, rotation, flip_vert, flip_horiz); 40 int y_size = width * height; 41 src += y_size; 42 media::RotatePlaneByPixels( 43 src, dest_uplane, width/2, height/2, rotation, flip_vert, flip_horiz); 44 src += y_size/4; 45 media::RotatePlaneByPixels( 46 src, dest_vplane, width/2, height/2, rotation, flip_vert, flip_horiz); 47} 48#endif // #if defined(OS_IOS) || defined(OS_ANDROID) 49 50} // namespace 51 52namespace content { 53 54// The number of buffers that VideoCaptureBufferPool should allocate. 55static const int kNoOfBuffers = 3; 56 57struct VideoCaptureController::ControllerClient { 58 ControllerClient( 59 const VideoCaptureControllerID& id, 60 VideoCaptureControllerEventHandler* handler, 61 base::ProcessHandle render_process, 62 const media::VideoCaptureParams& params) 63 : controller_id(id), 64 event_handler(handler), 65 render_process_handle(render_process), 66 parameters(params), 67 session_closed(false) { 68 } 69 70 ~ControllerClient() {} 71 72 // ID used for identifying this object. 73 VideoCaptureControllerID controller_id; 74 VideoCaptureControllerEventHandler* event_handler; 75 76 // Handle to the render process that will receive the capture buffers. 77 base::ProcessHandle render_process_handle; 78 media::VideoCaptureParams parameters; 79 80 // Buffers used by this client. 81 std::set<int> buffers; 82 83 // State of capture session, controlled by VideoCaptureManager directly. This 84 // transitions to true as soon as StopSession() occurs, at which point the 85 // client is sent an OnEnded() event. However, because the client retains a 86 // VideoCaptureController* pointer, its ControllerClient entry lives on until 87 // it unregisters itself via RemoveClient(), which may happen asynchronously. 88 // 89 // TODO(nick): If we changed the semantics of VideoCaptureHost so that 90 // OnEnded() events were processed synchronously (with the RemoveClient() done 91 // implicitly), we could avoid tracking this state here in the Controller, and 92 // simplify the code in both places. 93 bool session_closed; 94}; 95 96// Receives events from the VideoCaptureDevice and posts them to a 97// VideoCaptureController on the IO thread. An instance of this class may safely 98// outlive its target VideoCaptureController. 99// 100// Methods of this class may be called from any thread, and in practice will 101// often be called on some auxiliary thread depending on the platform and the 102// device type; including, for example, the DirectShow thread on Windows, the 103// v4l2_thread on Linux, and the UI thread for tab capture. 104class VideoCaptureController::VideoCaptureDeviceClient 105 : public media::VideoCaptureDevice::EventHandler { 106 public: 107 explicit VideoCaptureDeviceClient( 108 const base::WeakPtr<VideoCaptureController>& controller); 109 virtual ~VideoCaptureDeviceClient(); 110 111 // VideoCaptureDevice::EventHandler implementation. 112 virtual scoped_refptr<media::VideoFrame> ReserveOutputBuffer() OVERRIDE; 113 virtual void OnIncomingCapturedFrame(const uint8* data, 114 int length, 115 base::Time timestamp, 116 int rotation, 117 bool flip_vert, 118 bool flip_horiz) OVERRIDE; 119 virtual void OnIncomingCapturedVideoFrame( 120 const scoped_refptr<media::VideoFrame>& frame, 121 base::Time timestamp) OVERRIDE; 122 virtual void OnError() OVERRIDE; 123 virtual void OnFrameInfo( 124 const media::VideoCaptureCapability& info) OVERRIDE; 125 virtual void OnFrameInfoChanged( 126 const media::VideoCaptureCapability& info) OVERRIDE; 127 128 private: 129 // The controller to which we post events. 130 const base::WeakPtr<VideoCaptureController> controller_; 131 132 // The pool of shared-memory buffers used for capturing. 133 scoped_refptr<VideoCaptureBufferPool> buffer_pool_; 134 135 // Chopped pixels in width/height in case video capture device has odd 136 // numbers for width/height. 137 int chopped_width_; 138 int chopped_height_; 139 140 // Tracks the current frame format. 141 media::VideoCaptureCapability frame_info_; 142 143 // For NV21 we have to do color conversion into the intermediate buffer and 144 // from there the rotations. This variable won't be needed after 145 // http://crbug.com/292400 146#if defined(OS_IOS) || defined(OS_ANDROID) 147 scoped_ptr<uint8[]> i420_intermediate_buffer_; 148#endif // #if defined(OS_IOS) || defined(OS_ANDROID) 149}; 150 151VideoCaptureController::VideoCaptureController() 152 : state_(VIDEO_CAPTURE_STATE_STARTED), 153 weak_ptr_factory_(this) { 154 memset(¤t_params_, 0, sizeof(current_params_)); 155} 156 157VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient( 158 const base::WeakPtr<VideoCaptureController>& controller) 159 : controller_(controller), 160 chopped_width_(0), 161 chopped_height_(0) {} 162 163VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {} 164 165base::WeakPtr<VideoCaptureController> VideoCaptureController::GetWeakPtr() { 166 return weak_ptr_factory_.GetWeakPtr(); 167} 168 169scoped_ptr<media::VideoCaptureDevice::EventHandler> 170VideoCaptureController::NewDeviceClient() { 171 scoped_ptr<media::VideoCaptureDevice::EventHandler> result( 172 new VideoCaptureDeviceClient(this->GetWeakPtr())); 173 return result.Pass(); 174} 175 176void VideoCaptureController::AddClient( 177 const VideoCaptureControllerID& id, 178 VideoCaptureControllerEventHandler* event_handler, 179 base::ProcessHandle render_process, 180 const media::VideoCaptureParams& params) { 181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 182 DVLOG(1) << "VideoCaptureController::AddClient, id " << id.device_id 183 << ", (" << params.width 184 << ", " << params.height 185 << ", " << params.frame_rate 186 << ", " << params.session_id 187 << ")"; 188 189 // Signal error in case device is already in error state. 190 if (state_ == VIDEO_CAPTURE_STATE_ERROR) { 191 event_handler->OnError(id); 192 return; 193 } 194 195 // Do nothing if this client has called AddClient before. 196 if (FindClient(id, event_handler, controller_clients_)) 197 return; 198 199 ControllerClient* client = new ControllerClient(id, event_handler, 200 render_process, params); 201 // If we already have gotten frame_info from the device, repeat it to the new 202 // client. 203 if (state_ == VIDEO_CAPTURE_STATE_STARTED) { 204 if (frame_info_.IsValid()) { 205 SendFrameInfoAndBuffers(client); 206 } 207 controller_clients_.push_back(client); 208 return; 209 } 210} 211 212int VideoCaptureController::RemoveClient( 213 const VideoCaptureControllerID& id, 214 VideoCaptureControllerEventHandler* event_handler) { 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 216 DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id.device_id; 217 218 ControllerClient* client = FindClient(id, event_handler, controller_clients_); 219 if (!client) 220 return kInvalidMediaCaptureSessionId; 221 222 // Take back all buffers held by the |client|. 223 if (buffer_pool_.get()) { 224 for (std::set<int>::iterator buffer_it = client->buffers.begin(); 225 buffer_it != client->buffers.end(); 226 ++buffer_it) { 227 int buffer_id = *buffer_it; 228 buffer_pool_->RelinquishConsumerHold(buffer_id, 1); 229 } 230 } 231 client->buffers.clear(); 232 233 int session_id = client->parameters.session_id; 234 controller_clients_.remove(client); 235 delete client; 236 237 return session_id; 238} 239 240void VideoCaptureController::StopSession(int session_id) { 241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 242 DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id; 243 244 ControllerClient* client = FindClient(session_id, controller_clients_); 245 246 if (client) { 247 client->session_closed = true; 248 client->event_handler->OnEnded(client->controller_id); 249 } 250} 251 252void VideoCaptureController::ReturnBuffer( 253 const VideoCaptureControllerID& id, 254 VideoCaptureControllerEventHandler* event_handler, 255 int buffer_id) { 256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 257 258 ControllerClient* client = FindClient(id, event_handler, controller_clients_); 259 260 // If this buffer is not held by this client, or this client doesn't exist 261 // in controller, do nothing. 262 if (!client || 263 client->buffers.find(buffer_id) == client->buffers.end()) { 264 NOTREACHED(); 265 return; 266 } 267 268 client->buffers.erase(buffer_id); 269 buffer_pool_->RelinquishConsumerHold(buffer_id, 1); 270} 271 272scoped_refptr<media::VideoFrame> 273VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer() { 274 return buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, 275 frame_info_.height), 276 0); 277} 278 279#if !defined(OS_IOS) && !defined(OS_ANDROID) 280void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( 281 const uint8* data, 282 int length, 283 base::Time timestamp, 284 int rotation, 285 bool flip_vert, 286 bool flip_horiz) { 287 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); 288 289 if (!buffer_pool_.get()) 290 return; 291 scoped_refptr<media::VideoFrame> dst = buffer_pool_->ReserveI420VideoFrame( 292 gfx::Size(frame_info_.width, frame_info_.height), rotation); 293 294 if (!dst.get()) 295 return; 296 297 uint8* yplane = dst->data(media::VideoFrame::kYPlane); 298 uint8* uplane = dst->data(media::VideoFrame::kUPlane); 299 uint8* vplane = dst->data(media::VideoFrame::kVPlane); 300 int yplane_stride = frame_info_.width; 301 int uv_plane_stride = (frame_info_.width + 1) / 2; 302 int crop_x = 0; 303 int crop_y = 0; 304 libyuv::FourCC origin_colorspace = libyuv::FOURCC_ANY; 305 // Assuming rotation happens first and flips next, we can consolidate both 306 // vertical and horizontal flips together with rotation into two variables: 307 // new_rotation = (rotation + 180 * vertical_flip) modulo 360 308 // new_vertical_flip = horizontal_flip XOR vertical_flip 309 int new_rotation_angle = (rotation + 180 * flip_vert) % 360; 310 libyuv::RotationMode rotation_mode = libyuv::kRotate0; 311 if (new_rotation_angle == 90) 312 rotation_mode = libyuv::kRotate90; 313 else if (new_rotation_angle == 180) 314 rotation_mode = libyuv::kRotate180; 315 else if (new_rotation_angle == 270) 316 rotation_mode = libyuv::kRotate270; 317 318 switch (frame_info_.color) { 319 case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. 320 break; 321 case media::PIXEL_FORMAT_I420: 322 DCHECK(!chopped_width_ && !chopped_height_); 323 origin_colorspace = libyuv::FOURCC_I420; 324 break; 325 case media::PIXEL_FORMAT_YV12: 326 DCHECK(!chopped_width_ && !chopped_height_); 327 origin_colorspace = libyuv::FOURCC_YV12; 328 break; 329 case media::PIXEL_FORMAT_NV21: 330 DCHECK(!chopped_width_ && !chopped_height_); 331 origin_colorspace = libyuv::FOURCC_NV12; 332 break; 333 case media::PIXEL_FORMAT_YUY2: 334 DCHECK(!chopped_width_ && !chopped_height_); 335 origin_colorspace = libyuv::FOURCC_YUY2; 336 break; 337 case media::PIXEL_FORMAT_UYVY: 338 DCHECK(!chopped_width_ && !chopped_height_); 339 origin_colorspace = libyuv::FOURCC_UYVY; 340 break; 341 case media::PIXEL_FORMAT_RGB24: 342 origin_colorspace = libyuv::FOURCC_RAW; 343 break; 344 case media::PIXEL_FORMAT_ARGB: 345 origin_colorspace = libyuv::FOURCC_ARGB; 346 break; 347 case media::PIXEL_FORMAT_MJPEG: 348 origin_colorspace = libyuv::FOURCC_MJPG; 349 break; 350 default: 351 NOTREACHED(); 352 } 353 354 int need_convert_rgb24_on_win = false; 355#if defined(OS_WIN) 356 // kRGB24 on Windows start at the bottom line and has a negative stride. This 357 // is not supported by libyuv, so the media API is used instead. 358 if (frame_info_.color == media::PIXEL_FORMAT_RGB24) { 359 // Rotation and flipping is not supported in kRGB24 and OS_WIN case. 360 DCHECK(!rotation && !flip_vert && !flip_horiz); 361 need_convert_rgb24_on_win = true; 362 } 363#endif 364 if (need_convert_rgb24_on_win) { 365 int rgb_stride = -3 * (frame_info_.width + chopped_width_); 366 const uint8* rgb_src = 367 data + 3 * (frame_info_.width + chopped_width_) * 368 (frame_info_.height - 1 + chopped_height_); 369 media::ConvertRGB24ToYUV(rgb_src, 370 yplane, 371 uplane, 372 vplane, 373 frame_info_.width, 374 frame_info_.height, 375 rgb_stride, 376 yplane_stride, 377 uv_plane_stride); 378 } else { 379 libyuv::ConvertToI420( 380 data, 381 length, 382 yplane, 383 yplane_stride, 384 uplane, 385 uv_plane_stride, 386 vplane, 387 uv_plane_stride, 388 crop_x, 389 crop_y, 390 frame_info_.width + chopped_width_, 391 frame_info_.height * (flip_vert ^ flip_horiz ? -1 : 1), 392 frame_info_.width, 393 frame_info_.height, 394 rotation_mode, 395 origin_colorspace); 396 } 397 BrowserThread::PostTask( 398 BrowserThread::IO, 399 FROM_HERE, 400 base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, 401 controller_, 402 dst, 403 timestamp)); 404} 405#else 406void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedFrame( 407 const uint8* data, 408 int length, 409 base::Time timestamp, 410 int rotation, 411 bool flip_vert, 412 bool flip_horiz) { 413 DCHECK(frame_info_.color == media::PIXEL_FORMAT_I420 || 414 frame_info_.color == media::PIXEL_FORMAT_YV12 || 415 frame_info_.color == media::PIXEL_FORMAT_NV21 || 416 (rotation == 0 && !flip_vert && !flip_horiz)); 417 418 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedFrame"); 419 420 if (!buffer_pool_) 421 return; 422 scoped_refptr<media::VideoFrame> dst = 423 buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, 424 frame_info_.height), 425 rotation); 426 427 if (!dst.get()) 428 return; 429 430 uint8* yplane = dst->data(media::VideoFrame::kYPlane); 431 uint8* uplane = dst->data(media::VideoFrame::kUPlane); 432 uint8* vplane = dst->data(media::VideoFrame::kVPlane); 433 434 // Do color conversion from the camera format to I420. 435 switch (frame_info_.color) { 436 case media::PIXEL_FORMAT_UNKNOWN: // Color format not set. 437 break; 438 case media::PIXEL_FORMAT_I420: 439 DCHECK(!chopped_width_ && !chopped_height_); 440 RotatePackedYV12Frame( 441 data, yplane, uplane, vplane, frame_info_.width, frame_info_.height, 442 rotation, flip_vert, flip_horiz); 443 break; 444 case media::PIXEL_FORMAT_YV12: 445 DCHECK(!chopped_width_ && !chopped_height_); 446 RotatePackedYV12Frame( 447 data, yplane, vplane, uplane, frame_info_.width, frame_info_.height, 448 rotation, flip_vert, flip_horiz); 449 break; 450 case media::PIXEL_FORMAT_NV21: { 451 DCHECK(!chopped_width_ && !chopped_height_); 452 int num_pixels = frame_info_.width * frame_info_.height; 453 media::ConvertNV21ToYUV(data, 454 &i420_intermediate_buffer_[0], 455 &i420_intermediate_buffer_[num_pixels], 456 &i420_intermediate_buffer_[num_pixels * 5 / 4], 457 frame_info_.width, 458 frame_info_.height); 459 RotatePackedYV12Frame( 460 i420_intermediate_buffer_.get(), yplane, uplane, vplane, 461 frame_info_.width, frame_info_.height, 462 rotation, flip_vert, flip_horiz); 463 break; 464 } 465 case media::PIXEL_FORMAT_YUY2: 466 DCHECK(!chopped_width_ && !chopped_height_); 467 if (frame_info_.width * frame_info_.height * 2 != length) { 468 // If |length| of |data| does not match the expected width and height 469 // we can't convert the frame to I420. YUY2 is 2 bytes per pixel. 470 break; 471 } 472 473 media::ConvertYUY2ToYUV(data, yplane, uplane, vplane, frame_info_.width, 474 frame_info_.height); 475 break; 476 case media::PIXEL_FORMAT_RGB24: { 477 int ystride = frame_info_.width; 478 int uvstride = frame_info_.width / 2; 479 int rgb_stride = 3 * (frame_info_.width + chopped_width_); 480 const uint8* rgb_src = data; 481 media::ConvertRGB24ToYUV(rgb_src, yplane, uplane, vplane, 482 frame_info_.width, frame_info_.height, 483 rgb_stride, ystride, uvstride); 484 break; 485 } 486 case media::PIXEL_FORMAT_ARGB: 487 media::ConvertRGB32ToYUV(data, yplane, uplane, vplane, frame_info_.width, 488 frame_info_.height, 489 (frame_info_.width + chopped_width_) * 4, 490 frame_info_.width, frame_info_.width / 2); 491 break; 492 default: 493 NOTREACHED(); 494 } 495 496 BrowserThread::PostTask(BrowserThread::IO, 497 FROM_HERE, 498 base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, 499 controller_, dst, timestamp)); 500} 501#endif // #if !defined(OS_IOS) && !defined(OS_ANDROID) 502 503void 504VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame( 505 const scoped_refptr<media::VideoFrame>& frame, 506 base::Time timestamp) { 507 if (!buffer_pool_) 508 return; 509 510 // If this is a frame that belongs to the buffer pool, we can forward it 511 // directly to the IO thread and be done. 512 if (buffer_pool_->RecognizeReservedBuffer( 513 frame->shared_memory_handle()) >= 0) { 514 BrowserThread::PostTask(BrowserThread::IO, 515 FROM_HERE, 516 base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, 517 controller_, frame, timestamp)); 518 return; 519 } 520 521 // Otherwise, this is a frame that belongs to the caller, and we must copy 522 // it to a frame from the buffer pool. 523 scoped_refptr<media::VideoFrame> target = 524 buffer_pool_->ReserveI420VideoFrame(gfx::Size(frame_info_.width, 525 frame_info_.height), 526 0); 527 528 if (!target.get()) 529 return; 530 531 // Validate the inputs. 532 if (frame->coded_size() != target->coded_size()) 533 return; // Only exact copies are supported. 534 if (!(frame->format() == media::VideoFrame::I420 || 535 frame->format() == media::VideoFrame::YV12 || 536 frame->format() == media::VideoFrame::RGB32)) { 537 NOTREACHED() << "Unsupported format passed to OnIncomingCapturedVideoFrame"; 538 return; 539 } 540 541 const int kYPlane = media::VideoFrame::kYPlane; 542 const int kUPlane = media::VideoFrame::kUPlane; 543 const int kVPlane = media::VideoFrame::kVPlane; 544 const int kAPlane = media::VideoFrame::kAPlane; 545 const int kRGBPlane = media::VideoFrame::kRGBPlane; 546 547 // Do color conversion from the camera format to I420. 548 switch (frame->format()) { 549#if defined(GOOGLE_TV) 550 case media::VideoFrame::HOLE: 551 // Fall-through to NOTREACHED() block. 552#endif 553 case media::VideoFrame::INVALID: 554 case media::VideoFrame::YV16: 555 case media::VideoFrame::EMPTY: 556 case media::VideoFrame::NATIVE_TEXTURE: { 557 NOTREACHED(); 558 break; 559 } 560 case media::VideoFrame::I420: 561 case media::VideoFrame::YV12: { 562 DCHECK(!chopped_width_ && !chopped_height_); 563 media::CopyYPlane(frame->data(kYPlane), 564 frame->stride(kYPlane), 565 frame->rows(kYPlane), 566 target.get()); 567 media::CopyUPlane(frame->data(kUPlane), 568 frame->stride(kUPlane), 569 frame->rows(kUPlane), 570 target.get()); 571 media::CopyVPlane(frame->data(kVPlane), 572 frame->stride(kVPlane), 573 frame->rows(kVPlane), 574 target.get()); 575 break; 576 } 577 case media::VideoFrame::YV12A: { 578 DCHECK(!chopped_width_ && !chopped_height_); 579 media::CopyYPlane(frame->data(kYPlane), 580 frame->stride(kYPlane), 581 frame->rows(kYPlane), 582 target.get()); 583 media::CopyUPlane(frame->data(kUPlane), 584 frame->stride(kUPlane), 585 frame->rows(kUPlane), 586 target.get()); 587 media::CopyVPlane(frame->data(kVPlane), 588 frame->stride(kVPlane), 589 frame->rows(kVPlane), 590 target.get()); 591 media::CopyAPlane(frame->data(kAPlane), 592 frame->stride(kAPlane), 593 frame->rows(kAPlane), 594 target.get()); 595 break; 596 } 597 case media::VideoFrame::RGB32: { 598 media::ConvertRGB32ToYUV(frame->data(kRGBPlane), 599 target->data(kYPlane), 600 target->data(kUPlane), 601 target->data(kVPlane), 602 target->coded_size().width(), 603 target->coded_size().height(), 604 frame->stride(kRGBPlane), 605 target->stride(kYPlane), 606 target->stride(kUPlane)); 607 break; 608 } 609 } 610 611 BrowserThread::PostTask(BrowserThread::IO, 612 FROM_HERE, 613 base::Bind(&VideoCaptureController::DoIncomingCapturedFrameOnIOThread, 614 controller_, target, timestamp)); 615} 616 617void VideoCaptureController::VideoCaptureDeviceClient::OnError() { 618 BrowserThread::PostTask(BrowserThread::IO, 619 FROM_HERE, 620 base::Bind(&VideoCaptureController::DoErrorOnIOThread, controller_)); 621} 622 623void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfo( 624 const media::VideoCaptureCapability& info) { 625 frame_info_ = info; 626 // Handle cases when |info| has odd numbers for width/height. 627 if (info.width & 1) { 628 --frame_info_.width; 629 chopped_width_ = 1; 630 } else { 631 chopped_width_ = 0; 632 } 633 if (info.height & 1) { 634 --frame_info_.height; 635 chopped_height_ = 1; 636 } else { 637 chopped_height_ = 0; 638 } 639#if defined(OS_IOS) || defined(OS_ANDROID) 640 if (frame_info_.color == media::PIXEL_FORMAT_NV21 && 641 !i420_intermediate_buffer_) { 642 i420_intermediate_buffer_.reset( 643 new uint8[frame_info_.width * frame_info_.height * 12 / 8]); 644 } 645#endif // #if defined(OS_IOS) || defined(OS_ANDROID) 646 647 DCHECK(!buffer_pool_.get()); 648 649 // TODO(nick): Give BufferPool the same lifetime as the controller, have it 650 // support frame size changes, and stop checking it for NULL everywhere. 651 // http://crbug.com/266082 652 buffer_pool_ = new VideoCaptureBufferPool( 653 media::VideoFrame::AllocationSize( 654 media::VideoFrame::I420, 655 gfx::Size(frame_info_.width, frame_info_.height)), 656 kNoOfBuffers); 657 658 // Check whether all buffers were created successfully. 659 if (!buffer_pool_->Allocate()) { 660 // Transition to the error state. 661 buffer_pool_ = NULL; 662 OnError(); 663 return; 664 } 665 666 BrowserThread::PostTask(BrowserThread::IO, 667 FROM_HERE, 668 base::Bind(&VideoCaptureController::DoFrameInfoOnIOThread, controller_, 669 frame_info_, buffer_pool_)); 670} 671 672void VideoCaptureController::VideoCaptureDeviceClient::OnFrameInfoChanged( 673 const media::VideoCaptureCapability& info) { 674 BrowserThread::PostTask(BrowserThread::IO, 675 FROM_HERE, 676 base::Bind(&VideoCaptureController::DoFrameInfoChangedOnIOThread, 677 controller_, info)); 678} 679 680VideoCaptureController::~VideoCaptureController() { 681 buffer_pool_ = NULL; // Release all buffers. 682 STLDeleteContainerPointers(controller_clients_.begin(), 683 controller_clients_.end()); 684} 685 686void VideoCaptureController::DoIncomingCapturedFrameOnIOThread( 687 const scoped_refptr<media::VideoFrame>& reserved_frame, 688 base::Time timestamp) { 689 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 690 691 if (!buffer_pool_.get()) 692 return; 693 694 int buffer_id = buffer_pool_->RecognizeReservedBuffer( 695 reserved_frame->shared_memory_handle()); 696 if (buffer_id < 0) { 697 NOTREACHED(); 698 return; 699 } 700 701 int count = 0; 702 if (state_ == VIDEO_CAPTURE_STATE_STARTED) { 703 for (ControllerClients::iterator client_it = controller_clients_.begin(); 704 client_it != controller_clients_.end(); ++client_it) { 705 if ((*client_it)->session_closed) 706 continue; 707 708 (*client_it)->event_handler->OnBufferReady((*client_it)->controller_id, 709 buffer_id, timestamp); 710 (*client_it)->buffers.insert(buffer_id); 711 count++; 712 } 713 } 714 715 buffer_pool_->HoldForConsumers(buffer_id, count); 716} 717 718void VideoCaptureController::DoFrameInfoOnIOThread( 719 const media::VideoCaptureCapability& frame_info, 720 const scoped_refptr<VideoCaptureBufferPool>& buffer_pool) { 721 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 722 DCHECK(!buffer_pool_.get()) << "Frame info should happen only once."; 723 724 // Allocate memory only when device has been started. 725 if (state_ != VIDEO_CAPTURE_STATE_STARTED) 726 return; 727 728 frame_info_ = frame_info; 729 buffer_pool_ = buffer_pool; 730 731 for (ControllerClients::iterator client_it = controller_clients_.begin(); 732 client_it != controller_clients_.end(); ++client_it) { 733 if ((*client_it)->session_closed) 734 continue; 735 736 SendFrameInfoAndBuffers(*client_it); 737 } 738} 739 740void VideoCaptureController::DoFrameInfoChangedOnIOThread( 741 const media::VideoCaptureCapability& info) { 742 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 743 // TODO(mcasas): Here we should reallocate the VideoCaptureBufferPool, if 744 // needed, to support the new video capture format. See crbug.com/266082. 745 for (ControllerClients::iterator client_it = controller_clients_.begin(); 746 client_it != controller_clients_.end(); ++client_it) { 747 if ((*client_it)->session_closed) 748 continue; 749 750 (*client_it)->event_handler->OnFrameInfoChanged( 751 (*client_it)->controller_id, 752 info.width, 753 info.height, 754 info.frame_rate); 755 } 756} 757 758void VideoCaptureController::DoErrorOnIOThread() { 759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 760 state_ = VIDEO_CAPTURE_STATE_ERROR; 761 ControllerClients::iterator client_it; 762 for (client_it = controller_clients_.begin(); 763 client_it != controller_clients_.end(); ++client_it) { 764 if ((*client_it)->session_closed) 765 continue; 766 767 (*client_it)->event_handler->OnError((*client_it)->controller_id); 768 } 769} 770 771void VideoCaptureController::SendFrameInfoAndBuffers(ControllerClient* client) { 772 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 773 DCHECK(frame_info_.IsValid()); 774 client->event_handler->OnFrameInfo(client->controller_id, 775 frame_info_); 776 for (int buffer_id = 0; buffer_id < buffer_pool_->count(); ++buffer_id) { 777 base::SharedMemoryHandle remote_handle = 778 buffer_pool_->ShareToProcess(buffer_id, client->render_process_handle); 779 780 client->event_handler->OnBufferCreated(client->controller_id, 781 remote_handle, 782 buffer_pool_->GetMemorySize(), 783 buffer_id); 784 } 785} 786 787VideoCaptureController::ControllerClient* 788VideoCaptureController::FindClient( 789 const VideoCaptureControllerID& id, 790 VideoCaptureControllerEventHandler* handler, 791 const ControllerClients& clients) { 792 for (ControllerClients::const_iterator client_it = clients.begin(); 793 client_it != clients.end(); ++client_it) { 794 if ((*client_it)->controller_id == id && 795 (*client_it)->event_handler == handler) { 796 return *client_it; 797 } 798 } 799 return NULL; 800} 801 802VideoCaptureController::ControllerClient* 803VideoCaptureController::FindClient( 804 int session_id, 805 const ControllerClients& clients) { 806 for (ControllerClients::const_iterator client_it = clients.begin(); 807 client_it != clients.end(); ++client_it) { 808 if ((*client_it)->parameters.session_id == session_id) { 809 return *client_it; 810 } 811 } 812 return NULL; 813} 814 815int VideoCaptureController::GetClientCount() { 816 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 817 return controller_clients_.size(); 818} 819 820} // namespace content 821