[MacViews] Show comboboxes with a native NSMenu
[chromium-blink-merge.git] / content / renderer / media / video_capture_impl.cc
blobcee17f936899873cc51045439ac2fd0bc29bf84a
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 // Notes about usage of this object by VideoCaptureImplManager.
6 //
7 // VideoCaptureImplManager access this object by using a Unretained()
8 // binding and tasks on the IO thread. It is then important that
9 // VideoCaptureImpl never post task to itself. All operations must be
10 // synchronous.
12 #include "content/renderer/media/video_capture_impl.h"
14 #include "base/bind.h"
15 #include "base/stl_util.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "content/child/child_process.h"
18 #include "content/common/gpu/client/gpu_memory_buffer_impl.h"
19 #include "content/common/media/video_capture_messages.h"
20 #include "media/base/bind_to_current_loop.h"
21 #include "media/base/limits.h"
22 #include "media/base/video_frame.h"
24 namespace content {
26 namespace {
28 const int kUndefinedDeviceId = 0;
30 // This is called on an unknown thread when the VideoFrame destructor executes.
31 // As of this writing, this callback mechanism is the only interface in
32 // VideoFrame to provide the final value for |release_sync_point|.
33 // VideoCaptureImpl::DidFinishConsumingFrame() will read the value saved here,
34 // and pass it back to the IO thread to pass back to the host via the
35 // BufferReady IPC.
36 void SaveReleaseSyncPoint(uint32* storage, uint32 release_sync_point) {
37 *storage = release_sync_point;
40 } // namespace
42 // A holder of a memory-backed buffer and accessors to it.
43 class VideoCaptureImpl::ClientBuffer
44 : public base::RefCountedThreadSafe<ClientBuffer> {
45 public:
46 ClientBuffer(scoped_ptr<base::SharedMemory> buffer, size_t buffer_size)
47 : buffer_(buffer.Pass()), buffer_size_(buffer_size) {}
49 base::SharedMemory* buffer() const { return buffer_.get(); }
50 size_t buffer_size() const { return buffer_size_; }
52 private:
53 friend class base::RefCountedThreadSafe<ClientBuffer>;
55 virtual ~ClientBuffer() {}
57 const scoped_ptr<base::SharedMemory> buffer_;
58 const size_t buffer_size_;
60 DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
63 // A holder of a GpuMemoryBuffer-backed buffer, Map()ed on ctor and Unmap()ed on
64 // dtor. Creates and owns GpuMemoryBuffer instances.
65 class VideoCaptureImpl::ClientBuffer2
66 : public base::RefCountedThreadSafe<ClientBuffer2> {
67 public:
68 ClientBuffer2(
69 const std::vector<gfx::GpuMemoryBufferHandle>& client_handles,
70 const gfx::Size& size)
71 : handles_(client_handles),
72 size_(size) {
73 const media::VideoPixelFormat format = media::PIXEL_FORMAT_I420;
74 for (size_t i = 0; i < handles_.size(); ++i) {
75 const size_t width = media::VideoFrame::Columns(i, format, size_.width());
76 const size_t height = media::VideoFrame::Rows(i, format, size_.height());
77 buffers_.push_back(GpuMemoryBufferImpl::CreateFromHandle(
78 handles_[i],
79 gfx::Size(width, height),
80 gfx::BufferFormat::R_8,
81 gfx::BufferUsage::MAP,
82 base::Bind(&ClientBuffer2::DestroyGpuMemoryBuffer,
83 base::Unretained(this))));
84 void* data_ptr = nullptr;
85 buffers_[i]->Map(&data_ptr);
86 data_[i] = reinterpret_cast<uint8*>(data_ptr);
87 strides_[i] = width;
91 uint8* data(int plane) const { return data_[plane]; }
92 int32 stride(int plane) const { return strides_[plane]; }
93 std::vector<gfx::GpuMemoryBufferHandle> gpu_memory_buffer_handles() {
94 return handles_;
97 private:
98 friend class base::RefCountedThreadSafe<ClientBuffer2>;
100 virtual ~ClientBuffer2() {
101 for (auto& buffer : buffers_)
102 buffer->Unmap();
105 void DestroyGpuMemoryBuffer(uint32 sync_point) {}
107 const std::vector<gfx::GpuMemoryBufferHandle> handles_;
108 const gfx::Size size_;
109 ScopedVector<gfx::GpuMemoryBuffer> buffers_;
110 uint8* data_[media::VideoFrame::kMaxPlanes];
111 int32 strides_[media::VideoFrame::kMaxPlanes];
113 DISALLOW_COPY_AND_ASSIGN(ClientBuffer2);
116 VideoCaptureImpl::VideoCaptureImpl(
117 const media::VideoCaptureSessionId session_id,
118 VideoCaptureMessageFilter* filter)
119 : message_filter_(filter),
120 device_id_(kUndefinedDeviceId),
121 session_id_(session_id),
122 state_(VIDEO_CAPTURE_STATE_STOPPED),
123 weak_factory_(this) {
124 DCHECK(filter);
127 VideoCaptureImpl::~VideoCaptureImpl() {
128 DCHECK(io_task_runner_->BelongsToCurrentThread());
131 void VideoCaptureImpl::Init() {
132 // For creating callbacks in unittest, this class may be constructed from a
133 // different thread than the IO thread, e.g. wherever unittest runs on.
134 // Therefore, this function should define the thread ownership.
135 #if DCHECK_IS_ON()
136 io_task_runner_ = base::ThreadTaskRunnerHandle::Get();
137 #endif
138 message_filter_->AddDelegate(this);
141 void VideoCaptureImpl::DeInit() {
142 DCHECK(io_task_runner_->BelongsToCurrentThread());
143 if (state_ == VIDEO_CAPTURE_STATE_STARTED)
144 Send(new VideoCaptureHostMsg_Stop(device_id_));
145 message_filter_->RemoveDelegate(this);
148 void VideoCaptureImpl::SuspendCapture(bool suspend) {
149 DCHECK(io_task_runner_->BelongsToCurrentThread());
150 Send(suspend ? static_cast<IPC::Message*>(
151 new VideoCaptureHostMsg_Pause(device_id_))
152 : static_cast<IPC::Message*>(new VideoCaptureHostMsg_Resume(
153 device_id_, session_id_, client_params_)));
156 void VideoCaptureImpl::StartCapture(
157 const media::VideoCaptureParams& params,
158 const VideoCaptureStateUpdateCB& state_update_cb,
159 const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
160 DCHECK(io_task_runner_->BelongsToCurrentThread());
161 if (state_update_cb.is_null() || deliver_frame_cb.is_null()) {
162 DVLOG(1) << "StartCapture: Passed null callbacks.";
163 return;
165 if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
166 state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
167 return;
170 // Save client info regardless of whether we're actually starting capture
171 // now or not, in case a delegate hasn't yet been added.
172 client_params_ = params;
173 state_update_cb_ = state_update_cb;
174 deliver_frame_cb_ = deliver_frame_cb;
175 client_params_.requested_format.frame_rate =
176 std::min(client_params_.requested_format.frame_rate,
177 static_cast<float>(media::limits::kMaxFramesPerSecond));
179 // Postpone starting capture until OnDelegateAdded(). Do this after saving
180 // |client_params_|, etc. as StartCapture will be called with these saved
181 // values as soon as a delegate has been added
182 if (device_id_ == kUndefinedDeviceId) {
183 DVLOG(1) << "StartCapture: Not starting, device_id_ is undefined.";
184 return;
187 // Finally, we can start (assuming we haven't started already).
188 // Notify the client that we have started regardless of |state_|.
189 state_update_cb.Run(VIDEO_CAPTURE_STATE_STARTED);
190 if (state_ == VIDEO_CAPTURE_STATE_STARTED)
191 return;
192 DVLOG(1) << "StartCapture: starting with resolution "
193 << client_params_.requested_format.frame_size.ToString();
194 StartCaptureInternal();
197 void VideoCaptureImpl::StopCapture() {
198 DCHECK(io_task_runner_->BelongsToCurrentThread());
199 if (state_ == VIDEO_CAPTURE_STATE_STOPPED || !IsInitialized())
200 return;
202 DVLOG(1) << "StopCapture: Stopping capture.";
203 state_update_cb_.Run(VIDEO_CAPTURE_STATE_STOPPED);
204 StopDevice();
205 client_buffers_.clear();
206 client_buffer2s_.clear();
207 ResetClient();
208 weak_factory_.InvalidateWeakPtrs();
211 void VideoCaptureImpl::GetDeviceSupportedFormats(
212 const VideoCaptureDeviceFormatsCB& callback) {
213 DCHECK(io_task_runner_->BelongsToCurrentThread());
214 device_formats_cb_queue_.push_back(callback);
215 if (device_formats_cb_queue_.size() == 1)
216 Send(new VideoCaptureHostMsg_GetDeviceSupportedFormats(device_id_,
217 session_id_));
220 void VideoCaptureImpl::GetDeviceFormatsInUse(
221 const VideoCaptureDeviceFormatsCB& callback) {
222 DCHECK(io_task_runner_->BelongsToCurrentThread());
223 device_formats_in_use_cb_queue_.push_back(callback);
224 if (device_formats_in_use_cb_queue_.size() == 1)
225 Send(
226 new VideoCaptureHostMsg_GetDeviceFormatsInUse(device_id_, session_id_));
229 void VideoCaptureImpl::OnBufferCreated(base::SharedMemoryHandle handle,
230 int length,
231 int buffer_id) {
232 DCHECK(io_task_runner_->BelongsToCurrentThread());
234 // In case client calls StopCapture before the arrival of created buffer,
235 // just close this buffer and return.
236 if (state_ != VIDEO_CAPTURE_STATE_STARTED) {
237 base::SharedMemory::CloseHandle(handle);
238 return;
241 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false));
242 if (!shm->Map(length)) {
243 DLOG(ERROR) << "OnBufferCreated: Map failed.";
244 return;
246 const bool inserted =
247 client_buffers_.insert(std::make_pair(buffer_id, new ClientBuffer(
248 shm.Pass(), length)))
249 .second;
250 DCHECK(inserted);
253 void VideoCaptureImpl::OnBufferCreated2(
254 const std::vector<gfx::GpuMemoryBufferHandle>& handles,
255 const gfx::Size& size,
256 int buffer_id) {
257 DCHECK(io_task_runner_->BelongsToCurrentThread());
259 // In case client calls StopCapture before the arrival of created buffer,
260 // just close this buffer and return.
261 if (state_ != VIDEO_CAPTURE_STATE_STARTED)
262 return;
264 const bool inserted =
265 client_buffer2s_.insert(std::make_pair(buffer_id,
266 new ClientBuffer2(handles, size)))
267 .second;
268 DCHECK(inserted);
271 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
272 DCHECK(io_task_runner_->BelongsToCurrentThread());
274 const auto& cb_iter = client_buffers_.find(buffer_id);
275 if (cb_iter != client_buffers_.end()) {
276 DCHECK(!cb_iter->second.get() || cb_iter->second->HasOneRef())
277 << "Instructed to delete buffer we are still using.";
278 client_buffers_.erase(cb_iter);
279 } else {
280 const auto& cb2_iter = client_buffer2s_.find(buffer_id);
281 if (cb2_iter != client_buffer2s_.end()) {
282 DCHECK(!cb2_iter->second.get() || cb2_iter->second->HasOneRef())
283 << "Instructed to delete buffer we are still using.";
284 client_buffer2s_.erase(cb2_iter);
289 void VideoCaptureImpl::OnBufferReceived(
290 int buffer_id,
291 base::TimeTicks timestamp,
292 const base::DictionaryValue& metadata,
293 media::VideoPixelFormat pixel_format,
294 media::VideoFrame::StorageType storage_type,
295 const gfx::Size& coded_size,
296 const gfx::Rect& visible_rect,
297 const std::vector<gpu::MailboxHolder>& mailbox_holders) {
298 DCHECK(io_task_runner_->BelongsToCurrentThread());
299 if (state_ != VIDEO_CAPTURE_STATE_STARTED) {
300 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id, 0, -1.0));
301 return;
303 if (first_frame_timestamp_.is_null())
304 first_frame_timestamp_ = timestamp;
306 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
307 TRACE_EVENT_INSTANT2("cast_perf_test", "OnBufferReceived",
308 TRACE_EVENT_SCOPE_THREAD, "timestamp",
309 timestamp.ToInternalValue(), "time_delta",
310 (timestamp - first_frame_timestamp_).ToInternalValue());
312 scoped_refptr<media::VideoFrame> frame;
313 base::Callback<void(uint32, double)> buffer_finished_callback;
314 uint32* release_sync_point_storage = new uint32(0);
315 if (mailbox_holders.empty()) {
316 DCHECK_EQ(media::PIXEL_FORMAT_I420, pixel_format);
317 const auto& iter = client_buffers_.find(buffer_id);
318 DCHECK(iter != client_buffers_.end());
319 const scoped_refptr<ClientBuffer> buffer = iter->second;
320 frame = media::VideoFrame::WrapExternalSharedMemory(
321 pixel_format,
322 coded_size,
323 visible_rect,
324 gfx::Size(visible_rect.width(), visible_rect.height()),
325 reinterpret_cast<uint8*>(buffer->buffer()->memory()),
326 buffer->buffer_size(),
327 buffer->buffer()->handle(),
328 0 /* shared_memory_offset */,
329 timestamp - first_frame_timestamp_);
330 buffer_finished_callback = media::BindToCurrentLoop(
331 base::Bind(&VideoCaptureImpl::OnClientBufferFinished,
332 weak_factory_.GetWeakPtr(), buffer_id, buffer));
333 } else {
334 #if DCHECK_IS_ON()
335 for (const auto& mailbox_holder : mailbox_holders)
336 DCHECK(mailbox_holder.mailbox.Verify());
337 DCHECK(mailbox_holders.size() == 1u || mailbox_holders.size() == 3u);
338 #endif
340 scoped_refptr<ClientBuffer2> buffer;
341 if (mailbox_holders.size() ==
342 media::VideoFrame::NumPlanes(media::PIXEL_FORMAT_ARGB)) {
343 DCHECK_EQ(media::PIXEL_FORMAT_ARGB, pixel_format);
344 frame = media::VideoFrame::WrapNativeTexture(
345 pixel_format,
346 mailbox_holders[0],
347 base::Bind(&SaveReleaseSyncPoint, release_sync_point_storage),
348 coded_size,
349 gfx::Rect(coded_size),
350 coded_size,
351 timestamp - first_frame_timestamp_);
352 } else if (mailbox_holders.size() ==
353 media::VideoFrame::NumPlanes(media::PIXEL_FORMAT_I420)) {
354 DCHECK_EQ(media::PIXEL_FORMAT_I420, pixel_format);
355 const auto& iter = client_buffer2s_.find(buffer_id);
356 DCHECK(iter != client_buffer2s_.end());
357 buffer = iter->second;
358 frame = media::VideoFrame::WrapYUV420NativeTextures(
359 mailbox_holders[media::VideoFrame::kYPlane],
360 mailbox_holders[media::VideoFrame::kUPlane],
361 mailbox_holders[media::VideoFrame::kVPlane],
362 base::Bind(&SaveReleaseSyncPoint, release_sync_point_storage),
363 coded_size,
364 gfx::Rect(coded_size),
365 coded_size,
366 timestamp - first_frame_timestamp_);
368 buffer_finished_callback = media::BindToCurrentLoop(
369 base::Bind(&VideoCaptureImpl::OnClientBufferFinished2,
370 weak_factory_.GetWeakPtr(), buffer_id, buffer));
372 frame->AddDestructionObserver(
373 base::Bind(&VideoCaptureImpl::DidFinishConsumingFrame,
374 frame->metadata(), release_sync_point_storage,
375 buffer_finished_callback));
376 frame->metadata()->MergeInternalValuesFrom(metadata);
377 deliver_frame_cb_.Run(frame, timestamp);
380 void VideoCaptureImpl::OnClientBufferFinished(
381 int buffer_id,
382 const scoped_refptr<ClientBuffer>& /* ignored_buffer */,
383 uint32 release_sync_point,
384 double consumer_resource_utilization) {
385 DCHECK(io_task_runner_->BelongsToCurrentThread());
386 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id,
387 release_sync_point,
388 consumer_resource_utilization));
390 void VideoCaptureImpl::OnClientBufferFinished2(
391 int buffer_id,
392 const scoped_refptr<ClientBuffer2>& gpu_memory_buffer /* ignored_buffer */,
393 uint32 release_sync_point,
394 double consumer_resource_utilization) {
395 OnClientBufferFinished(buffer_id, scoped_refptr<ClientBuffer>(),
396 release_sync_point, consumer_resource_utilization);
399 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
400 // TODO(ajose): http://crbug.com/522155 improve this state machine.
401 DCHECK(io_task_runner_->BelongsToCurrentThread());
402 state_ = state;
404 if (state == VIDEO_CAPTURE_STATE_STOPPED) {
405 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_;
406 client_buffers_.clear();
407 client_buffer2s_.clear();
408 weak_factory_.InvalidateWeakPtrs();
409 return;
411 if (state == VIDEO_CAPTURE_STATE_ERROR) {
412 DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
413 if (!state_update_cb_.is_null())
414 state_update_cb_.Run(VIDEO_CAPTURE_STATE_ERROR);
415 ResetClient();
416 return;
418 if (state == VIDEO_CAPTURE_STATE_ENDED) {
419 DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
420 // We'll only notify the client that the stream has stopped.
421 if (!state_update_cb_.is_null())
422 state_update_cb_.Run(VIDEO_CAPTURE_STATE_STOPPED);
423 ResetClient();
424 return;
426 NOTREACHED();
429 void VideoCaptureImpl::OnDeviceSupportedFormatsEnumerated(
430 const media::VideoCaptureFormats& supported_formats) {
431 DCHECK(io_task_runner_->BelongsToCurrentThread());
432 for (size_t i = 0; i < device_formats_cb_queue_.size(); ++i)
433 device_formats_cb_queue_[i].Run(supported_formats);
434 device_formats_cb_queue_.clear();
437 void VideoCaptureImpl::OnDeviceFormatsInUseReceived(
438 const media::VideoCaptureFormats& formats_in_use) {
439 DCHECK(io_task_runner_->BelongsToCurrentThread());
440 for (size_t i = 0; i < device_formats_in_use_cb_queue_.size(); ++i)
441 device_formats_in_use_cb_queue_[i].Run(formats_in_use);
442 device_formats_in_use_cb_queue_.clear();
445 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
446 DCHECK(io_task_runner_->BelongsToCurrentThread());
447 DVLOG(1) << "OnDelegateAdded: device_id " << device_id;
449 device_id_ = device_id;
450 StartCapture(client_params_, state_update_cb_, deliver_frame_cb_);
453 void VideoCaptureImpl::StopDevice() {
454 DCHECK(io_task_runner_->BelongsToCurrentThread());
456 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
457 Send(new VideoCaptureHostMsg_Stop(device_id_));
458 client_params_.requested_format.frame_size.SetSize(0, 0);
462 void VideoCaptureImpl::RestartCapture() {
463 DCHECK(io_task_runner_->BelongsToCurrentThread());
464 DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
466 DVLOG(1) << "RestartCapture, restarting capture.";
467 StartCaptureInternal();
470 void VideoCaptureImpl::StartCaptureInternal() {
471 DCHECK(io_task_runner_->BelongsToCurrentThread());
472 DCHECK(device_id_);
474 Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, client_params_));
475 state_ = VIDEO_CAPTURE_STATE_STARTED;
478 void VideoCaptureImpl::Send(IPC::Message* message) {
479 DCHECK(io_task_runner_->BelongsToCurrentThread());
480 message_filter_->Send(message);
483 // static
484 void VideoCaptureImpl::DidFinishConsumingFrame(
485 const media::VideoFrameMetadata* metadata,
486 uint32* release_sync_point_storage,
487 const base::Callback<void(uint32, double)>& callback_to_io_thread) {
488 // Note: This function may be called on any thread by the VideoFrame
489 // destructor. |metadata| is still valid for read-access at this point.
491 uint32 release_sync_point = 0u;
492 if (release_sync_point_storage) {
493 release_sync_point = *release_sync_point_storage;
494 delete release_sync_point_storage;
497 double consumer_resource_utilization = -1.0;
498 if (!metadata->GetDouble(media::VideoFrameMetadata::RESOURCE_UTILIZATION,
499 &consumer_resource_utilization)) {
500 consumer_resource_utilization = -1.0;
503 callback_to_io_thread.Run(release_sync_point, consumer_resource_utilization);
506 bool VideoCaptureImpl::IsInitialized() const {
507 return !(state_update_cb_.is_null() || deliver_frame_cb_.is_null()) &&
508 device_id_ != kUndefinedDeviceId;
511 void VideoCaptureImpl::ResetClient() {
512 client_params_ = media::VideoCaptureParams();
513 state_update_cb_.Reset();
514 deliver_frame_cb_.Reset();
515 first_frame_timestamp_ = base::TimeTicks();
516 device_id_ = kUndefinedDeviceId;
517 state_ = VIDEO_CAPTURE_STATE_STOPPED;
520 } // namespace content