• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
11 
12 #include "content/renderer/media/video_capture_impl.h"
13 
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"
21 
22 namespace content {
23 
24 class VideoCaptureImpl::ClientBuffer
25     : public base::RefCountedThreadSafe<ClientBuffer> {
26  public:
ClientBuffer(scoped_ptr<base::SharedMemory> buffer,size_t buffer_size)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;
33 
34  private:
35   friend class base::RefCountedThreadSafe<ClientBuffer>;
36 
~ClientBuffer()37   virtual ~ClientBuffer() {}
38 
39   DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
40 };
41 
ClientInfo()42 VideoCaptureImpl::ClientInfo::ClientInfo() {}
~ClientInfo()43 VideoCaptureImpl::ClientInfo::~ClientInfo() {}
44 
VideoCaptureImpl(const media::VideoCaptureSessionId session_id,VideoCaptureMessageFilter * filter)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);
55   thread_checker_.DetachFromThread();
56 }
57 
~VideoCaptureImpl()58 VideoCaptureImpl::~VideoCaptureImpl() {
59   DCHECK(thread_checker_.CalledOnValidThread());
60 }
61 
Init()62 void VideoCaptureImpl::Init() {
63   DCHECK(thread_checker_.CalledOnValidThread());
64   message_filter_->AddDelegate(this);
65 }
66 
DeInit()67 void VideoCaptureImpl::DeInit() {
68   DCHECK(thread_checker_.CalledOnValidThread());
69   if (state_ == VIDEO_CAPTURE_STATE_STARTED)
70     Send(new VideoCaptureHostMsg_Stop(device_id_));
71   message_filter_->RemoveDelegate(this);
72 }
73 
SuspendCapture(bool suspend)74 void VideoCaptureImpl::SuspendCapture(bool suspend) {
75   DCHECK(thread_checker_.CalledOnValidThread());
76   suspended_ = suspend;
77 }
78 
StartCapture(int client_id,const media::VideoCaptureParams & params,const VideoCaptureStateUpdateCB & state_update_cb,const VideoCaptureDeliverFrameCB & deliver_frame_cb)79 void VideoCaptureImpl::StartCapture(
80     int client_id,
81     const media::VideoCaptureParams& params,
82     const VideoCaptureStateUpdateCB& state_update_cb,
83     const VideoCaptureDeliverFrameCB& deliver_frame_cb) {
84   DCHECK(thread_checker_.CalledOnValidThread());
85   ClientInfo client_info;
86   client_info.params = params;
87   client_info.state_update_cb = state_update_cb;
88   client_info.deliver_frame_cb = deliver_frame_cb;
89 
90   if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
91     state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
92   } else if (clients_pending_on_filter_.count(client_id) ||
93              clients_pending_on_restart_.count(client_id) ||
94              clients_.count(client_id)) {
95     LOG(FATAL) << "This client has already started.";
96   } else if (!device_id_) {
97     clients_pending_on_filter_[client_id] = client_info;
98   } else {
99     // Note: |state_| might not be started at this point. But we tell
100     // client that we have started.
101     state_update_cb.Run(VIDEO_CAPTURE_STATE_STARTED);
102     if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
103       clients_[client_id] = client_info;
104       // TODO(sheu): Allowing resolution change will require that all
105       // outstanding clients of a capture session support resolution change.
106       DCHECK_EQ(params_.allow_resolution_change,
107                 params.allow_resolution_change);
108     } else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
109       clients_pending_on_restart_[client_id] = client_info;
110       DVLOG(1) << "StartCapture: Got new resolution "
111                << params.requested_format.frame_size.ToString()
112                << " during stopping.";
113     } else {
114       clients_[client_id] = client_info;
115       if (state_ == VIDEO_CAPTURE_STATE_STARTED)
116         return;
117       params_ = params;
118       if (params_.requested_format.frame_rate >
119           media::limits::kMaxFramesPerSecond) {
120         params_.requested_format.frame_rate =
121             media::limits::kMaxFramesPerSecond;
122       }
123       DVLOG(1) << "StartCapture: starting with first resolution "
124                << params_.requested_format.frame_size.ToString();
125       first_frame_timestamp_ = base::TimeTicks();
126       StartCaptureInternal();
127     }
128   }
129 }
130 
StopCapture(int client_id)131 void VideoCaptureImpl::StopCapture(int client_id) {
132   DCHECK(thread_checker_.CalledOnValidThread());
133 
134   // A client ID can be in only one client list.
135   // If this ID is in any client list, we can just remove it from
136   // that client list and don't have to run the other following RemoveClient().
137   if (!RemoveClient(client_id, &clients_pending_on_filter_)) {
138     if (!RemoveClient(client_id, &clients_pending_on_restart_)) {
139       RemoveClient(client_id, &clients_);
140     }
141   }
142 
143   if (clients_.empty()) {
144     DVLOG(1) << "StopCapture: No more client, stopping ...";
145     StopDevice();
146     client_buffers_.clear();
147     weak_factory_.InvalidateWeakPtrs();
148   }
149 }
150 
GetDeviceSupportedFormats(const VideoCaptureDeviceFormatsCB & callback)151 void VideoCaptureImpl::GetDeviceSupportedFormats(
152     const VideoCaptureDeviceFormatsCB& callback) {
153   DCHECK(thread_checker_.CalledOnValidThread());
154   device_formats_cb_queue_.push_back(callback);
155   if (device_formats_cb_queue_.size() == 1)
156     Send(new VideoCaptureHostMsg_GetDeviceSupportedFormats(device_id_,
157                                                            session_id_));
158 }
159 
GetDeviceFormatsInUse(const VideoCaptureDeviceFormatsCB & callback)160 void VideoCaptureImpl::GetDeviceFormatsInUse(
161     const VideoCaptureDeviceFormatsCB& callback) {
162   DCHECK(thread_checker_.CalledOnValidThread());
163   device_formats_in_use_cb_queue_.push_back(callback);
164   if (device_formats_in_use_cb_queue_.size() == 1)
165     Send(
166         new VideoCaptureHostMsg_GetDeviceFormatsInUse(device_id_, session_id_));
167 }
168 
OnBufferCreated(base::SharedMemoryHandle handle,int length,int buffer_id)169 void VideoCaptureImpl::OnBufferCreated(
170     base::SharedMemoryHandle handle,
171     int length, int buffer_id) {
172   DCHECK(thread_checker_.CalledOnValidThread());
173 
174   // In case client calls StopCapture before the arrival of created buffer,
175   // just close this buffer and return.
176   if (state_ != VIDEO_CAPTURE_STATE_STARTED) {
177     base::SharedMemory::CloseHandle(handle);
178     return;
179   }
180 
181   scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false));
182   if (!shm->Map(length)) {
183     DLOG(ERROR) << "OnBufferCreated: Map failed.";
184     return;
185   }
186 
187   bool inserted =
188       client_buffers_.insert(std::make_pair(
189                                  buffer_id,
190                                  new ClientBuffer(shm.Pass(),
191                                                   length))).second;
192   DCHECK(inserted);
193 }
194 
OnBufferDestroyed(int buffer_id)195 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
196   DCHECK(thread_checker_.CalledOnValidThread());
197 
198   ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
199   if (iter == client_buffers_.end())
200     return;
201 
202   DCHECK(!iter->second || iter->second->HasOneRef())
203       << "Instructed to delete buffer we are still using.";
204   client_buffers_.erase(iter);
205 }
206 
OnBufferReceived(int buffer_id,const media::VideoCaptureFormat & format,base::TimeTicks timestamp)207 void VideoCaptureImpl::OnBufferReceived(int buffer_id,
208                                         const media::VideoCaptureFormat& format,
209                                         base::TimeTicks timestamp) {
210   DCHECK(thread_checker_.CalledOnValidThread());
211 
212   // The capture pipeline supports only I420 for now.
213   DCHECK_EQ(format.pixel_format, media::PIXEL_FORMAT_I420);
214 
215   if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
216     Send(new VideoCaptureHostMsg_BufferReady(
217         device_id_, buffer_id, std::vector<uint32>()));
218     return;
219   }
220 
221   last_frame_format_ = format;
222   if (first_frame_timestamp_.is_null())
223     first_frame_timestamp_ = timestamp;
224 
225   // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc
226   TRACE_EVENT_INSTANT2(
227       "cast_perf_test", "OnBufferReceived",
228       TRACE_EVENT_SCOPE_THREAD,
229       "timestamp", timestamp.ToInternalValue(),
230       "time_delta", (timestamp - first_frame_timestamp_).ToInternalValue());
231 
232   ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
233   DCHECK(iter != client_buffers_.end());
234   scoped_refptr<ClientBuffer> buffer = iter->second;
235   scoped_refptr<media::VideoFrame> frame =
236       media::VideoFrame::WrapExternalPackedMemory(
237           media::VideoFrame::I420,
238           last_frame_format_.frame_size,
239           gfx::Rect(last_frame_format_.frame_size),
240           last_frame_format_.frame_size,
241           reinterpret_cast<uint8*>(buffer->buffer->memory()),
242           buffer->buffer_size,
243           buffer->buffer->handle(),
244           timestamp - first_frame_timestamp_,
245           media::BindToCurrentLoop(
246               base::Bind(&VideoCaptureImpl::OnClientBufferFinished,
247                          weak_factory_.GetWeakPtr(),
248                          buffer_id,
249                          buffer,
250                          std::vector<uint32>())));
251 
252   for (ClientInfoMap::iterator it = clients_.begin(); it != clients_.end();
253        ++it) {
254     it->second.deliver_frame_cb.Run(frame, format, timestamp);
255   }
256 }
257 
NullReadPixelsCB(const SkBitmap & bitmap)258 static void NullReadPixelsCB(const SkBitmap& bitmap) { NOTIMPLEMENTED(); }
259 
OnMailboxBufferReceived(int buffer_id,const gpu::MailboxHolder & mailbox_holder,const media::VideoCaptureFormat & format,base::TimeTicks timestamp)260 void VideoCaptureImpl::OnMailboxBufferReceived(
261     int buffer_id,
262     const gpu::MailboxHolder& mailbox_holder,
263     const media::VideoCaptureFormat& format,
264     base::TimeTicks timestamp) {
265   DCHECK(thread_checker_.CalledOnValidThread());
266 
267   if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
268     Send(new VideoCaptureHostMsg_BufferReady(
269         device_id_, buffer_id, std::vector<uint32>()));
270     return;
271   }
272 
273   last_frame_format_ = format;
274   if (first_frame_timestamp_.is_null())
275     first_frame_timestamp_ = timestamp;
276 
277   scoped_refptr<media::VideoFrame> frame = media::VideoFrame::WrapNativeTexture(
278       make_scoped_ptr(new gpu::MailboxHolder(mailbox_holder)),
279       media::BindToCurrentLoop(
280           base::Bind(&VideoCaptureImpl::OnClientBufferFinished,
281                      weak_factory_.GetWeakPtr(),
282                      buffer_id,
283                      scoped_refptr<ClientBuffer>())),
284       last_frame_format_.frame_size,
285       gfx::Rect(last_frame_format_.frame_size),
286       last_frame_format_.frame_size,
287       timestamp - first_frame_timestamp_,
288       base::Bind(&NullReadPixelsCB));
289 
290   for (ClientInfoMap::iterator it = clients_.begin(); it != clients_.end();
291        ++it) {
292     it->second.deliver_frame_cb.Run(frame, format, timestamp);
293   }
294 }
295 
OnClientBufferFinished(int buffer_id,const scoped_refptr<ClientBuffer> &,const std::vector<uint32> & release_sync_points)296 void VideoCaptureImpl::OnClientBufferFinished(
297     int buffer_id,
298     const scoped_refptr<ClientBuffer>& /* ignored_buffer */,
299     const std::vector<uint32>& release_sync_points) {
300   DCHECK(thread_checker_.CalledOnValidThread());
301   Send(new VideoCaptureHostMsg_BufferReady(
302       device_id_, buffer_id, release_sync_points));
303 }
304 
OnStateChanged(VideoCaptureState state)305 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
306   DCHECK(thread_checker_.CalledOnValidThread());
307 
308   switch (state) {
309     case VIDEO_CAPTURE_STATE_STARTED:
310       // Camera has started in the browser process. Since we have already
311       // told all clients that we have started there's nothing to do.
312       break;
313     case VIDEO_CAPTURE_STATE_STOPPED:
314       state_ = VIDEO_CAPTURE_STATE_STOPPED;
315       DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_;
316       client_buffers_.clear();
317       weak_factory_.InvalidateWeakPtrs();
318       if (!clients_.empty() || !clients_pending_on_restart_.empty())
319         RestartCapture();
320       break;
321     case VIDEO_CAPTURE_STATE_PAUSED:
322       for (ClientInfoMap::iterator it = clients_.begin();
323            it != clients_.end(); ++it) {
324         it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_PAUSED);
325       }
326       break;
327     case VIDEO_CAPTURE_STATE_ERROR:
328       DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
329       for (ClientInfoMap::iterator it = clients_.begin();
330            it != clients_.end(); ++it) {
331         it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_ERROR);
332       }
333       clients_.clear();
334       state_ = VIDEO_CAPTURE_STATE_ERROR;
335       break;
336     case VIDEO_CAPTURE_STATE_ENDED:
337       DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
338       for (ClientInfoMap::iterator it = clients_.begin();
339           it != clients_.end(); ++it) {
340         // We'll only notify the client that the stream has stopped.
341         it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
342       }
343       clients_.clear();
344       state_ = VIDEO_CAPTURE_STATE_ENDED;
345       break;
346     default:
347       break;
348   }
349 }
350 
OnDeviceSupportedFormatsEnumerated(const media::VideoCaptureFormats & supported_formats)351 void VideoCaptureImpl::OnDeviceSupportedFormatsEnumerated(
352     const media::VideoCaptureFormats& supported_formats) {
353   DCHECK(thread_checker_.CalledOnValidThread());
354   for (size_t i = 0; i < device_formats_cb_queue_.size(); ++i)
355     device_formats_cb_queue_[i].Run(supported_formats);
356   device_formats_cb_queue_.clear();
357 }
358 
OnDeviceFormatsInUseReceived(const media::VideoCaptureFormats & formats_in_use)359 void VideoCaptureImpl::OnDeviceFormatsInUseReceived(
360     const media::VideoCaptureFormats& formats_in_use) {
361   DCHECK(thread_checker_.CalledOnValidThread());
362   for (size_t i = 0; i < device_formats_in_use_cb_queue_.size(); ++i)
363     device_formats_in_use_cb_queue_[i].Run(formats_in_use);
364   device_formats_in_use_cb_queue_.clear();
365 }
366 
OnDelegateAdded(int32 device_id)367 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
368   DCHECK(thread_checker_.CalledOnValidThread());
369   DVLOG(1) << "OnDelegateAdded: device_id " << device_id;
370 
371   device_id_ = device_id;
372   for (ClientInfoMap::iterator it = clients_pending_on_filter_.begin();
373        it != clients_pending_on_filter_.end(); ) {
374     int client_id = it->first;
375     VideoCaptureStateUpdateCB state_update_cb =
376         it->second.state_update_cb;
377     VideoCaptureDeliverFrameCB deliver_frame_cb =
378         it->second.deliver_frame_cb;
379     const media::VideoCaptureParams params = it->second.params;
380     clients_pending_on_filter_.erase(it++);
381     StartCapture(client_id, params, state_update_cb,
382                  deliver_frame_cb);
383   }
384 }
385 
StopDevice()386 void VideoCaptureImpl::StopDevice() {
387   DCHECK(thread_checker_.CalledOnValidThread());
388 
389   if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
390     state_ = VIDEO_CAPTURE_STATE_STOPPING;
391     Send(new VideoCaptureHostMsg_Stop(device_id_));
392     params_.requested_format.frame_size.SetSize(0, 0);
393   }
394 }
395 
RestartCapture()396 void VideoCaptureImpl::RestartCapture() {
397   DCHECK(thread_checker_.CalledOnValidThread());
398   DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
399 
400   int width = 0;
401   int height = 0;
402   clients_.insert(clients_pending_on_restart_.begin(),
403                   clients_pending_on_restart_.end());
404   clients_pending_on_restart_.clear();
405   for (ClientInfoMap::iterator it = clients_.begin();
406        it != clients_.end(); ++it) {
407     width = std::max(width,
408                      it->second.params.requested_format.frame_size.width());
409     height = std::max(height,
410                       it->second.params.requested_format.frame_size.height());
411   }
412   params_.requested_format.frame_size.SetSize(width, height);
413   DVLOG(1) << "RestartCapture, "
414            << params_.requested_format.frame_size.ToString();
415   StartCaptureInternal();
416 }
417 
StartCaptureInternal()418 void VideoCaptureImpl::StartCaptureInternal() {
419   DCHECK(thread_checker_.CalledOnValidThread());
420   DCHECK(device_id_);
421 
422   Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, params_));
423   state_ = VIDEO_CAPTURE_STATE_STARTED;
424 }
425 
Send(IPC::Message * message)426 void VideoCaptureImpl::Send(IPC::Message* message) {
427   DCHECK(thread_checker_.CalledOnValidThread());
428   message_filter_->Send(message);
429 }
430 
RemoveClient(int client_id,ClientInfoMap * clients)431 bool VideoCaptureImpl::RemoveClient(int client_id, ClientInfoMap* clients) {
432   DCHECK(thread_checker_.CalledOnValidThread());
433   bool found = false;
434 
435   ClientInfoMap::iterator it = clients->find(client_id);
436   if (it != clients->end()) {
437     it->second.state_update_cb.Run(VIDEO_CAPTURE_STATE_STOPPED);
438     clients->erase(it);
439     found = true;
440   }
441   return found;
442 }
443 
444 }  // namespace content
445