Don't send a SHChangeNotify for creating an app icon when creating a shortcut.
[chromium-blink-merge.git] / content / renderer / media / video_capture_impl.cc
blob9b104d28a7a1dd0517843d55e1a18f10a0fd6d13
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 "content/child/child_process.h"
17 #include "content/common/media/video_capture_messages.h"
18 #include "media/base/bind_to_current_loop.h"
19 #include "media/base/limits.h"
20 #include "media/base/video_frame.h"
22 namespace content {
24 class VideoCaptureImpl::ClientBuffer
25 : public base::RefCountedThreadSafe<ClientBuffer> {
26 public:
27 ClientBuffer(scoped_ptr<base::SharedMemory> buffer,
28 size_t buffer_size)
29 : buffer(buffer.Pass()),
30 buffer_size(buffer_size) {}
31 const scoped_ptr<base::SharedMemory> buffer;
32 const size_t buffer_size;
34 private:
35 friend class base::RefCountedThreadSafe<ClientBuffer>;
37 virtual ~ClientBuffer() {}
39 DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
42 VideoCaptureImpl::ClientInfo::ClientInfo() {}
43 VideoCaptureImpl::ClientInfo::~ClientInfo() {}
45 VideoCaptureImpl::VideoCaptureImpl(
46 const media::VideoCaptureSessionId session_id,
47 VideoCaptureMessageFilter* filter)
48 : message_filter_(filter),
49 device_id_(0),
50 session_id_(session_id),
51 suspended_(false),
52 state_(VIDEO_CAPTURE_STATE_STOPPED),
53 weak_factory_(this) {
54 DCHECK(filter);
57 VideoCaptureImpl::~VideoCaptureImpl() {
58 DCHECK(io_message_loop_->BelongsToCurrentThread());
61 void VideoCaptureImpl::Init() {
62 // For creating callbacks in unittest, this class may be constructed from a
63 // different thread than the IO thread, e.g. wherever unittest runs on.
64 // Therefore, this function should define the thread ownership.
65 #if DCHECK_IS_ON()
66 io_message_loop_ = base::MessageLoopProxy::current();
67 #endif
68 message_filter_->AddDelegate(this);
71 void VideoCaptureImpl::DeInit() {
72 DCHECK(io_message_loop_->BelongsToCurrentThread());
73 if (state_ == VIDEO_CAPTURE_STATE_STARTED)
74 Send(new VideoCaptureHostMsg_Stop(device_id_));
75 message_filter_->RemoveDelegate(this);
78 void VideoCaptureImpl::SuspendCapture(bool suspend) {
79 DCHECK(io_message_loop_->BelongsToCurrentThread());
80 Send(suspend ?
81 static_cast<IPC::Message*>(new VideoCaptureHostMsg_Pause(device_id_)) :
82 static_cast<IPC::Message*>(
83 new VideoCaptureHostMsg_Resume(device_id_, session_id_, params_)));
86 void VideoCaptureImpl::StartCapture(
87 int client_id,
88 const media::VideoCaptureParams& params,
89 const VideoCaptureStateUpdateCB& state_update_cb,
90 const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
91 DCHECK(io_message_loop_->BelongsToCurrentThread());
92 ClientInfo client_info;
93 client_info.params = params;
94 client_info.state_update_cb = state_update_cb;
95 client_info.deliver_frame_cb = deliver_frame_cb;
97 if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
98 state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
99 } else if (clients_pending_on_filter_.count(client_id) ||
100 clients_pending_on_restart_.count(client_id) ||
101 clients_.count(client_id)) {
102 LOG(FATAL) << "This client has already started.";
103 } else if (!device_id_) {
104 clients_pending_on_filter_[client_id] = client_info;
105 } else {
106 // Note: |state_| might not be started at this point. But we tell
107 // client that we have started.
108 state_update_cb.Run(VIDEO_CAPTURE_STATE_STARTED);
109 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
110 clients_[client_id] = client_info;
111 // TODO(sheu): Allowing resolution change will require that all
112 // outstanding clients of a capture session support resolution change.
113 DCHECK_EQ(params_.resolution_change_policy,
114 params.resolution_change_policy);
115 } else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
116 clients_pending_on_restart_[client_id] = client_info;
117 DVLOG(1) << "StartCapture: Got new resolution "
118 << params.requested_format.frame_size.ToString()
119 << " during stopping.";
120 } else {
121 clients_[client_id] = client_info;
122 if (state_ == VIDEO_CAPTURE_STATE_STARTED)
123 return;
124 params_ = params;
125 if (params_.requested_format.frame_rate >
126 media::limits::kMaxFramesPerSecond) {
127 params_.requested_format.frame_rate =
128 media::limits::kMaxFramesPerSecond;
130 DVLOG(1) << "StartCapture: starting with first resolution "
131 << params_.requested_format.frame_size.ToString();
132 first_frame_timestamp_ = base::TimeTicks();
133 StartCaptureInternal();
138 void VideoCaptureImpl::StopCapture(int client_id) {
139 DCHECK(io_message_loop_->BelongsToCurrentThread());
141 // A client ID can be in only one client list.
142 // If this ID is in any client list, we can just remove it from
143 // that client list and don't have to run the other following RemoveClient().
144 if (!RemoveClient(client_id, &clients_pending_on_filter_)) {
145 if (!RemoveClient(client_id, &clients_pending_on_restart_)) {
146 RemoveClient(client_id, &clients_);
150 if (clients_.empty()) {
151 DVLOG(1) << "StopCapture: No more client, stopping ...";
152 StopDevice();
153 client_buffers_.clear();
154 weak_factory_.InvalidateWeakPtrs();
158 void VideoCaptureImpl::GetDeviceSupportedFormats(
159 const VideoCaptureDeviceFormatsCB& callback) {
160 DCHECK(io_message_loop_->BelongsToCurrentThread());
161 device_formats_cb_queue_.push_back(callback);
162 if (device_formats_cb_queue_.size() == 1)
163 Send(new VideoCaptureHostMsg_GetDeviceSupportedFormats(device_id_,
164 session_id_));
167 void VideoCaptureImpl::GetDeviceFormatsInUse(
168 const VideoCaptureDeviceFormatsCB& callback) {
169 DCHECK(io_message_loop_->BelongsToCurrentThread());
170 device_formats_in_use_cb_queue_.push_back(callback);
171 if (device_formats_in_use_cb_queue_.size() == 1)
172 Send(
173 new VideoCaptureHostMsg_GetDeviceFormatsInUse(device_id_, session_id_));
176 void VideoCaptureImpl::OnBufferCreated(
177 base::SharedMemoryHandle handle,
178 int length, int buffer_id) {
179 DCHECK(io_message_loop_->BelongsToCurrentThread());
181 // In case client calls StopCapture before the arrival of created buffer,
182 // just close this buffer and return.
183 if (state_ != VIDEO_CAPTURE_STATE_STARTED) {
184 base::SharedMemory::CloseHandle(handle);
185 return;
188 scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false));
189 if (!shm->Map(length)) {
190 DLOG(ERROR) << "OnBufferCreated: Map failed.";
191 return;
194 bool inserted =
195 client_buffers_.insert(std::make_pair(
196 buffer_id,
197 new ClientBuffer(shm.Pass(),
198 length))).second;
199 DCHECK(inserted);
202 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
203 DCHECK(io_message_loop_->BelongsToCurrentThread());
205 const ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
206 if (iter == client_buffers_.end())
207 return;
209 DCHECK(!iter->second.get() || iter->second->HasOneRef())
210 << "Instructed to delete buffer we are still using.";
211 client_buffers_.erase(iter);
214 void VideoCaptureImpl::OnBufferReceived(int buffer_id,
215 const gfx::Size& coded_size,
216 const gfx::Rect& visible_rect,
217 base::TimeTicks timestamp,
218 const base::DictionaryValue& metadata) {
219 DCHECK(io_message_loop_->BelongsToCurrentThread());
221 if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
222 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id, 0));
223 return;
226 if (first_frame_timestamp_.is_null())
227 first_frame_timestamp_ = timestamp;
229 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
230 TRACE_EVENT_INSTANT2(
231 "cast_perf_test", "OnBufferReceived",
232 TRACE_EVENT_SCOPE_THREAD,
233 "timestamp", timestamp.ToInternalValue(),
234 "time_delta", (timestamp - first_frame_timestamp_).ToInternalValue());
236 const ClientBufferMap::const_iterator iter = client_buffers_.find(buffer_id);
237 DCHECK(iter != client_buffers_.end());
238 scoped_refptr<ClientBuffer> buffer = iter->second;
239 scoped_refptr<media::VideoFrame> frame =
240 media::VideoFrame::WrapExternalPackedMemory(
241 media::VideoFrame::I420,
242 coded_size,
243 visible_rect,
244 gfx::Size(visible_rect.width(), visible_rect.height()),
245 reinterpret_cast<uint8*>(buffer->buffer->memory()),
246 buffer->buffer_size,
247 buffer->buffer->handle(),
249 timestamp - first_frame_timestamp_,
250 media::BindToCurrentLoop(
251 base::Bind(&VideoCaptureImpl::OnClientBufferFinished,
252 weak_factory_.GetWeakPtr(),
253 buffer_id,
254 buffer,
255 0)));
256 frame->metadata()->MergeInternalValuesFrom(metadata);
258 for (const auto& client : clients_)
259 client.second.deliver_frame_cb.Run(frame, timestamp);
262 void VideoCaptureImpl::OnMailboxBufferReceived(
263 int buffer_id,
264 const gpu::MailboxHolder& mailbox_holder,
265 const gfx::Size& packed_frame_size,
266 base::TimeTicks timestamp,
267 const base::DictionaryValue& metadata) {
268 DCHECK(io_message_loop_->BelongsToCurrentThread());
270 if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
271 Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id, 0));
272 return;
275 if (first_frame_timestamp_.is_null())
276 first_frame_timestamp_ = timestamp;
278 scoped_refptr<media::VideoFrame> frame = media::VideoFrame::WrapNativeTexture(
279 make_scoped_ptr(new gpu::MailboxHolder(mailbox_holder)),
280 media::BindToCurrentLoop(base::Bind(
281 &VideoCaptureImpl::OnClientBufferFinished, weak_factory_.GetWeakPtr(),
282 buffer_id, scoped_refptr<ClientBuffer>())),
283 packed_frame_size, gfx::Rect(packed_frame_size), packed_frame_size,
284 timestamp - first_frame_timestamp_, false);
285 frame->metadata()->MergeInternalValuesFrom(metadata);
287 for (const auto& client : clients_)
288 client.second.deliver_frame_cb.Run(frame, timestamp);
291 void VideoCaptureImpl::OnClientBufferFinished(
292 int buffer_id,
293 const scoped_refptr<ClientBuffer>& /* ignored_buffer */,
294 uint32 release_sync_point) {
295 DCHECK(io_message_loop_->BelongsToCurrentThread());
296 Send(new VideoCaptureHostMsg_BufferReady(
297 device_id_, buffer_id, release_sync_point));
300 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
301 DCHECK(io_message_loop_->BelongsToCurrentThread());
303 switch (state) {
304 case VIDEO_CAPTURE_STATE_STARTED:
305 // Camera has started in the browser process. Since we have already
306 // told all clients that we have started there's nothing to do.
307 break;
308 case VIDEO_CAPTURE_STATE_STOPPED:
309 state_ = VIDEO_CAPTURE_STATE_STOPPED;
310 DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_;
311 client_buffers_.clear();
312 weak_factory_.InvalidateWeakPtrs();
313 if (!clients_.empty() || !clients_pending_on_restart_.empty())
314 RestartCapture();
315 break;
316 case VIDEO_CAPTURE_STATE_PAUSED:
317 for (const auto& client : clients_)
318 client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_PAUSED);
319 break;
320 case VIDEO_CAPTURE_STATE_ERROR:
321 DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
322 for (const auto& client : clients_)
323 client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
324 clients_.clear();
325 state_ = VIDEO_CAPTURE_STATE_ERROR;
326 break;
327 case VIDEO_CAPTURE_STATE_ENDED:
328 DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
329 for (const auto& client : clients_) {
330 // We'll only notify the client that the stream has stopped.
331 client.second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
333 clients_.clear();
334 state_ = VIDEO_CAPTURE_STATE_ENDED;
335 break;
336 default:
337 break;
341 void VideoCaptureImpl::OnDeviceSupportedFormatsEnumerated(
342 const media::VideoCaptureFormats& supported_formats) {
343 DCHECK(io_message_loop_->BelongsToCurrentThread());
344 for (size_t i = 0; i < device_formats_cb_queue_.size(); ++i)
345 device_formats_cb_queue_[i].Run(supported_formats);
346 device_formats_cb_queue_.clear();
349 void VideoCaptureImpl::OnDeviceFormatsInUseReceived(
350 const media::VideoCaptureFormats& formats_in_use) {
351 DCHECK(io_message_loop_->BelongsToCurrentThread());
352 for (size_t i = 0; i < device_formats_in_use_cb_queue_.size(); ++i)
353 device_formats_in_use_cb_queue_[i].Run(formats_in_use);
354 device_formats_in_use_cb_queue_.clear();
357 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
358 DCHECK(io_message_loop_->BelongsToCurrentThread());
359 DVLOG(1) << "OnDelegateAdded: device_id " << device_id;
361 device_id_ = device_id;
362 ClientInfoMap::iterator it = clients_pending_on_filter_.begin();
363 while (it != clients_pending_on_filter_.end()) {
364 const int client_id = it->first;
365 const ClientInfo client_info = it->second;
366 clients_pending_on_filter_.erase(it++);
367 StartCapture(client_id, client_info.params, client_info.state_update_cb,
368 client_info.deliver_frame_cb);
372 void VideoCaptureImpl::StopDevice() {
373 DCHECK(io_message_loop_->BelongsToCurrentThread());
375 if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
376 state_ = VIDEO_CAPTURE_STATE_STOPPING;
377 Send(new VideoCaptureHostMsg_Stop(device_id_));
378 params_.requested_format.frame_size.SetSize(0, 0);
382 void VideoCaptureImpl::RestartCapture() {
383 DCHECK(io_message_loop_->BelongsToCurrentThread());
384 DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
386 int width = 0;
387 int height = 0;
388 clients_.insert(clients_pending_on_restart_.begin(),
389 clients_pending_on_restart_.end());
390 clients_pending_on_restart_.clear();
391 for (const auto& client : clients_) {
392 width = std::max(width,
393 client.second.params.requested_format.frame_size.width());
394 height = std::max(
395 height, client.second.params.requested_format.frame_size.height());
397 params_.requested_format.frame_size.SetSize(width, height);
398 DVLOG(1) << "RestartCapture, "
399 << params_.requested_format.frame_size.ToString();
400 StartCaptureInternal();
403 void VideoCaptureImpl::StartCaptureInternal() {
404 DCHECK(io_message_loop_->BelongsToCurrentThread());
405 DCHECK(device_id_);
407 Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, params_));
408 state_ = VIDEO_CAPTURE_STATE_STARTED;
411 void VideoCaptureImpl::Send(IPC::Message* message) {
412 DCHECK(io_message_loop_->BelongsToCurrentThread());
413 message_filter_->Send(message);
416 bool VideoCaptureImpl::RemoveClient(int client_id, ClientInfoMap* clients) {
417 DCHECK(io_message_loop_->BelongsToCurrentThread());
418 bool found = false;
420 const ClientInfoMap::iterator it = clients->find(client_id);
421 if (it != clients->end()) {
422 it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
423 clients->erase(it);
424 found = true;
426 return found;
429 } // namespace content