• 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 #include "content/renderer/media/video_capture_impl.h"
6 
7 #include "base/bind.h"
8 #include "base/stl_util.h"
9 #include "content/child/child_process.h"
10 #include "content/common/media/video_capture_messages.h"
11 #include "media/base/bind_to_loop.h"
12 #include "media/base/limits.h"
13 #include "media/base/video_frame.h"
14 
15 namespace content {
16 
17 class VideoCaptureImpl::ClientBuffer
18     : public base::RefCountedThreadSafe<ClientBuffer> {
19  public:
ClientBuffer(scoped_ptr<base::SharedMemory> buffer,size_t buffer_size)20   ClientBuffer(scoped_ptr<base::SharedMemory> buffer,
21                size_t buffer_size)
22       : buffer(buffer.Pass()),
23         buffer_size(buffer_size) {}
24   const scoped_ptr<base::SharedMemory> buffer;
25   const size_t buffer_size;
26 
27  private:
28   friend class base::RefCountedThreadSafe<ClientBuffer>;
29 
~ClientBuffer()30   virtual ~ClientBuffer() {}
31 
32   DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
33 };
34 
CaptureStarted()35 bool VideoCaptureImpl::CaptureStarted() {
36   return state_ == VIDEO_CAPTURE_STATE_STARTED;
37 }
38 
CaptureFrameRate()39 int VideoCaptureImpl::CaptureFrameRate() {
40   return last_frame_format_.frame_rate;
41 }
42 
VideoCaptureImpl(const media::VideoCaptureSessionId session_id,base::MessageLoopProxy * capture_message_loop_proxy,VideoCaptureMessageFilter * filter)43 VideoCaptureImpl::VideoCaptureImpl(
44     const media::VideoCaptureSessionId session_id,
45     base::MessageLoopProxy* capture_message_loop_proxy,
46     VideoCaptureMessageFilter* filter)
47     : VideoCapture(),
48       message_filter_(filter),
49       capture_message_loop_proxy_(capture_message_loop_proxy),
50       io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
51       device_id_(0),
52       session_id_(session_id),
53       suspended_(false),
54       state_(VIDEO_CAPTURE_STATE_STOPPED),
55       weak_this_factory_(this) {
56   DCHECK(filter);
57 }
58 
~VideoCaptureImpl()59 VideoCaptureImpl::~VideoCaptureImpl() {}
60 
Init()61 void VideoCaptureImpl::Init() {
62   if (!io_message_loop_proxy_->BelongsToCurrentThread()) {
63     io_message_loop_proxy_->PostTask(FROM_HERE,
64         base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread,
65                    base::Unretained(this)));
66   } else {
67     AddDelegateOnIOThread();
68   }
69 }
70 
DeInit(base::Closure task)71 void VideoCaptureImpl::DeInit(base::Closure task) {
72   capture_message_loop_proxy_->PostTask(FROM_HERE,
73       base::Bind(&VideoCaptureImpl::DoDeInitOnCaptureThread,
74                  base::Unretained(this), task));
75 }
76 
StartCapture(media::VideoCapture::EventHandler * handler,const media::VideoCaptureParams & params)77 void VideoCaptureImpl::StartCapture(
78     media::VideoCapture::EventHandler* handler,
79     const media::VideoCaptureParams& params) {
80   capture_message_loop_proxy_->PostTask(FROM_HERE,
81       base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread,
82                  base::Unretained(this), handler, params));
83 }
84 
StopCapture(media::VideoCapture::EventHandler * handler)85 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) {
86   capture_message_loop_proxy_->PostTask(FROM_HERE,
87       base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread,
88                  base::Unretained(this), handler));
89 }
90 
OnBufferCreated(base::SharedMemoryHandle handle,int length,int buffer_id)91 void VideoCaptureImpl::OnBufferCreated(
92     base::SharedMemoryHandle handle,
93     int length, int buffer_id) {
94   capture_message_loop_proxy_->PostTask(FROM_HERE,
95       base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread,
96                  base::Unretained(this), handle, length, buffer_id));
97 }
98 
OnBufferDestroyed(int buffer_id)99 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
100   capture_message_loop_proxy_->PostTask(FROM_HERE,
101       base::Bind(&VideoCaptureImpl::DoBufferDestroyedOnCaptureThread,
102                  base::Unretained(this), buffer_id));
103 }
104 
OnBufferReceived(int buffer_id,base::Time timestamp,const media::VideoCaptureFormat & format)105 void VideoCaptureImpl::OnBufferReceived(
106     int buffer_id,
107     base::Time timestamp,
108     const media::VideoCaptureFormat& format) {
109   capture_message_loop_proxy_->PostTask(FROM_HERE,
110       base::Bind(&VideoCaptureImpl::DoBufferReceivedOnCaptureThread,
111                  base::Unretained(this), buffer_id, timestamp, format));
112 }
113 
OnStateChanged(VideoCaptureState state)114 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
115   capture_message_loop_proxy_->PostTask(FROM_HERE,
116       base::Bind(&VideoCaptureImpl::DoStateChangedOnCaptureThread,
117                  base::Unretained(this), state));
118 }
119 
OnDelegateAdded(int32 device_id)120 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
121   capture_message_loop_proxy_->PostTask(FROM_HERE,
122       base::Bind(&VideoCaptureImpl::DoDelegateAddedOnCaptureThread,
123                  base::Unretained(this), device_id));
124 }
125 
SuspendCapture(bool suspend)126 void VideoCaptureImpl::SuspendCapture(bool suspend) {
127   capture_message_loop_proxy_->PostTask(FROM_HERE,
128       base::Bind(&VideoCaptureImpl::DoSuspendCaptureOnCaptureThread,
129                  base::Unretained(this), suspend));
130 }
131 
DoDeInitOnCaptureThread(base::Closure task)132 void VideoCaptureImpl::DoDeInitOnCaptureThread(base::Closure task) {
133   if (state_ == VIDEO_CAPTURE_STATE_STARTED)
134     Send(new VideoCaptureHostMsg_Stop(device_id_));
135 
136   io_message_loop_proxy_->PostTask(FROM_HERE,
137       base::Bind(&VideoCaptureImpl::RemoveDelegateOnIOThread,
138                  base::Unretained(this), task));
139 }
140 
DoStartCaptureOnCaptureThread(media::VideoCapture::EventHandler * handler,const media::VideoCaptureParams & params)141 void VideoCaptureImpl::DoStartCaptureOnCaptureThread(
142     media::VideoCapture::EventHandler* handler,
143     const media::VideoCaptureParams& params) {
144   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
145 
146   if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
147     handler->OnError(this, 1);
148     handler->OnRemoved(this);
149   } else if ((clients_pending_on_filter_.find(handler) !=
150               clients_pending_on_filter_.end()) ||
151              (clients_pending_on_restart_.find(handler) !=
152               clients_pending_on_restart_.end()) ||
153              clients_.find(handler) != clients_.end() ) {
154     // This client has started.
155   } else if (!device_id_) {
156     clients_pending_on_filter_[handler] = params;
157   } else {
158     handler->OnStarted(this);
159     if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
160       clients_[handler] = params;
161     } else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
162       clients_pending_on_restart_[handler] = params;
163       DVLOG(1) << "StartCapture: Got new resolution "
164                << params.requested_format.frame_size.ToString()
165                << " during stopping.";
166     } else {
167       // TODO(sheu): Allowing resolution change will require that all
168       // outstanding clients of a capture session support resolution change.
169       DCHECK(!params.allow_resolution_change);
170       clients_[handler] = params;
171       DCHECK_EQ(1ul, clients_.size());
172       params_ = params;
173       if (params_.requested_format.frame_rate >
174           media::limits::kMaxFramesPerSecond) {
175         params_.requested_format.frame_rate =
176             media::limits::kMaxFramesPerSecond;
177       }
178       DVLOG(1) << "StartCapture: starting with first resolution "
179                << params_.requested_format.frame_size.ToString();
180 
181       StartCaptureInternal();
182     }
183   }
184 }
185 
DoStopCaptureOnCaptureThread(media::VideoCapture::EventHandler * handler)186 void VideoCaptureImpl::DoStopCaptureOnCaptureThread(
187     media::VideoCapture::EventHandler* handler) {
188   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
189 
190   // A handler can be in only one client list.
191   // If this handler is in any client list, we can just remove it from
192   // that client list and don't have to run the other following RemoveClient().
193   RemoveClient(handler, &clients_pending_on_filter_) ||
194   RemoveClient(handler, &clients_pending_on_restart_) ||
195   RemoveClient(handler, &clients_);
196 
197   if (clients_.empty()) {
198     DVLOG(1) << "StopCapture: No more client, stopping ...";
199     StopDevice();
200     client_buffers_.clear();
201     weak_this_factory_.InvalidateWeakPtrs();
202   }
203 }
204 
DoBufferCreatedOnCaptureThread(base::SharedMemoryHandle handle,int length,int buffer_id)205 void VideoCaptureImpl::DoBufferCreatedOnCaptureThread(
206     base::SharedMemoryHandle handle,
207     int length, int buffer_id) {
208   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
209 
210   // In case client calls StopCapture before the arrival of created buffer,
211   // just close this buffer and return.
212   if (state_ != VIDEO_CAPTURE_STATE_STARTED) {
213     base::SharedMemory::CloseHandle(handle);
214     return;
215   }
216 
217   scoped_ptr<base::SharedMemory> shm(new base::SharedMemory(handle, false));
218   if (!shm->Map(length)) {
219     DLOG(ERROR) << "DoBufferCreatedOnCaptureThread: Map() failed.";
220     return;
221   }
222 
223   bool inserted =
224       client_buffers_.insert(std::make_pair(
225                                  buffer_id,
226                                  new ClientBuffer(shm.Pass(),
227                                                   length))).second;
228   DCHECK(inserted);
229 }
230 
DoBufferDestroyedOnCaptureThread(int buffer_id)231 void VideoCaptureImpl::DoBufferDestroyedOnCaptureThread(int buffer_id) {
232   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
233 
234   ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
235   if (iter == client_buffers_.end())
236     return;
237 
238   DCHECK(!iter->second || iter->second->HasOneRef())
239       << "Instructed to delete buffer we are still using.";
240   client_buffers_.erase(iter);
241 }
242 
DoBufferReceivedOnCaptureThread(int buffer_id,base::Time timestamp,const media::VideoCaptureFormat & format)243 void VideoCaptureImpl::DoBufferReceivedOnCaptureThread(
244     int buffer_id,
245     base::Time timestamp,
246     const media::VideoCaptureFormat& format) {
247   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
248 
249   if (state_ != VIDEO_CAPTURE_STATE_STARTED || suspended_) {
250     Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id));
251     return;
252   }
253 
254   last_frame_format_ = format;
255 
256   ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
257   DCHECK(iter != client_buffers_.end());
258   scoped_refptr<ClientBuffer> buffer = iter->second;
259   scoped_refptr<media::VideoFrame> frame =
260       media::VideoFrame::WrapExternalPackedMemory(
261           media::VideoFrame::I420,
262           last_frame_format_.frame_size,
263           gfx::Rect(last_frame_format_.frame_size),
264           last_frame_format_.frame_size,
265           reinterpret_cast<uint8*>(buffer->buffer->memory()),
266           buffer->buffer_size,
267           buffer->buffer->handle(),
268           // TODO(sheu): convert VideoCaptureMessageFilter::Delegate to use
269           // base::TimeTicks instead of base::Time.  http://crbug.com/249215
270           timestamp - base::Time::UnixEpoch(),
271           media::BindToLoop(
272               capture_message_loop_proxy_,
273               base::Bind(
274                   &VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread,
275                   weak_this_factory_.GetWeakPtr(),
276                   buffer_id,
277                   buffer)));
278 
279   for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it)
280     it->first->OnFrameReady(this, frame);
281 }
282 
DoClientBufferFinishedOnCaptureThread(int buffer_id,const scoped_refptr<ClientBuffer> & buffer)283 void VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread(
284     int buffer_id,
285     const scoped_refptr<ClientBuffer>& buffer) {
286   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
287   Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id));
288 }
289 
DoStateChangedOnCaptureThread(VideoCaptureState state)290 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state) {
291   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
292 
293   switch (state) {
294     case VIDEO_CAPTURE_STATE_STARTED:
295       break;
296     case VIDEO_CAPTURE_STATE_STOPPED:
297       state_ = VIDEO_CAPTURE_STATE_STOPPED;
298       DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_;
299       client_buffers_.clear();
300       weak_this_factory_.InvalidateWeakPtrs();
301       if (!clients_.empty() || !clients_pending_on_restart_.empty())
302         RestartCapture();
303       break;
304     case VIDEO_CAPTURE_STATE_PAUSED:
305       for (ClientInfo::iterator it = clients_.begin();
306            it != clients_.end(); ++it) {
307         it->first->OnPaused(this);
308       }
309       break;
310     case VIDEO_CAPTURE_STATE_ERROR:
311       DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
312       for (ClientInfo::iterator it = clients_.begin();
313            it != clients_.end(); ++it) {
314         // TODO(wjia): browser process would send error code.
315         it->first->OnError(this, 1);
316         it->first->OnRemoved(this);
317       }
318       clients_.clear();
319       state_ = VIDEO_CAPTURE_STATE_ERROR;
320       break;
321     case VIDEO_CAPTURE_STATE_ENDED:
322       DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
323       for (ClientInfo::iterator it = clients_.begin();
324           it != clients_.end(); ++it) {
325         it->first->OnRemoved(this);
326       }
327       clients_.clear();
328       state_ = VIDEO_CAPTURE_STATE_ENDED;
329       break;
330     default:
331       break;
332   }
333 }
334 
DoDelegateAddedOnCaptureThread(int32 device_id)335 void VideoCaptureImpl::DoDelegateAddedOnCaptureThread(int32 device_id) {
336   DVLOG(1) << "DoDelegateAdded: device_id " << device_id;
337   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
338 
339   device_id_ = device_id;
340   for (ClientInfo::iterator it = clients_pending_on_filter_.begin();
341        it != clients_pending_on_filter_.end(); ) {
342     media::VideoCapture::EventHandler* handler = it->first;
343     const media::VideoCaptureParams params = it->second;
344     clients_pending_on_filter_.erase(it++);
345     StartCapture(handler, params);
346   }
347 }
348 
DoSuspendCaptureOnCaptureThread(bool suspend)349 void VideoCaptureImpl::DoSuspendCaptureOnCaptureThread(bool suspend) {
350   DVLOG(1) << "DoSuspendCapture: suspend " << (suspend ? "yes" : "no");
351   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
352 
353   suspended_ = suspend;
354 }
355 
StopDevice()356 void VideoCaptureImpl::StopDevice() {
357   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
358 
359   if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
360     state_ = VIDEO_CAPTURE_STATE_STOPPING;
361     Send(new VideoCaptureHostMsg_Stop(device_id_));
362     params_.requested_format.frame_size.SetSize(0, 0);
363   }
364 }
365 
RestartCapture()366 void VideoCaptureImpl::RestartCapture() {
367   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
368   DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
369 
370   int width = 0;
371   int height = 0;
372   for (ClientInfo::iterator it = clients_.begin();
373        it != clients_.end(); ++it) {
374     width = std::max(width, it->second.requested_format.frame_size.width());
375     height = std::max(height, it->second.requested_format.frame_size.height());
376   }
377   for (ClientInfo::iterator it = clients_pending_on_restart_.begin();
378        it != clients_pending_on_restart_.end(); ) {
379     width = std::max(width, it->second.requested_format.frame_size.width());
380     height = std::max(height, it->second.requested_format.frame_size.height());
381     clients_[it->first] = it->second;
382     clients_pending_on_restart_.erase(it++);
383   }
384   params_.requested_format.frame_size.SetSize(width, height);
385   DVLOG(1) << "RestartCapture, "
386            << params_.requested_format.frame_size.ToString();
387   StartCaptureInternal();
388 }
389 
StartCaptureInternal()390 void VideoCaptureImpl::StartCaptureInternal() {
391   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
392   DCHECK(device_id_);
393 
394   Send(new VideoCaptureHostMsg_Start(device_id_, session_id_, params_));
395   state_ = VIDEO_CAPTURE_STATE_STARTED;
396 }
397 
AddDelegateOnIOThread()398 void VideoCaptureImpl::AddDelegateOnIOThread() {
399   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
400   message_filter_->AddDelegate(this);
401 }
402 
RemoveDelegateOnIOThread(base::Closure task)403 void VideoCaptureImpl::RemoveDelegateOnIOThread(base::Closure task) {
404   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
405   message_filter_->RemoveDelegate(this);
406   capture_message_loop_proxy_->PostTask(FROM_HERE, task);
407 }
408 
Send(IPC::Message * message)409 void VideoCaptureImpl::Send(IPC::Message* message) {
410   io_message_loop_proxy_->PostTask(FROM_HERE,
411       base::Bind(base::IgnoreResult(&VideoCaptureMessageFilter::Send),
412                  message_filter_.get(), message));
413 }
414 
RemoveClient(media::VideoCapture::EventHandler * handler,ClientInfo * clients)415 bool VideoCaptureImpl::RemoveClient(
416     media::VideoCapture::EventHandler* handler,
417     ClientInfo* clients) {
418   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
419   bool found = false;
420 
421   ClientInfo::iterator it = clients->find(handler);
422   if (it != clients->end()) {
423     handler->OnStopped(this);
424     handler->OnRemoved(this);
425     clients->erase(it);
426     found = true;
427   }
428   return found;
429 }
430 
431 }  // namespace content
432