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/command_line.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_buffer_pool.h"
17 #include "content/browser/renderer_host/media/video_capture_device_client.h"
18 #include "content/browser/renderer_host/media/video_capture_manager.h"
19 #include "content/common/gpu/client/gl_helper.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/common/content_switches.h"
22 #include "gpu/command_buffer/common/mailbox_holder.h"
23 #include "media/base/video_frame.h"
25 #if !defined(OS_ANDROID)
26 #include "content/browser/compositor/image_transport_factory.h"
29 #if defined(ENABLE_WEBRTC) && (defined(OS_LINUX) || defined(OS_MACOSX))
30 #include "content/browser/renderer_host/media/video_capture_texture_wrapper.h"
33 using media::VideoCaptureFormat
;
34 using media::VideoFrame
;
40 static const int kInfiniteRatio
= 99999;
42 #define UMA_HISTOGRAM_ASPECT_RATIO(name, width, height) \
43 UMA_HISTOGRAM_SPARSE_SLOWLY( \
45 (height) ? ((width) * 100) / (height) : kInfiniteRatio);
47 class SyncPointClientImpl
: public VideoFrame::SyncPointClient
{
49 explicit SyncPointClientImpl(GLHelper
* gl_helper
) : gl_helper_(gl_helper
) {}
50 ~SyncPointClientImpl() override
{}
51 uint32
InsertSyncPoint() override
{ return gl_helper_
->InsertSyncPoint(); }
52 void WaitSyncPoint(uint32 sync_point
) override
{
53 gl_helper_
->WaitSyncPoint(sync_point
);
60 void ReturnVideoFrame(const scoped_refptr
<VideoFrame
>& video_frame
,
62 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
63 #if defined(OS_ANDROID)
66 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
67 // UpdateReleaseSyncPoint() creates a new sync_point using |gl_helper|, so
68 // wait the given |sync_point| using |gl_helper|.
70 gl_helper
->WaitSyncPoint(sync_point
);
71 SyncPointClientImpl
client(gl_helper
);
72 video_frame
->UpdateReleaseSyncPoint(&client
);
77 } // anonymous namespace
79 struct VideoCaptureController::ControllerClient
{
80 ControllerClient(VideoCaptureControllerID id
,
81 VideoCaptureControllerEventHandler
* handler
,
82 base::ProcessHandle render_process
,
83 media::VideoCaptureSessionId session_id
,
84 const media::VideoCaptureParams
& params
)
86 event_handler(handler
),
87 render_process_handle(render_process
),
88 session_id(session_id
),
90 session_closed(false),
93 ~ControllerClient() {}
95 // ID used for identifying this object.
96 const VideoCaptureControllerID controller_id
;
97 VideoCaptureControllerEventHandler
* const event_handler
;
99 // Handle to the render process that will receive the capture buffers.
100 const base::ProcessHandle render_process_handle
;
101 const media::VideoCaptureSessionId session_id
;
102 const media::VideoCaptureParams parameters
;
104 // Buffers that are currently known to this client.
105 std::set
<int> known_buffers
;
107 // Buffers currently held by this client, and syncpoint callback to call when
108 // they are returned from the client.
109 typedef std::map
<int, scoped_refptr
<VideoFrame
>> ActiveBufferMap
;
110 ActiveBufferMap active_buffers
;
112 // State of capture session, controlled by VideoCaptureManager directly. This
113 // transitions to true as soon as StopSession() occurs, at which point the
114 // client is sent an OnEnded() event. However, because the client retains a
115 // VideoCaptureController* pointer, its ControllerClient entry lives on until
116 // it unregisters itself via RemoveClient(), which may happen asynchronously.
118 // TODO(nick): If we changed the semantics of VideoCaptureHost so that
119 // OnEnded() events were processed synchronously (with the RemoveClient() done
120 // implicitly), we could avoid tracking this state here in the Controller, and
121 // simplify the code in both places.
124 // Indicates whether the client is paused, if true, VideoCaptureController
125 // stops updating its buffer.
129 VideoCaptureController::VideoCaptureController(int max_buffers
)
130 : buffer_pool_(new VideoCaptureBufferPool(max_buffers
)),
131 state_(VIDEO_CAPTURE_STATE_STARTED
),
132 has_received_frames_(false),
133 weak_ptr_factory_(this) {
134 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
137 base::WeakPtr
<VideoCaptureController
>
138 VideoCaptureController::GetWeakPtrForIOThread() {
139 return weak_ptr_factory_
.GetWeakPtr();
142 scoped_ptr
<media::VideoCaptureDevice::Client
>
143 VideoCaptureController::NewDeviceClient(
144 const scoped_refptr
<base::SingleThreadTaskRunner
>& capture_task_runner
,
145 const media::VideoCaptureFormat
& format
) {
146 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
147 #if defined(ENABLE_WEBRTC) && (defined(OS_LINUX) || defined(OS_MACOSX))
148 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
149 switches::kEnableWebRtcCaptureToTexture
)) {
150 return make_scoped_ptr(new VideoCaptureTextureWrapper(
151 this->GetWeakPtrForIOThread(), buffer_pool_
, capture_task_runner
,
153 DVLOG(1) << "TextureWrapper, format " << format
.ToString();
156 return make_scoped_ptr(
157 new VideoCaptureDeviceClient(this->GetWeakPtrForIOThread(),
161 void VideoCaptureController::AddClient(
162 VideoCaptureControllerID id
,
163 VideoCaptureControllerEventHandler
* event_handler
,
164 base::ProcessHandle render_process
,
165 media::VideoCaptureSessionId session_id
,
166 const media::VideoCaptureParams
& params
) {
167 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
168 DVLOG(1) << "VideoCaptureController::AddClient, id " << id
169 << ", " << params
.requested_format
.frame_size
.ToString()
170 << ", " << params
.requested_format
.frame_rate
171 << ", " << session_id
174 // If this is the first client added to the controller, cache the parameters.
175 if (!controller_clients_
.size())
176 video_capture_format_
= params
.requested_format
;
178 // Signal error in case device is already in error state.
179 if (state_
== VIDEO_CAPTURE_STATE_ERROR
) {
180 event_handler
->OnError(id
);
184 // Do nothing if this client has called AddClient before.
185 if (FindClient(id
, event_handler
, controller_clients_
))
188 ControllerClient
* client
= new ControllerClient(
189 id
, event_handler
, render_process
, session_id
, params
);
190 // If we already have gotten frame_info from the device, repeat it to the new
192 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
193 controller_clients_
.push_back(client
);
198 int VideoCaptureController::RemoveClient(
199 VideoCaptureControllerID id
,
200 VideoCaptureControllerEventHandler
* event_handler
) {
201 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
202 DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id
;
204 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
206 return kInvalidMediaCaptureSessionId
;
208 // Take back all buffers held by the |client|.
209 for (const auto& buffer
: client
->active_buffers
)
210 buffer_pool_
->RelinquishConsumerHold(buffer
.first
, 1);
211 client
->active_buffers
.clear();
213 int session_id
= client
->session_id
;
214 controller_clients_
.remove(client
);
220 void VideoCaptureController::PauseOrResumeClient(
221 VideoCaptureControllerID id
,
222 VideoCaptureControllerEventHandler
* event_handler
,
224 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
225 DVLOG(1) << "VideoCaptureController::PauseOrResumeClient, id "
226 << id
<< ", " << pause
;
228 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
232 DCHECK(client
->paused
!= pause
);
233 client
->paused
= pause
;
236 void VideoCaptureController::StopSession(int session_id
) {
237 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
238 DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id
;
240 ControllerClient
* client
= FindClient(session_id
, controller_clients_
);
243 client
->session_closed
= true;
244 client
->event_handler
->OnEnded(client
->controller_id
);
248 void VideoCaptureController::ReturnBuffer(
249 VideoCaptureControllerID id
,
250 VideoCaptureControllerEventHandler
* event_handler
,
253 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
255 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
257 // If this buffer is not held by this client, or this client doesn't exist
258 // in controller, do nothing.
259 ControllerClient::ActiveBufferMap::iterator iter
;
260 if (!client
|| (iter
= client
->active_buffers
.find(buffer_id
)) ==
261 client
->active_buffers
.end()) {
265 scoped_refptr
<VideoFrame
> frame
= iter
->second
;
266 client
->active_buffers
.erase(iter
);
267 buffer_pool_
->RelinquishConsumerHold(buffer_id
, 1);
269 #if defined(OS_ANDROID)
270 DCHECK_EQ(0u, sync_point
);
273 BrowserThread::PostTask(BrowserThread::UI
,
275 base::Bind(&ReturnVideoFrame
, frame
, sync_point
));
278 const media::VideoCaptureFormat
&
279 VideoCaptureController::GetVideoCaptureFormat() const {
280 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
281 return video_capture_format_
;
284 VideoCaptureController::~VideoCaptureController() {
285 STLDeleteContainerPointers(controller_clients_
.begin(),
286 controller_clients_
.end());
289 void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
290 const scoped_refptr
<media::VideoCaptureDevice::Client::Buffer
>& buffer
,
291 const scoped_refptr
<VideoFrame
>& frame
,
292 const base::TimeTicks
& timestamp
) {
293 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
294 DCHECK_NE(buffer
->id(), VideoCaptureBufferPool::kInvalidId
);
297 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
298 if (!frame
->metadata()->HasKey(media::VideoFrameMetadata::FRAME_RATE
)) {
299 frame
->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE
,
300 video_capture_format_
.frame_rate
);
302 scoped_ptr
<base::DictionaryValue
> metadata(new base::DictionaryValue());
303 frame
->metadata()->MergeInternalValuesInto(metadata
.get());
305 for (const auto& client
: controller_clients_
) {
306 if (client
->session_closed
|| client
->paused
)
309 scoped_ptr
<base::DictionaryValue
> copy_of_metadata
;
310 if (client
== controller_clients_
.back())
311 copy_of_metadata
= metadata
.Pass();
313 copy_of_metadata
.reset(metadata
->DeepCopy());
315 if (frame
->format() == VideoFrame::NATIVE_TEXTURE
) {
316 DCHECK(frame
->coded_size() == frame
->visible_rect().size())
317 << "Textures are always supposed to be tightly packed.";
318 client
->event_handler
->OnMailboxBufferReady(client
->controller_id
,
320 *frame
->mailbox_holder(),
323 copy_of_metadata
.Pass());
324 } else if (frame
->format() == media::VideoFrame::I420
) {
325 bool is_new_buffer
= client
->known_buffers
.insert(buffer
->id()).second
;
327 // On the first use of a buffer on a client, share the memory handle.
328 size_t memory_size
= 0;
329 base::SharedMemoryHandle remote_handle
= buffer_pool_
->ShareToProcess(
330 buffer
->id(), client
->render_process_handle
, &memory_size
);
331 client
->event_handler
->OnBufferCreated(
332 client
->controller_id
, remote_handle
, memory_size
, buffer
->id());
335 client
->event_handler
->OnBufferReady(
336 client
->controller_id
, buffer
->id(), frame
->coded_size(),
337 frame
->visible_rect(), timestamp
, copy_of_metadata
.Pass());
339 // VideoFrame format not supported.
345 client
->active_buffers
.insert(std::make_pair(buffer
->id(), frame
))
347 DCHECK(inserted
) << "Unexpected duplicate buffer: " << buffer
->id();
352 if (!has_received_frames_
) {
353 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width",
354 frame
->visible_rect().width());
355 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height",
356 frame
->visible_rect().height());
357 UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio",
358 frame
->visible_rect().width(),
359 frame
->visible_rect().height());
361 if (!frame
->metadata()->GetDouble(media::VideoFrameMetadata::FRAME_RATE
,
363 frame_rate
= video_capture_format_
.frame_rate
;
364 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate", frame_rate
);
365 has_received_frames_
= true;
368 buffer_pool_
->HoldForConsumers(buffer
->id(), count
);
371 void VideoCaptureController::DoErrorOnIOThread() {
372 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
373 state_
= VIDEO_CAPTURE_STATE_ERROR
;
375 for (const auto* client
: controller_clients_
) {
376 if (client
->session_closed
)
378 client
->event_handler
->OnError(client
->controller_id
);
382 void VideoCaptureController::DoLogOnIOThread(const std::string
& message
) {
383 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
384 MediaStreamManager::SendMessageToNativeLog("Video capture: " + message
);
387 void VideoCaptureController::DoBufferDestroyedOnIOThread(
388 int buffer_id_to_drop
) {
389 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
391 for (auto* client
: controller_clients_
) {
392 if (client
->session_closed
)
395 if (client
->known_buffers
.erase(buffer_id_to_drop
)) {
396 client
->event_handler
->OnBufferDestroyed(client
->controller_id
,
402 VideoCaptureController::ControllerClient
* VideoCaptureController::FindClient(
403 VideoCaptureControllerID id
,
404 VideoCaptureControllerEventHandler
* handler
,
405 const ControllerClients
& clients
) {
406 for (auto* client
: clients
) {
407 if (client
->controller_id
== id
&& client
->event_handler
== handler
)
413 VideoCaptureController::ControllerClient
* VideoCaptureController::FindClient(
415 const ControllerClients
& clients
) {
416 for (auto client
: clients
) {
417 if (client
->session_id
== session_id
)
423 int VideoCaptureController::GetClientCount() const {
424 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
425 return controller_clients_
.size();
428 int VideoCaptureController::GetActiveClientCount() const {
429 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
430 int active_client_count
= 0;
431 for (ControllerClient
* client
: controller_clients_
) {
433 ++active_client_count
;
435 return active_client_count
;
438 } // namespace content