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.
5 #include "content/browser/renderer_host/media/video_capture_controller.h"
10 #include "base/bind.h"
11 #include "base/debug/trace_event.h"
12 #include "base/metrics/histogram.h"
13 #include "base/metrics/sparse_histogram.h"
14 #include "base/stl_util.h"
15 #include "content/browser/renderer_host/media/media_stream_manager.h"
16 #include "content/browser/renderer_host/media/video_capture_manager.h"
17 #include "content/common/gpu/client/gl_helper.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "gpu/command_buffer/common/mailbox_holder.h"
20 #include "media/base/video_frame.h"
21 #include "media/base/video_util.h"
22 #include "media/base/yuv_convert.h"
23 #include "third_party/libyuv/include/libyuv.h"
25 #if defined(OS_ANDROID)
26 #include "content/browser/renderer_host/image_transport_factory_android.h"
28 #include "content/browser/compositor/image_transport_factory.h"
31 using media::VideoCaptureFormat
;
37 static const int kInfiniteRatio
= 99999;
39 #define UMA_HISTOGRAM_ASPECT_RATIO(name, width, height) \
40 UMA_HISTOGRAM_SPARSE_SLOWLY( \
42 (height) ? ((width) * 100) / (height) : kInfiniteRatio);
44 class PoolBuffer
: public media::VideoCaptureDevice::Client::Buffer
{
46 PoolBuffer(const scoped_refptr
<VideoCaptureBufferPool
>& pool
,
50 : Buffer(buffer_id
, data
, size
), pool_(pool
) {
55 virtual ~PoolBuffer() { pool_
->RelinquishProducerReservation(id()); }
57 const scoped_refptr
<VideoCaptureBufferPool
> pool_
;
60 class SyncPointClientImpl
: public media::VideoFrame::SyncPointClient
{
62 explicit SyncPointClientImpl(GLHelper
* gl_helper
) : gl_helper_(gl_helper
) {}
63 virtual ~SyncPointClientImpl() {}
64 virtual uint32
InsertSyncPoint() OVERRIDE
{
65 return gl_helper_
->InsertSyncPoint();
67 virtual void WaitSyncPoint(uint32 sync_point
) OVERRIDE
{
68 gl_helper_
->WaitSyncPoint(sync_point
);
75 void ReturnVideoFrame(const scoped_refptr
<media::VideoFrame
>& video_frame
,
77 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
78 #if defined(OS_ANDROID)
80 ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
82 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
85 // UpdateReleaseSyncPoint() creates a new sync_point using |gl_helper|, so
86 // wait the given |sync_point| using |gl_helper|.
87 gl_helper
->WaitSyncPoint(sync_point
);
88 SyncPointClientImpl
client(gl_helper
);
89 video_frame
->UpdateReleaseSyncPoint(&client
);
92 } // anonymous namespace
94 struct VideoCaptureController::ControllerClient
{
95 ControllerClient(const VideoCaptureControllerID
& id
,
96 VideoCaptureControllerEventHandler
* handler
,
97 base::ProcessHandle render_process
,
98 media::VideoCaptureSessionId session_id
,
99 const media::VideoCaptureParams
& params
)
101 event_handler(handler
),
102 render_process_handle(render_process
),
103 session_id(session_id
),
105 session_closed(false) {}
107 ~ControllerClient() {}
109 // ID used for identifying this object.
110 const VideoCaptureControllerID controller_id
;
111 VideoCaptureControllerEventHandler
* const event_handler
;
113 // Handle to the render process that will receive the capture buffers.
114 const base::ProcessHandle render_process_handle
;
115 const media::VideoCaptureSessionId session_id
;
116 const media::VideoCaptureParams parameters
;
118 // Buffers that are currently known to this client.
119 std::set
<int> known_buffers
;
121 // Buffers currently held by this client, and syncpoint callback to call when
122 // they are returned from the client.
123 typedef std::map
<int, scoped_refptr
<media::VideoFrame
> > ActiveBufferMap
;
124 ActiveBufferMap active_buffers
;
126 // State of capture session, controlled by VideoCaptureManager directly. This
127 // transitions to true as soon as StopSession() occurs, at which point the
128 // client is sent an OnEnded() event. However, because the client retains a
129 // VideoCaptureController* pointer, its ControllerClient entry lives on until
130 // it unregisters itself via RemoveClient(), which may happen asynchronously.
132 // TODO(nick): If we changed the semantics of VideoCaptureHost so that
133 // OnEnded() events were processed synchronously (with the RemoveClient() done
134 // implicitly), we could avoid tracking this state here in the Controller, and
135 // simplify the code in both places.
139 // Receives events from the VideoCaptureDevice and posts them to a
140 // VideoCaptureController on the IO thread. An instance of this class may safely
141 // outlive its target VideoCaptureController.
143 // Methods of this class may be called from any thread, and in practice will
144 // often be called on some auxiliary thread depending on the platform and the
145 // device type; including, for example, the DirectShow thread on Windows, the
146 // v4l2_thread on Linux, and the UI thread for tab capture.
147 class VideoCaptureController::VideoCaptureDeviceClient
148 : public media::VideoCaptureDevice::Client
{
150 explicit VideoCaptureDeviceClient(
151 const base::WeakPtr
<VideoCaptureController
>& controller
,
152 const scoped_refptr
<VideoCaptureBufferPool
>& buffer_pool
);
153 virtual ~VideoCaptureDeviceClient();
155 // VideoCaptureDevice::Client implementation.
156 virtual scoped_refptr
<Buffer
> ReserveOutputBuffer(
157 media::VideoFrame::Format format
,
158 const gfx::Size
& size
) OVERRIDE
;
159 virtual void OnIncomingCapturedData(const uint8
* data
,
161 const VideoCaptureFormat
& frame_format
,
163 base::TimeTicks timestamp
) OVERRIDE
;
164 virtual void OnIncomingCapturedVideoFrame(
165 const scoped_refptr
<Buffer
>& buffer
,
166 const VideoCaptureFormat
& buffer_format
,
167 const scoped_refptr
<media::VideoFrame
>& frame
,
168 base::TimeTicks timestamp
) OVERRIDE
;
169 virtual void OnError(const std::string
& reason
) OVERRIDE
;
170 virtual void OnLog(const std::string
& message
) OVERRIDE
;
173 scoped_refptr
<Buffer
> DoReserveOutputBuffer(media::VideoFrame::Format format
,
174 const gfx::Size
& dimensions
);
176 // The controller to which we post events.
177 const base::WeakPtr
<VideoCaptureController
> controller_
;
179 // The pool of shared-memory buffers used for capturing.
180 const scoped_refptr
<VideoCaptureBufferPool
> buffer_pool_
;
185 VideoCaptureController::VideoCaptureController(int max_buffers
)
186 : buffer_pool_(new VideoCaptureBufferPool(max_buffers
)),
187 state_(VIDEO_CAPTURE_STATE_STARTED
),
188 weak_ptr_factory_(this) {
191 VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient(
192 const base::WeakPtr
<VideoCaptureController
>& controller
,
193 const scoped_refptr
<VideoCaptureBufferPool
>& buffer_pool
)
194 : controller_(controller
), buffer_pool_(buffer_pool
), first_frame_(true) {}
196 VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
198 base::WeakPtr
<VideoCaptureController
> VideoCaptureController::GetWeakPtr() {
199 return weak_ptr_factory_
.GetWeakPtr();
202 scoped_ptr
<media::VideoCaptureDevice::Client
>
203 VideoCaptureController::NewDeviceClient() {
204 scoped_ptr
<media::VideoCaptureDevice::Client
> result(
205 new VideoCaptureDeviceClient(this->GetWeakPtr(), buffer_pool_
));
206 return result
.Pass();
209 void VideoCaptureController::AddClient(
210 const VideoCaptureControllerID
& id
,
211 VideoCaptureControllerEventHandler
* event_handler
,
212 base::ProcessHandle render_process
,
213 media::VideoCaptureSessionId session_id
,
214 const media::VideoCaptureParams
& params
) {
215 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
216 DVLOG(1) << "VideoCaptureController::AddClient, id " << id
.device_id
217 << ", " << params
.requested_format
.frame_size
.ToString()
218 << ", " << params
.requested_format
.frame_rate
219 << ", " << session_id
222 // If this is the first client added to the controller, cache the parameters.
223 if (!controller_clients_
.size())
224 video_capture_format_
= params
.requested_format
;
226 // Signal error in case device is already in error state.
227 if (state_
== VIDEO_CAPTURE_STATE_ERROR
) {
228 event_handler
->OnError(id
);
232 // Do nothing if this client has called AddClient before.
233 if (FindClient(id
, event_handler
, controller_clients_
))
236 ControllerClient
* client
= new ControllerClient(
237 id
, event_handler
, render_process
, session_id
, params
);
238 // If we already have gotten frame_info from the device, repeat it to the new
240 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
241 controller_clients_
.push_back(client
);
246 int VideoCaptureController::RemoveClient(
247 const VideoCaptureControllerID
& id
,
248 VideoCaptureControllerEventHandler
* event_handler
) {
249 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
250 DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id
.device_id
;
252 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
254 return kInvalidMediaCaptureSessionId
;
256 // Take back all buffers held by the |client|.
257 for (ControllerClient::ActiveBufferMap::iterator buffer_it
=
258 client
->active_buffers
.begin();
259 buffer_it
!= client
->active_buffers
.end();
261 buffer_pool_
->RelinquishConsumerHold(buffer_it
->first
, 1);
263 client
->active_buffers
.clear();
265 int session_id
= client
->session_id
;
266 controller_clients_
.remove(client
);
272 void VideoCaptureController::StopSession(int session_id
) {
273 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
274 DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id
;
276 ControllerClient
* client
= FindClient(session_id
, controller_clients_
);
279 client
->session_closed
= true;
280 client
->event_handler
->OnEnded(client
->controller_id
);
284 void VideoCaptureController::ReturnBuffer(
285 const VideoCaptureControllerID
& id
,
286 VideoCaptureControllerEventHandler
* event_handler
,
289 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
291 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
293 // If this buffer is not held by this client, or this client doesn't exist
294 // in controller, do nothing.
295 ControllerClient::ActiveBufferMap::iterator iter
;
296 if (!client
|| (iter
= client
->active_buffers
.find(buffer_id
)) ==
297 client
->active_buffers
.end()) {
301 scoped_refptr
<media::VideoFrame
> frame
= iter
->second
;
302 client
->active_buffers
.erase(iter
);
303 buffer_pool_
->RelinquishConsumerHold(buffer_id
, 1);
306 BrowserThread::PostTask(BrowserThread::UI
,
308 base::Bind(&ReturnVideoFrame
, frame
, sync_point
));
311 const media::VideoCaptureFormat
&
312 VideoCaptureController::GetVideoCaptureFormat() const {
313 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
314 return video_capture_format_
;
317 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>
318 VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer(
319 media::VideoFrame::Format format
,
320 const gfx::Size
& size
) {
321 return DoReserveOutputBuffer(format
, size
);
324 void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedData(
327 const VideoCaptureFormat
& frame_format
,
329 base::TimeTicks timestamp
) {
330 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData");
332 if (!frame_format
.IsValid())
335 // Chopped pixels in width/height in case video capture device has odd
336 // numbers for width/height.
337 int chopped_width
= 0;
338 int chopped_height
= 0;
339 int new_unrotated_width
= frame_format
.frame_size
.width();
340 int new_unrotated_height
= frame_format
.frame_size
.height();
342 if (new_unrotated_width
& 1) {
343 --new_unrotated_width
;
346 if (new_unrotated_height
& 1) {
347 --new_unrotated_height
;
351 int destination_width
= new_unrotated_width
;
352 int destination_height
= new_unrotated_height
;
353 if (rotation
== 90 || rotation
== 270) {
354 destination_width
= new_unrotated_height
;
355 destination_height
= new_unrotated_width
;
357 const gfx::Size
dimensions(destination_width
, destination_height
);
358 if (!media::VideoFrame::IsValidConfig(media::VideoFrame::I420
,
360 gfx::Rect(dimensions
),
365 scoped_refptr
<Buffer
> buffer
=
366 DoReserveOutputBuffer(media::VideoFrame::I420
, dimensions
);
370 uint8
* yplane
= NULL
;
372 yplane
= reinterpret_cast<uint8
*>(buffer
->data());
375 media::VideoFrame::PlaneAllocationSize(
376 media::VideoFrame::I420
, media::VideoFrame::kYPlane
, dimensions
);
379 media::VideoFrame::PlaneAllocationSize(
380 media::VideoFrame::I420
, media::VideoFrame::kUPlane
, dimensions
);
381 int yplane_stride
= dimensions
.width();
382 int uv_plane_stride
= yplane_stride
/ 2;
385 libyuv::FourCC origin_colorspace
= libyuv::FOURCC_ANY
;
387 libyuv::RotationMode rotation_mode
= libyuv::kRotate0
;
389 rotation_mode
= libyuv::kRotate90
;
390 else if (rotation
== 180)
391 rotation_mode
= libyuv::kRotate180
;
392 else if (rotation
== 270)
393 rotation_mode
= libyuv::kRotate270
;
395 switch (frame_format
.pixel_format
) {
396 case media::PIXEL_FORMAT_UNKNOWN
: // Color format not set.
398 case media::PIXEL_FORMAT_I420
:
399 DCHECK(!chopped_width
&& !chopped_height
);
400 origin_colorspace
= libyuv::FOURCC_I420
;
402 case media::PIXEL_FORMAT_YV12
:
403 DCHECK(!chopped_width
&& !chopped_height
);
404 origin_colorspace
= libyuv::FOURCC_YV12
;
406 case media::PIXEL_FORMAT_NV21
:
407 DCHECK(!chopped_width
&& !chopped_height
);
408 origin_colorspace
= libyuv::FOURCC_NV21
;
410 case media::PIXEL_FORMAT_YUY2
:
411 DCHECK(!chopped_width
&& !chopped_height
);
412 origin_colorspace
= libyuv::FOURCC_YUY2
;
414 case media::PIXEL_FORMAT_UYVY
:
415 DCHECK(!chopped_width
&& !chopped_height
);
416 origin_colorspace
= libyuv::FOURCC_UYVY
;
418 case media::PIXEL_FORMAT_RGB24
:
419 origin_colorspace
= libyuv::FOURCC_24BG
;
421 // TODO(wjia): Currently, for RGB24 on WIN, capture device always
422 // passes in positive src_width and src_height. Remove this hardcoded
423 // value when nagative src_height is supported. The negative src_height
424 // indicates that vertical flipping is needed.
428 case media::PIXEL_FORMAT_ARGB
:
429 origin_colorspace
= libyuv::FOURCC_ARGB
;
431 case media::PIXEL_FORMAT_MJPEG
:
432 origin_colorspace
= libyuv::FOURCC_MJPG
;
438 libyuv::ConvertToI420(data
,
448 frame_format
.frame_size
.width(),
449 (flip
? -frame_format
.frame_size
.height() :
450 frame_format
.frame_size
.height()),
452 new_unrotated_height
,
455 scoped_refptr
<media::VideoFrame
> frame
=
456 media::VideoFrame::WrapExternalPackedMemory(
457 media::VideoFrame::I420
,
459 gfx::Rect(dimensions
),
462 media::VideoFrame::AllocationSize(media::VideoFrame::I420
,
464 base::SharedMemory::NULLHandle(),
469 VideoCaptureFormat
format(
470 dimensions
, frame_format
.frame_rate
, media::PIXEL_FORMAT_I420
);
471 BrowserThread::PostTask(
475 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
483 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width",
484 frame_format
.frame_size
.width());
485 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height",
486 frame_format
.frame_size
.height());
487 UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio",
488 frame_format
.frame_size
.width(),
489 frame_format
.frame_size
.height());
490 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate",
491 frame_format
.frame_rate
);
492 UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.PixelFormat",
493 frame_format
.pixel_format
,
494 media::PIXEL_FORMAT_MAX
);
495 first_frame_
= false;
500 VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
501 const scoped_refptr
<Buffer
>& buffer
,
502 const VideoCaptureFormat
& buffer_format
,
503 const scoped_refptr
<media::VideoFrame
>& frame
,
504 base::TimeTicks timestamp
) {
505 BrowserThread::PostTask(
509 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
517 void VideoCaptureController::VideoCaptureDeviceClient::OnError(
518 const std::string
& reason
) {
519 MediaStreamManager::SendMessageToNativeLog(
520 "Error on video capture: " + reason
);
521 BrowserThread::PostTask(BrowserThread::IO
,
523 base::Bind(&VideoCaptureController::DoErrorOnIOThread
, controller_
));
526 void VideoCaptureController::VideoCaptureDeviceClient::OnLog(
527 const std::string
& message
) {
528 MediaStreamManager::SendMessageToNativeLog("Video capture: " + message
);
531 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>
532 VideoCaptureController::VideoCaptureDeviceClient::DoReserveOutputBuffer(
533 media::VideoFrame::Format format
,
534 const gfx::Size
& dimensions
) {
535 size_t frame_bytes
= 0;
536 if (format
== media::VideoFrame::NATIVE_TEXTURE
) {
537 DCHECK_EQ(dimensions
.width(), 0);
538 DCHECK_EQ(dimensions
.height(), 0);
540 // The capture pipeline expects I420 for now.
541 DCHECK_EQ(format
, media::VideoFrame::I420
)
542 << "Non-I420 output buffer format " << format
<< " requested";
543 frame_bytes
= media::VideoFrame::AllocationSize(format
, dimensions
);
546 int buffer_id_to_drop
= VideoCaptureBufferPool::kInvalidId
;
548 buffer_pool_
->ReserveForProducer(frame_bytes
, &buffer_id_to_drop
);
549 if (buffer_id
== VideoCaptureBufferPool::kInvalidId
)
553 buffer_pool_
->GetBufferInfo(buffer_id
, &data
, &size
);
555 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> output_buffer(
556 new PoolBuffer(buffer_pool_
, buffer_id
, data
, size
));
558 if (buffer_id_to_drop
!= VideoCaptureBufferPool::kInvalidId
) {
559 BrowserThread::PostTask(BrowserThread::IO
,
561 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread
,
562 controller_
, buffer_id_to_drop
));
565 return output_buffer
;
568 VideoCaptureController::~VideoCaptureController() {
569 STLDeleteContainerPointers(controller_clients_
.begin(),
570 controller_clients_
.end());
573 void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
574 const scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>& buffer
,
575 const media::VideoCaptureFormat
& buffer_format
,
576 const scoped_refptr
<media::VideoFrame
>& frame
,
577 base::TimeTicks timestamp
) {
578 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
579 DCHECK_NE(buffer
->id(), VideoCaptureBufferPool::kInvalidId
);
582 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
583 for (ControllerClients::iterator client_it
= controller_clients_
.begin();
584 client_it
!= controller_clients_
.end(); ++client_it
) {
585 ControllerClient
* client
= *client_it
;
586 if (client
->session_closed
)
589 if (frame
->format() == media::VideoFrame::NATIVE_TEXTURE
) {
590 client
->event_handler
->OnMailboxBufferReady(client
->controller_id
,
592 *frame
->mailbox_holder(),
596 bool is_new_buffer
= client
->known_buffers
.insert(buffer
->id()).second
;
598 // On the first use of a buffer on a client, share the memory handle.
599 size_t memory_size
= 0;
600 base::SharedMemoryHandle remote_handle
= buffer_pool_
->ShareToProcess(
601 buffer
->id(), client
->render_process_handle
, &memory_size
);
602 client
->event_handler
->OnBufferCreated(
603 client
->controller_id
, remote_handle
, memory_size
, buffer
->id());
606 client
->event_handler
->OnBufferReady(
607 client
->controller_id
, buffer
->id(), buffer_format
, timestamp
);
611 client
->active_buffers
.insert(std::make_pair(buffer
->id(), frame
))
613 DCHECK(inserted
) << "Unexpected duplicate buffer: " << buffer
->id();
618 buffer_pool_
->HoldForConsumers(buffer
->id(), count
);
621 void VideoCaptureController::DoErrorOnIOThread() {
622 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
623 state_
= VIDEO_CAPTURE_STATE_ERROR
;
625 for (ControllerClients::iterator client_it
= controller_clients_
.begin();
626 client_it
!= controller_clients_
.end(); ++client_it
) {
627 ControllerClient
* client
= *client_it
;
628 if (client
->session_closed
)
631 client
->event_handler
->OnError(client
->controller_id
);
635 void VideoCaptureController::DoBufferDestroyedOnIOThread(
636 int buffer_id_to_drop
) {
637 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
639 for (ControllerClients::iterator client_it
= controller_clients_
.begin();
640 client_it
!= controller_clients_
.end(); ++client_it
) {
641 ControllerClient
* client
= *client_it
;
642 if (client
->session_closed
)
645 if (client
->known_buffers
.erase(buffer_id_to_drop
)) {
646 client
->event_handler
->OnBufferDestroyed(client
->controller_id
,
652 VideoCaptureController::ControllerClient
*
653 VideoCaptureController::FindClient(
654 const VideoCaptureControllerID
& id
,
655 VideoCaptureControllerEventHandler
* handler
,
656 const ControllerClients
& clients
) {
657 for (ControllerClients::const_iterator client_it
= clients
.begin();
658 client_it
!= clients
.end(); ++client_it
) {
659 if ((*client_it
)->controller_id
== id
&&
660 (*client_it
)->event_handler
== handler
) {
667 VideoCaptureController::ControllerClient
*
668 VideoCaptureController::FindClient(
670 const ControllerClients
& clients
) {
671 for (ControllerClients::const_iterator client_it
= clients
.begin();
672 client_it
!= clients
.end(); ++client_it
) {
673 if ((*client_it
)->session_id
== session_id
) {
680 int VideoCaptureController::GetClientCount() {
681 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
682 return controller_clients_
.size();
685 } // namespace content