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 using media::VideoCaptureFormat
;
30 using media::VideoFrame
;
31 using media::VideoFrameMetadata
;
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 SyncPointClientImpl
: public VideoFrame::SyncPointClient
{
46 explicit SyncPointClientImpl(GLHelper
* gl_helper
) : gl_helper_(gl_helper
) {}
47 ~SyncPointClientImpl() override
{}
48 uint32
InsertSyncPoint() override
{ return gl_helper_
->InsertSyncPoint(); }
49 void WaitSyncPoint(uint32 sync_point
) override
{
50 gl_helper_
->WaitSyncPoint(sync_point
);
57 void ReturnVideoFrame(const scoped_refptr
<VideoFrame
>& video_frame
,
59 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
60 #if defined(OS_ANDROID)
63 GLHelper
* gl_helper
= ImageTransportFactory::GetInstance()->GetGLHelper();
64 // UpdateReleaseSyncPoint() creates a new sync_point using |gl_helper|, so
65 // wait the given |sync_point| using |gl_helper|.
67 gl_helper
->WaitSyncPoint(sync_point
);
68 SyncPointClientImpl
client(gl_helper
);
69 video_frame
->UpdateReleaseSyncPoint(&client
);
74 } // anonymous namespace
76 struct VideoCaptureController::ControllerClient
{
77 ControllerClient(VideoCaptureControllerID id
,
78 VideoCaptureControllerEventHandler
* handler
,
79 base::ProcessHandle render_process
,
80 media::VideoCaptureSessionId session_id
,
81 const media::VideoCaptureParams
& params
)
83 event_handler(handler
),
84 render_process_handle(render_process
),
85 session_id(session_id
),
87 session_closed(false),
90 ~ControllerClient() {}
92 // ID used for identifying this object.
93 const VideoCaptureControllerID controller_id
;
94 VideoCaptureControllerEventHandler
* const event_handler
;
96 // Handle to the render process that will receive the capture buffers.
97 const base::ProcessHandle render_process_handle
;
98 const media::VideoCaptureSessionId session_id
;
99 const media::VideoCaptureParams parameters
;
101 // Buffers that are currently known to this client.
102 std::set
<int> known_buffers
;
104 // Buffers currently held by this client, and syncpoint callback to call when
105 // they are returned from the client.
106 typedef std::map
<int, scoped_refptr
<VideoFrame
>> ActiveBufferMap
;
107 ActiveBufferMap active_buffers
;
109 // State of capture session, controlled by VideoCaptureManager directly. This
110 // transitions to true as soon as StopSession() occurs, at which point the
111 // client is sent an OnEnded() event. However, because the client retains a
112 // VideoCaptureController* pointer, its ControllerClient entry lives on until
113 // it unregisters itself via RemoveClient(), which may happen asynchronously.
115 // TODO(nick): If we changed the semantics of VideoCaptureHost so that
116 // OnEnded() events were processed synchronously (with the RemoveClient() done
117 // implicitly), we could avoid tracking this state here in the Controller, and
118 // simplify the code in both places.
121 // Indicates whether the client is paused, if true, VideoCaptureController
122 // stops updating its buffer.
126 VideoCaptureController::VideoCaptureController(int max_buffers
)
127 : buffer_pool_(new VideoCaptureBufferPool(max_buffers
)),
128 state_(VIDEO_CAPTURE_STATE_STARTED
),
129 has_received_frames_(false),
130 weak_ptr_factory_(this) {
131 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
134 base::WeakPtr
<VideoCaptureController
>
135 VideoCaptureController::GetWeakPtrForIOThread() {
136 return weak_ptr_factory_
.GetWeakPtr();
139 scoped_ptr
<media::VideoCaptureDevice::Client
>
140 VideoCaptureController::NewDeviceClient(
141 const scoped_refptr
<base::SingleThreadTaskRunner
>& capture_task_runner
) {
142 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
143 return make_scoped_ptr(new VideoCaptureDeviceClient(
144 this->GetWeakPtrForIOThread(), buffer_pool_
, capture_task_runner
));
147 void VideoCaptureController::AddClient(
148 VideoCaptureControllerID id
,
149 VideoCaptureControllerEventHandler
* event_handler
,
150 base::ProcessHandle render_process
,
151 media::VideoCaptureSessionId session_id
,
152 const media::VideoCaptureParams
& params
) {
153 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
154 DVLOG(1) << "VideoCaptureController::AddClient, id " << id
155 << ", " << params
.requested_format
.frame_size
.ToString()
156 << ", " << params
.requested_format
.frame_rate
157 << ", " << session_id
160 // If this is the first client added to the controller, cache the parameters.
161 if (!controller_clients_
.size())
162 video_capture_format_
= params
.requested_format
;
164 // Signal error in case device is already in error state.
165 if (state_
== VIDEO_CAPTURE_STATE_ERROR
) {
166 event_handler
->OnError(id
);
170 // Do nothing if this client has called AddClient before.
171 if (FindClient(id
, event_handler
, controller_clients_
))
174 ControllerClient
* client
= new ControllerClient(
175 id
, event_handler
, render_process
, session_id
, params
);
176 // If we already have gotten frame_info from the device, repeat it to the new
178 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
179 controller_clients_
.push_back(client
);
184 int VideoCaptureController::RemoveClient(
185 VideoCaptureControllerID id
,
186 VideoCaptureControllerEventHandler
* event_handler
) {
187 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
188 DVLOG(1) << "VideoCaptureController::RemoveClient, id " << id
;
190 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
192 return kInvalidMediaCaptureSessionId
;
194 // Take back all buffers held by the |client|.
195 for (const auto& buffer
: client
->active_buffers
)
196 buffer_pool_
->RelinquishConsumerHold(buffer
.first
, 1);
197 client
->active_buffers
.clear();
199 int session_id
= client
->session_id
;
200 controller_clients_
.remove(client
);
206 void VideoCaptureController::PauseOrResumeClient(
207 VideoCaptureControllerID id
,
208 VideoCaptureControllerEventHandler
* event_handler
,
210 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
211 DVLOG(1) << "VideoCaptureController::PauseOrResumeClient, id "
212 << id
<< ", " << pause
;
214 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
218 DLOG_IF(WARNING
, client
->paused
== pause
) << "Redundant client configuration";
219 client
->paused
= pause
;
222 void VideoCaptureController::StopSession(int session_id
) {
223 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
224 DVLOG(1) << "VideoCaptureController::StopSession, id " << session_id
;
226 ControllerClient
* client
= FindClient(session_id
, controller_clients_
);
229 client
->session_closed
= true;
230 client
->event_handler
->OnEnded(client
->controller_id
);
234 void VideoCaptureController::ReturnBuffer(
235 VideoCaptureControllerID id
,
236 VideoCaptureControllerEventHandler
* event_handler
,
239 double consumer_resource_utilization
) {
240 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
242 ControllerClient
* client
= FindClient(id
, event_handler
, controller_clients_
);
244 // If this buffer is not held by this client, or this client doesn't exist
245 // in controller, do nothing.
246 ControllerClient::ActiveBufferMap::iterator iter
;
247 if (!client
|| (iter
= client
->active_buffers
.find(buffer_id
)) ==
248 client
->active_buffers
.end()) {
253 // Set the RESOURCE_UTILIZATION to the maximum of those provided by each
254 // consumer (via separate calls to this method that refer to the same
255 // VideoFrame). The producer of this VideoFrame may check this value, after
256 // all consumer holds are relinquished, to make quality versus performance
257 // trade-off decisions.
258 scoped_refptr
<VideoFrame
> frame
= iter
->second
;
259 if (std::isfinite(consumer_resource_utilization
) &&
260 consumer_resource_utilization
>= 0.0) {
261 double resource_utilization
= -1.0;
262 if (frame
->metadata()->GetDouble(VideoFrameMetadata::RESOURCE_UTILIZATION
,
263 &resource_utilization
)) {
264 frame
->metadata()->SetDouble(VideoFrameMetadata::RESOURCE_UTILIZATION
,
265 std::max(consumer_resource_utilization
,
266 resource_utilization
));
268 frame
->metadata()->SetDouble(VideoFrameMetadata::RESOURCE_UTILIZATION
,
269 consumer_resource_utilization
);
273 client
->active_buffers
.erase(iter
);
274 buffer_pool_
->RelinquishConsumerHold(buffer_id
, 1);
276 #if defined(OS_ANDROID)
277 DCHECK_EQ(0u, sync_point
);
280 BrowserThread::PostTask(BrowserThread::UI
,
282 base::Bind(&ReturnVideoFrame
, frame
, sync_point
));
285 const media::VideoCaptureFormat
&
286 VideoCaptureController::GetVideoCaptureFormat() const {
287 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
288 return video_capture_format_
;
291 VideoCaptureController::~VideoCaptureController() {
292 STLDeleteContainerPointers(controller_clients_
.begin(),
293 controller_clients_
.end());
296 void VideoCaptureController::DoIncomingCapturedVideoFrameOnIOThread(
297 scoped_ptr
<media::VideoCaptureDevice::Client::Buffer
> buffer
,
298 const scoped_refptr
<VideoFrame
>& frame
,
299 const base::TimeTicks
& timestamp
) {
300 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
301 const int buffer_id
= buffer
->id();
302 DCHECK_NE(buffer_id
, VideoCaptureBufferPool::kInvalidId
);
305 if (state_
== VIDEO_CAPTURE_STATE_STARTED
) {
306 if (!frame
->metadata()->HasKey(VideoFrameMetadata::FRAME_RATE
)) {
307 frame
->metadata()->SetDouble(VideoFrameMetadata::FRAME_RATE
,
308 video_capture_format_
.frame_rate
);
310 scoped_ptr
<base::DictionaryValue
> metadata(new base::DictionaryValue());
311 frame
->metadata()->MergeInternalValuesInto(metadata
.get());
313 for (const auto& client
: controller_clients_
) {
314 if (client
->session_closed
|| client
->paused
)
317 CHECK((frame
->IsMappable() &&
318 frame
->format() == media::PIXEL_FORMAT_I420
) ||
319 (!frame
->IsMappable() && frame
->HasTextures() &&
320 frame
->format() == media::PIXEL_FORMAT_ARGB
))
321 << "Format and/or storage type combination not supported (received: "
322 << media::VideoPixelFormatToString(frame
->format()) << ")";
324 if (frame
->HasTextures()) {
325 DCHECK(frame
->coded_size() == frame
->visible_rect().size())
326 << "Textures are always supposed to be tightly packed.";
327 DCHECK_EQ(1u, VideoFrame::NumPlanes(frame
->format()));
328 } else if (frame
->format() == media::PIXEL_FORMAT_I420
) {
329 const bool is_new_buffer
=
330 client
->known_buffers
.insert(buffer_id
).second
;
332 // On the first use of a buffer on a client, share the memory handle.
333 size_t memory_size
= 0;
334 base::SharedMemoryHandle remote_handle
= buffer_pool_
->ShareToProcess(
335 buffer_id
, client
->render_process_handle
, &memory_size
);
336 client
->event_handler
->OnBufferCreated(
337 client
->controller_id
, remote_handle
, memory_size
, buffer_id
);
341 client
->event_handler
->OnBufferReady(client
->controller_id
,
345 const bool inserted
=
346 client
->active_buffers
.insert(std::make_pair(buffer_id
, frame
))
348 DCHECK(inserted
) << "Unexpected duplicate buffer: " << buffer_id
;
353 if (!has_received_frames_
) {
354 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Width",
355 frame
->visible_rect().width());
356 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.Height",
357 frame
->visible_rect().height());
358 UMA_HISTOGRAM_ASPECT_RATIO("Media.VideoCapture.AspectRatio",
359 frame
->visible_rect().width(),
360 frame
->visible_rect().height());
361 double frame_rate
= 0.0f
;
362 if (!frame
->metadata()->GetDouble(VideoFrameMetadata::FRAME_RATE
,
364 frame_rate
= video_capture_format_
.frame_rate
;
366 UMA_HISTOGRAM_COUNTS("Media.VideoCapture.FrameRate", frame_rate
);
367 has_received_frames_
= true;
370 buffer_pool_
->HoldForConsumers(buffer_id
, count
);
373 void VideoCaptureController::DoErrorOnIOThread() {
374 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
375 state_
= VIDEO_CAPTURE_STATE_ERROR
;
377 for (const auto* client
: controller_clients_
) {
378 if (client
->session_closed
)
380 client
->event_handler
->OnError(client
->controller_id
);
384 void VideoCaptureController::DoLogOnIOThread(const std::string
& message
) {
385 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
386 MediaStreamManager::SendMessageToNativeLog("Video capture: " + message
);
389 void VideoCaptureController::DoBufferDestroyedOnIOThread(
390 int buffer_id_to_drop
) {
391 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
393 for (auto* client
: controller_clients_
) {
394 if (client
->session_closed
)
397 if (client
->known_buffers
.erase(buffer_id_to_drop
)) {
398 client
->event_handler
->OnBufferDestroyed(client
->controller_id
,
404 VideoCaptureController::ControllerClient
* VideoCaptureController::FindClient(
405 VideoCaptureControllerID id
,
406 VideoCaptureControllerEventHandler
* handler
,
407 const ControllerClients
& clients
) {
408 for (auto* client
: clients
) {
409 if (client
->controller_id
== id
&& client
->event_handler
== handler
)
415 VideoCaptureController::ControllerClient
* VideoCaptureController::FindClient(
417 const ControllerClients
& clients
) {
418 for (auto client
: clients
) {
419 if (client
->session_id
== session_id
)
425 int VideoCaptureController::GetClientCount() const {
426 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
427 return controller_clients_
.size();
430 int VideoCaptureController::GetActiveClientCount() const {
431 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
432 int active_client_count
= 0;
433 for (ControllerClient
* client
: controller_clients_
) {
435 ++active_client_count
;
437 return active_client_count
;
440 } // namespace content