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 // The number of buffers that VideoCaptureBufferPool should allocate.
45 const int kNoOfBuffers
= 3;
47 class PoolBuffer
: public media::VideoCaptureDevice::Client::Buffer
{
49 PoolBuffer(const scoped_refptr
<VideoCaptureBufferPool
>& pool
,
53 : Buffer(buffer_id
, data
, size
), pool_(pool
) {
58 virtual ~PoolBuffer() { pool_
->RelinquishProducerReservation(id()); }
60 const scoped_refptr
<VideoCaptureBufferPool
> pool_
;
63 class SyncPointClientImpl
: public media::VideoFrame::SyncPointClient
{
65 explicit SyncPointClientImpl(GLHelper
* gl_helper
) : gl_helper_(gl_helper
) {}
66 virtual ~SyncPointClientImpl() {}
67 virtual uint32
InsertSyncPoint() OVERRIDE
{
68 return gl_helper_
->InsertSyncPoint();
70 virtual void WaitSyncPoint(uint32 sync_point
) OVERRIDE
{
71 gl_helper_
->WaitSyncPoint(sync_point
);
78 void ReturnVideoFrame(const scoped_refptr
<media::VideoFrame
>& video_frame
,
80 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
81 #if defined(OS_ANDROID)
83 ImageTransportFactoryAndroid::GetInstance()->GetGLHelper();
85 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
88 // UpdateReleaseSyncPoint() creates a new sync_point using |gl_helper|, so
89 // wait the given |sync_point| using |gl_helper|.
90 gl_helper
->WaitSyncPoint(sync_point
);
91 SyncPointClientImpl
client(gl_helper
);
92 video_frame
->UpdateReleaseSyncPoint(&client
);
95 } // anonymous namespace
97 struct VideoCaptureController::ControllerClient
{
98 ControllerClient(const VideoCaptureControllerID
& id
,
99 VideoCaptureControllerEventHandler
* handler
,
100 base::ProcessHandle render_process
,
101 media::VideoCaptureSessionId session_id
,
102 const media::VideoCaptureParams
& params
)
104 event_handler(handler
),
105 render_process_handle(render_process
),
106 session_id(session_id
),
108 session_closed(false) {}
110 ~ControllerClient() {}
112 // ID used for identifying this object.
113 const VideoCaptureControllerID controller_id
;
114 VideoCaptureControllerEventHandler
* const event_handler
;
116 // Handle to the render process that will receive the capture buffers.
117 const base::ProcessHandle render_process_handle
;
118 const media::VideoCaptureSessionId session_id
;
119 const media::VideoCaptureParams parameters
;
121 // Buffers that are currently known to this client.
122 std::set
<int> known_buffers
;
124 // Buffers currently held by this client, and syncpoint callback to call when
125 // they are returned from the client.
126 typedef std::map
<int, scoped_refptr
<media::VideoFrame
> > ActiveBufferMap
;
127 ActiveBufferMap active_buffers
;
129 // State of capture session, controlled by VideoCaptureManager directly. This
130 // transitions to true as soon as StopSession() occurs, at which point the
131 // client is sent an OnEnded() event. However, because the client retains a
132 // VideoCaptureController* pointer, its ControllerClient entry lives on until
133 // it unregisters itself via RemoveClient(), which may happen asynchronously.
135 // TODO(nick): If we changed the semantics of VideoCaptureHost so that
136 // OnEnded() events were processed synchronously (with the RemoveClient() done
137 // implicitly), we could avoid tracking this state here in the Controller, and
138 // simplify the code in both places.
142 // Receives events from the VideoCaptureDevice and posts them to a
143 // VideoCaptureController on the IO thread. An instance of this class may safely
144 // outlive its target VideoCaptureController.
146 // Methods of this class may be called from any thread, and in practice will
147 // often be called on some auxiliary thread depending on the platform and the
148 // device type; including, for example, the DirectShow thread on Windows, the
149 // v4l2_thread on Linux, and the UI thread for tab capture.
150 class VideoCaptureController::VideoCaptureDeviceClient
151 : public media::VideoCaptureDevice::Client
{
153 explicit VideoCaptureDeviceClient(
154 const base::WeakPtr
<VideoCaptureController
>& controller
,
155 const scoped_refptr
<VideoCaptureBufferPool
>& buffer_pool
);
156 virtual ~VideoCaptureDeviceClient();
158 // VideoCaptureDevice::Client implementation.
159 virtual scoped_refptr
<Buffer
> ReserveOutputBuffer(
160 media::VideoFrame::Format format
,
161 const gfx::Size
& size
) OVERRIDE
;
162 virtual void OnIncomingCapturedData(const uint8
* data
,
164 const VideoCaptureFormat
& frame_format
,
166 base::TimeTicks timestamp
) OVERRIDE
;
167 virtual void OnIncomingCapturedVideoFrame(
168 const scoped_refptr
<Buffer
>& buffer
,
169 const VideoCaptureFormat
& buffer_format
,
170 const scoped_refptr
<media::VideoFrame
>& frame
,
171 base::TimeTicks timestamp
) OVERRIDE
;
172 virtual void OnError(const std::string
& reason
) OVERRIDE
;
173 virtual void OnLog(const std::string
& message
) OVERRIDE
;
176 scoped_refptr
<Buffer
> DoReserveOutputBuffer(media::VideoFrame::Format format
,
177 const gfx::Size
& dimensions
);
179 // The controller to which we post events.
180 const base::WeakPtr
<VideoCaptureController
> controller_
;
182 // The pool of shared-memory buffers used for capturing.
183 const scoped_refptr
<VideoCaptureBufferPool
> buffer_pool_
;
188 VideoCaptureController::VideoCaptureController()
189 : buffer_pool_(new VideoCaptureBufferPool(kNoOfBuffers
)),
190 state_(VIDEO_CAPTURE_STATE_STARTED
),
191 weak_ptr_factory_(this) {
194 VideoCaptureController::VideoCaptureDeviceClient::VideoCaptureDeviceClient(
195 const base::WeakPtr
<VideoCaptureController
>& controller
,
196 const scoped_refptr
<VideoCaptureBufferPool
>& buffer_pool
)
197 : controller_(controller
), buffer_pool_(buffer_pool
), first_frame_(true) {}
199 VideoCaptureController::VideoCaptureDeviceClient::~VideoCaptureDeviceClient() {}
201 base::WeakPtr
<VideoCaptureController
> VideoCaptureController::GetWeakPtr() {
202 return weak_ptr_factory_
.GetWeakPtr();
205 scoped_ptr
<media::VideoCaptureDevice::Client
>
206 VideoCaptureController::NewDeviceClient() {
207 scoped_ptr
<media::VideoCaptureDevice::Client
> result(
208 new VideoCaptureDeviceClient(this->GetWeakPtr(), buffer_pool_
));
209 return result
.Pass();
212 void VideoCaptureController::AddClient(
213 const VideoCaptureControllerID
& id
,
214 VideoCaptureControllerEventHandler
* event_handler
,
215 base::ProcessHandle render_process
,
216 media::VideoCaptureSessionId session_id
,
217 const media::VideoCaptureParams
& params
) {
218 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
219 DVLOG(1) << "VideoCaptureController::AddClient, id " << id
.device_id
220 << ", " << params
.requested_format
.frame_size
.ToString()
221 << ", " << params
.requested_format
.frame_rate
222 << ", " << session_id
225 // If this is the first client added to the controller, cache the parameters.
226 if (!controller_clients_
.size())
227 video_capture_format_
= params
.requested_format
;
229 // Signal error in case device is already in error state.
230 if (state_
== VIDEO_CAPTURE_STATE_ERROR
) {
231 event_handler
->OnError(id
);
235 // Do nothing if this client has called AddClient before.
236 if (FindClient(id
, event_handler
, controller_clients_
))
239 ControllerClient
* client
= new ControllerClient(
240 id
, event_handler
, render_process
, session_id
, params
);
241 // If we already have gotten frame_info from the device, repeat it to the new
243 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
244 controller_clients_
.push_back(client
);
249 int VideoCaptureController::RemoveClient(
250 const VideoCaptureControllerID
& id
,
251 VideoCaptureControllerEventHandler
* event_handler
) {
252 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
253 DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id
.device_id
;
255 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
257 return kInvalidMediaCaptureSessionId
;
259 // Take back all buffers held by the |client|.
260 for (ControllerClient::ActiveBufferMap::iterator buffer_it
=
261 client
->active_buffers
.begin();
262 buffer_it
!= client
->active_buffers
.end();
264 buffer_pool_
->RelinquishConsumerHold(buffer_it
->first
, 1);
266 client
->active_buffers
.clear();
268 int session_id
= client
->session_id
;
269 controller_clients_
.remove(client
);
275 void VideoCaptureController::StopSession(int session_id
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
277 DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id
;
279 ControllerClient
* client
= FindClient(session_id
, controller_clients_
);
282 client
->session_closed
= true;
283 client
->event_handler
->OnEnded(client
->controller_id
);
287 void VideoCaptureController::ReturnBuffer(
288 const VideoCaptureControllerID
& id
,
289 VideoCaptureControllerEventHandler
* event_handler
,
292 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
294 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
296 // If this buffer is not held by this client, or this client doesn't exist
297 // in controller, do nothing.
298 ControllerClient::ActiveBufferMap::iterator iter
;
299 if (!client
|| (iter
= client
->active_buffers
.find(buffer_id
)) ==
300 client
->active_buffers
.end()) {
304 scoped_refptr
<media::VideoFrame
> frame
= iter
->second
;
305 client
->active_buffers
.erase(iter
);
306 buffer_pool_
->RelinquishConsumerHold(buffer_id
, 1);
309 BrowserThread::PostTask(BrowserThread::UI
,
311 base::Bind(&ReturnVideoFrame
, frame
, sync_point
));
314 const media::VideoCaptureFormat
&
315 VideoCaptureController::GetVideoCaptureFormat() const {
316 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
317 return video_capture_format_
;
320 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>
321 VideoCaptureController::VideoCaptureDeviceClient::ReserveOutputBuffer(
322 media::VideoFrame::Format format
,
323 const gfx::Size
& size
) {
324 return DoReserveOutputBuffer(format
, size
);
327 void VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedData(
330 const VideoCaptureFormat
& frame_format
,
332 base::TimeTicks timestamp
) {
333 TRACE_EVENT0("video", "VideoCaptureController::OnIncomingCapturedData");
335 if (!frame_format
.IsValid())
338 // Chopped pixels in width/height in case video capture device has odd
339 // numbers for width/height.
340 int chopped_width
= 0;
341 int chopped_height
= 0;
342 int new_unrotated_width
= frame_format
.frame_size
.width();
343 int new_unrotated_height
= frame_format
.frame_size
.height();
345 if (new_unrotated_width
& 1) {
346 --new_unrotated_width
;
349 if (new_unrotated_height
& 1) {
350 --new_unrotated_height
;
354 int destination_width
= new_unrotated_width
;
355 int destination_height
= new_unrotated_height
;
356 if (rotation
== 90 || rotation
== 270) {
357 destination_width
= new_unrotated_height
;
358 destination_height
= new_unrotated_width
;
360 const gfx::Size
dimensions(destination_width
, destination_height
);
361 if (!media::VideoFrame::IsValidConfig(media::VideoFrame::I420
,
363 gfx::Rect(dimensions
),
368 scoped_refptr
<Buffer
> buffer
=
369 DoReserveOutputBuffer(media::VideoFrame::I420
, dimensions
);
373 uint8
* yplane
= NULL
;
375 yplane
= reinterpret_cast<uint8
*>(buffer
->data());
378 media::VideoFrame::PlaneAllocationSize(
379 media::VideoFrame::I420
, media::VideoFrame::kYPlane
, dimensions
);
382 media::VideoFrame::PlaneAllocationSize(
383 media::VideoFrame::I420
, media::VideoFrame::kUPlane
, dimensions
);
384 int yplane_stride
= dimensions
.width();
385 int uv_plane_stride
= yplane_stride
/ 2;
388 libyuv::FourCC origin_colorspace
= libyuv::FOURCC_ANY
;
390 libyuv::RotationMode rotation_mode
= libyuv::kRotate0
;
392 rotation_mode
= libyuv::kRotate90
;
393 else if (rotation
== 180)
394 rotation_mode
= libyuv::kRotate180
;
395 else if (rotation
== 270)
396 rotation_mode
= libyuv::kRotate270
;
398 switch (frame_format
.pixel_format
) {
399 case media::PIXEL_FORMAT_UNKNOWN
: // Color format not set.
401 case media::PIXEL_FORMAT_I420
:
402 DCHECK(!chopped_width
&& !chopped_height
);
403 origin_colorspace
= libyuv::FOURCC_I420
;
405 case media::PIXEL_FORMAT_YV12
:
406 DCHECK(!chopped_width
&& !chopped_height
);
407 origin_colorspace
= libyuv::FOURCC_YV12
;
409 case media::PIXEL_FORMAT_NV21
:
410 DCHECK(!chopped_width
&& !chopped_height
);
411 origin_colorspace
= libyuv::FOURCC_NV21
;
413 case media::PIXEL_FORMAT_YUY2
:
414 DCHECK(!chopped_width
&& !chopped_height
);
415 origin_colorspace
= libyuv::FOURCC_YUY2
;
417 case media::PIXEL_FORMAT_UYVY
:
418 DCHECK(!chopped_width
&& !chopped_height
);
419 origin_colorspace
= libyuv::FOURCC_UYVY
;
421 case media::PIXEL_FORMAT_RGB24
:
422 origin_colorspace
= libyuv::FOURCC_24BG
;
424 // TODO(wjia): Currently, for RGB24 on WIN, capture device always
425 // passes in positive src_width and src_height. Remove this hardcoded
426 // value when nagative src_height is supported. The negative src_height
427 // indicates that vertical flipping is needed.
431 case media::PIXEL_FORMAT_ARGB
:
432 origin_colorspace
= libyuv::FOURCC_ARGB
;
434 case media::PIXEL_FORMAT_MJPEG
:
435 origin_colorspace
= libyuv::FOURCC_MJPG
;
441 libyuv::ConvertToI420(data
,
451 frame_format
.frame_size
.width(),
452 (flip
? -frame_format
.frame_size
.height() :
453 frame_format
.frame_size
.height()),
455 new_unrotated_height
,
458 scoped_refptr
<media::VideoFrame
> frame
=
459 media::VideoFrame::WrapExternalPackedMemory(
460 media::VideoFrame::I420
,
462 gfx::Rect(dimensions
),
465 media::VideoFrame::AllocationSize(media::VideoFrame::I420
,
467 base::SharedMemory::NULLHandle(),
472 VideoCaptureFormat
format(
473 dimensions
, frame_format
.frame_rate
, media::PIXEL_FORMAT_I420
);
474 BrowserThread::PostTask(
478 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
486 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width",
487 frame_format
.frame_size
.width());
488 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height",
489 frame_format
.frame_size
.height());
490 UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio",
491 frame_format
.frame_size
.width(),
492 frame_format
.frame_size
.height());
493 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate",
494 frame_format
.frame_rate
);
495 UMA_HISTOGRAM_ENUMERATION("Media.VideoCapture.PixelFormat",
496 frame_format
.pixel_format
,
497 media::PIXEL_FORMAT_MAX
);
498 first_frame_
= false;
503 VideoCaptureController::VideoCaptureDeviceClient::OnIncomingCapturedVideoFrame(
504 const scoped_refptr
<Buffer
>& buffer
,
505 const VideoCaptureFormat
& buffer_format
,
506 const scoped_refptr
<media::VideoFrame
>& frame
,
507 base::TimeTicks timestamp
) {
508 BrowserThread::PostTask(
512 &VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread
,
520 void VideoCaptureController::VideoCaptureDeviceClient::OnError(
521 const std::string
& reason
) {
522 MediaStreamManager::SendMessageToNativeLog(
523 "Error on video capture: " + reason
);
524 BrowserThread::PostTask(BrowserThread::IO
,
526 base::Bind(&VideoCaptureController::DoErrorOnIOThread
, controller_
));
529 void VideoCaptureController::VideoCaptureDeviceClient::OnLog(
530 const std::string
& message
) {
531 MediaStreamManager::SendMessageToNativeLog("Video capture: " + message
);
534 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>
535 VideoCaptureController::VideoCaptureDeviceClient::DoReserveOutputBuffer(
536 media::VideoFrame::Format format
,
537 const gfx::Size
& dimensions
) {
538 size_t frame_bytes
= 0;
539 if (format
== media::VideoFrame::NATIVE_TEXTURE
) {
540 DCHECK_EQ(dimensions
.width(), 0);
541 DCHECK_EQ(dimensions
.height(), 0);
543 // The capture pipeline expects I420 for now.
544 DCHECK_EQ(format
, media::VideoFrame::I420
)
545 << "Non-I420 output buffer format " << format
<< " requested";
546 frame_bytes
= media::VideoFrame::AllocationSize(format
, dimensions
);
549 int buffer_id_to_drop
= VideoCaptureBufferPool::kInvalidId
;
551 buffer_pool_
->ReserveForProducer(frame_bytes
, &buffer_id_to_drop
);
552 if (buffer_id
== VideoCaptureBufferPool::kInvalidId
)
556 buffer_pool_
->GetBufferInfo(buffer_id
, &data
, &size
);
558 scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
> output_buffer(
559 new PoolBuffer(buffer_pool_
, buffer_id
, data
, size
));
561 if (buffer_id_to_drop
!= VideoCaptureBufferPool::kInvalidId
) {
562 BrowserThread::PostTask(BrowserThread::IO
,
564 base::Bind(&VideoCaptureController::DoBufferDestroyedOnIOThread
,
565 controller_
, buffer_id_to_drop
));
568 return output_buffer
;
571 VideoCaptureController::~VideoCaptureController() {
572 STLDeleteContainerPointers(controller_clients_
.begin(),
573 controller_clients_
.end());
576 void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
577 const scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>& buffer
,
578 const media::VideoCaptureFormat
& buffer_format
,
579 const scoped_refptr
<media::VideoFrame
>& frame
,
580 base::TimeTicks timestamp
) {
581 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
582 DCHECK_NE(buffer
->id(), VideoCaptureBufferPool::kInvalidId
);
585 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
586 for (ControllerClients::iterator client_it
= controller_clients_
.begin();
587 client_it
!= controller_clients_
.end(); ++client_it
) {
588 ControllerClient
* client
= *client_it
;
589 if (client
->session_closed
)
592 if (frame
->format() == media::VideoFrame::NATIVE_TEXTURE
) {
593 client
->event_handler
->OnMailboxBufferReady(client
->controller_id
,
595 *frame
->mailbox_holder(),
599 bool is_new_buffer
= client
->known_buffers
.insert(buffer
->id()).second
;
601 // On the first use of a buffer on a client, share the memory handle.
602 size_t memory_size
= 0;
603 base::SharedMemoryHandle remote_handle
= buffer_pool_
->ShareToProcess(
604 buffer
->id(), client
->render_process_handle
, &memory_size
);
605 client
->event_handler
->OnBufferCreated(
606 client
->controller_id
, remote_handle
, memory_size
, buffer
->id());
609 client
->event_handler
->OnBufferReady(
610 client
->controller_id
, buffer
->id(), buffer_format
, timestamp
);
614 client
->active_buffers
.insert(std::make_pair(buffer
->id(), frame
))
616 DCHECK(inserted
) << "Unexpected duplicate buffer: " << buffer
->id();
621 buffer_pool_
->HoldForConsumers(buffer
->id(), count
);
624 void VideoCaptureController::DoErrorOnIOThread() {
625 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
626 state_
= VIDEO_CAPTURE_STATE_ERROR
;
628 for (ControllerClients::iterator client_it
= controller_clients_
.begin();
629 client_it
!= controller_clients_
.end(); ++client_it
) {
630 ControllerClient
* client
= *client_it
;
631 if (client
->session_closed
)
634 client
->event_handler
->OnError(client
->controller_id
);
638 void VideoCaptureController::DoBufferDestroyedOnIOThread(
639 int buffer_id_to_drop
) {
640 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
642 for (ControllerClients::iterator client_it
= controller_clients_
.begin();
643 client_it
!= controller_clients_
.end(); ++client_it
) {
644 ControllerClient
* client
= *client_it
;
645 if (client
->session_closed
)
648 if (client
->known_buffers
.erase(buffer_id_to_drop
)) {
649 client
->event_handler
->OnBufferDestroyed(client
->controller_id
,
655 VideoCaptureController::ControllerClient
*
656 VideoCaptureController::FindClient(
657 const VideoCaptureControllerID
& id
,
658 VideoCaptureControllerEventHandler
* handler
,
659 const ControllerClients
& clients
) {
660 for (ControllerClients::const_iterator client_it
= clients
.begin();
661 client_it
!= clients
.end(); ++client_it
) {
662 if ((*client_it
)->controller_id
== id
&&
663 (*client_it
)->event_handler
== handler
) {
670 VideoCaptureController::ControllerClient
*
671 VideoCaptureController::FindClient(
673 const ControllerClients
& clients
) {
674 for (ControllerClients::const_iterator client_it
= clients
.begin();
675 client_it
!= clients
.end(); ++client_it
) {
676 if ((*client_it
)->session_id
== session_id
) {
683 int VideoCaptureController::GetClientCount() {
684 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
685 return controller_clients_
.size();
688 } // namespace content