- add sources.
[platform/framework/web/crosswalk.git] / src / content / renderer / media / video_capture_impl.cc
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
14 namespace content {
15
16 class VideoCaptureImpl::ClientBuffer
17     : public base::RefCountedThreadSafe<ClientBuffer> {
18  public:
19   ClientBuffer(scoped_ptr<base::SharedMemory> buffer,
20                size_t buffer_size)
21       : buffer(buffer.Pass()),
22         buffer_size(buffer_size) {}
23   const scoped_ptr<base::SharedMemory> buffer;
24   const size_t buffer_size;
25
26  private:
27   friend class base::RefCountedThreadSafe<ClientBuffer>;
28
29   virtual ~ClientBuffer() {}
30
31   DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
32 };
33
34 bool VideoCaptureImpl::CaptureStarted() {
35   return state_ == VIDEO_CAPTURE_STATE_STARTED;
36 }
37
38 int VideoCaptureImpl::CaptureFrameRate() {
39   return last_frame_format_.frame_rate;
40 }
41
42 VideoCaptureImpl::VideoCaptureImpl(
43     const media::VideoCaptureSessionId session_id,
44     base::MessageLoopProxy* capture_message_loop_proxy,
45     VideoCaptureMessageFilter* filter)
46     : VideoCapture(),
47       message_filter_(filter),
48       capture_message_loop_proxy_(capture_message_loop_proxy),
49       io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()),
50       device_id_(0),
51       session_id_(session_id),
52       client_buffer_weak_this_factory_(this),
53       suspended_(false),
54       state_(VIDEO_CAPTURE_STATE_STOPPED) {
55   DCHECK(filter);
56 }
57
58 VideoCaptureImpl::~VideoCaptureImpl() {}
59
60 void VideoCaptureImpl::Init() {
61   if (!io_message_loop_proxy_->BelongsToCurrentThread()) {
62     io_message_loop_proxy_->PostTask(FROM_HERE,
63         base::Bind(&VideoCaptureImpl::AddDelegateOnIOThread,
64                    base::Unretained(this)));
65   } else {
66     AddDelegateOnIOThread();
67   }
68 }
69
70 void VideoCaptureImpl::DeInit(base::Closure task) {
71   capture_message_loop_proxy_->PostTask(FROM_HERE,
72       base::Bind(&VideoCaptureImpl::DoDeInitOnCaptureThread,
73                  base::Unretained(this), task));
74 }
75
76 void VideoCaptureImpl::StartCapture(
77     media::VideoCapture::EventHandler* handler,
78     const media::VideoCaptureParams& params) {
79   capture_message_loop_proxy_->PostTask(FROM_HERE,
80       base::Bind(&VideoCaptureImpl::DoStartCaptureOnCaptureThread,
81                  base::Unretained(this), handler, params));
82 }
83
84 void VideoCaptureImpl::StopCapture(media::VideoCapture::EventHandler* handler) {
85   capture_message_loop_proxy_->PostTask(FROM_HERE,
86       base::Bind(&VideoCaptureImpl::DoStopCaptureOnCaptureThread,
87                  base::Unretained(this), handler));
88 }
89
90 void VideoCaptureImpl::OnBufferCreated(
91     base::SharedMemoryHandle handle,
92     int length, int buffer_id) {
93   capture_message_loop_proxy_->PostTask(FROM_HERE,
94       base::Bind(&VideoCaptureImpl::DoBufferCreatedOnCaptureThread,
95                  base::Unretained(this), handle, length, buffer_id));
96 }
97
98 void VideoCaptureImpl::OnBufferDestroyed(int buffer_id) {
99   capture_message_loop_proxy_->PostTask(FROM_HERE,
100       base::Bind(&VideoCaptureImpl::DoBufferDestroyedOnCaptureThread,
101                  base::Unretained(this), buffer_id));
102 }
103
104 void VideoCaptureImpl::OnBufferReceived(
105     int buffer_id,
106     base::Time timestamp,
107     const media::VideoCaptureFormat& format) {
108   capture_message_loop_proxy_->PostTask(FROM_HERE,
109       base::Bind(&VideoCaptureImpl::DoBufferReceivedOnCaptureThread,
110                  base::Unretained(this), buffer_id, timestamp, format));
111 }
112
113 void VideoCaptureImpl::OnStateChanged(VideoCaptureState state) {
114   capture_message_loop_proxy_->PostTask(FROM_HERE,
115       base::Bind(&VideoCaptureImpl::DoStateChangedOnCaptureThread,
116                  base::Unretained(this), state));
117 }
118
119 void VideoCaptureImpl::OnDelegateAdded(int32 device_id) {
120   capture_message_loop_proxy_->PostTask(FROM_HERE,
121       base::Bind(&VideoCaptureImpl::DoDelegateAddedOnCaptureThread,
122                  base::Unretained(this), device_id));
123 }
124
125 void VideoCaptureImpl::SuspendCapture(bool suspend) {
126   capture_message_loop_proxy_->PostTask(FROM_HERE,
127       base::Bind(&VideoCaptureImpl::DoSuspendCaptureOnCaptureThread,
128                  base::Unretained(this), suspend));
129 }
130
131 void VideoCaptureImpl::DoDeInitOnCaptureThread(base::Closure task) {
132   if (state_ == VIDEO_CAPTURE_STATE_STARTED)
133     Send(new VideoCaptureHostMsg_Stop(device_id_));
134
135   io_message_loop_proxy_->PostTask(FROM_HERE,
136       base::Bind(&VideoCaptureImpl::RemoveDelegateOnIOThread,
137                  base::Unretained(this), task));
138 }
139
140 void VideoCaptureImpl::DoStartCaptureOnCaptureThread(
141     media::VideoCapture::EventHandler* handler,
142     const media::VideoCaptureParams& params) {
143   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
144
145   if (state_ == VIDEO_CAPTURE_STATE_ERROR) {
146     handler->OnError(this, 1);
147     handler->OnRemoved(this);
148   } else if ((clients_pending_on_filter_.find(handler) !=
149               clients_pending_on_filter_.end()) ||
150              (clients_pending_on_restart_.find(handler) !=
151               clients_pending_on_restart_.end()) ||
152              clients_.find(handler) != clients_.end() ) {
153     // This client has started.
154   } else if (!device_id_) {
155     clients_pending_on_filter_[handler] = params;
156   } else {
157     handler->OnStarted(this);
158     if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
159       clients_[handler] = params;
160     } else if (state_ == VIDEO_CAPTURE_STATE_STOPPING) {
161       clients_pending_on_restart_[handler] = params;
162       DVLOG(1) << "StartCapture: Got new resolution ("
163                << params.requested_format.width << ", "
164                << params.requested_format.height << ") "
165                << ", during stopping.";
166     } else {
167       DCHECK_EQ(params.session_id, 0);
168       clients_[handler] = params;
169       DCHECK_EQ(1ul, clients_.size());
170       params_ = params;
171       params_.session_id = session_id_;
172       if (params_.requested_format.frame_rate >
173           media::limits::kMaxFramesPerSecond) {
174         params_.requested_format.frame_rate =
175             media::limits::kMaxFramesPerSecond;
176       }
177       DVLOG(1) << "StartCapture: starting with first resolution ("
178                << params_.requested_format.width << ","
179                << params_.requested_format.height << ")";
180
181       StartCaptureInternal();
182     }
183   }
184 }
185
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     client_buffer_weak_this_factory_.InvalidateWeakPtrs();
202   }
203 }
204
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
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
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   gfx::Size size(format.width, format.height);
256
257   ClientBufferMap::iterator iter = client_buffers_.find(buffer_id);
258   DCHECK(iter != client_buffers_.end());
259   scoped_refptr<ClientBuffer> buffer = iter->second;
260   scoped_refptr<media::VideoFrame> frame =
261       media::VideoFrame::WrapExternalSharedMemory(
262           media::VideoFrame::I420,
263           size, gfx::Rect(size), size,
264           reinterpret_cast<uint8*>(buffer->buffer->memory()),
265           buffer->buffer_size,
266           buffer->buffer->handle(),
267           // TODO(sheu): convert VideoCaptureMessageFilter::Delegate to use
268           // base::TimeTicks instead of base::Time.  http://crbug.com/249215
269           timestamp - base::Time::UnixEpoch(),
270           media::BindToLoop(
271               capture_message_loop_proxy_,
272               base::Bind(
273                   &VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread,
274                   client_buffer_weak_this_factory_.GetWeakPtr(),
275                   buffer_id,
276                   buffer)));
277
278   for (ClientInfo::iterator it = clients_.begin(); it != clients_.end(); ++it)
279     it->first->OnFrameReady(this, frame);
280 }
281
282 void VideoCaptureImpl::DoClientBufferFinishedOnCaptureThread(
283     int buffer_id,
284     const scoped_refptr<ClientBuffer>& buffer) {
285   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
286   Send(new VideoCaptureHostMsg_BufferReady(device_id_, buffer_id));
287 }
288
289 void VideoCaptureImpl::DoStateChangedOnCaptureThread(VideoCaptureState state) {
290   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
291
292   switch (state) {
293     case VIDEO_CAPTURE_STATE_STARTED:
294       break;
295     case VIDEO_CAPTURE_STATE_STOPPED:
296       state_ = VIDEO_CAPTURE_STATE_STOPPED;
297       DVLOG(1) << "OnStateChanged: stopped!, device_id = " << device_id_;
298       client_buffers_.clear();
299       client_buffer_weak_this_factory_.InvalidateWeakPtrs();
300       if (!clients_.empty() || !clients_pending_on_restart_.empty())
301         RestartCapture();
302       break;
303     case VIDEO_CAPTURE_STATE_PAUSED:
304       for (ClientInfo::iterator it = clients_.begin();
305            it != clients_.end(); ++it) {
306         it->first->OnPaused(this);
307       }
308       break;
309     case VIDEO_CAPTURE_STATE_ERROR:
310       DVLOG(1) << "OnStateChanged: error!, device_id = " << device_id_;
311       for (ClientInfo::iterator it = clients_.begin();
312            it != clients_.end(); ++it) {
313         // TODO(wjia): browser process would send error code.
314         it->first->OnError(this, 1);
315         it->first->OnRemoved(this);
316       }
317       clients_.clear();
318       state_ = VIDEO_CAPTURE_STATE_ERROR;
319       break;
320     case VIDEO_CAPTURE_STATE_ENDED:
321       DVLOG(1) << "OnStateChanged: ended!, device_id = " << device_id_;
322       for (ClientInfo::iterator it = clients_.begin();
323           it != clients_.end(); ++it) {
324         it->first->OnRemoved(this);
325       }
326       clients_.clear();
327       state_ = VIDEO_CAPTURE_STATE_ENDED;
328       break;
329     default:
330       break;
331   }
332 }
333
334 void VideoCaptureImpl::DoDelegateAddedOnCaptureThread(int32 device_id) {
335   DVLOG(1) << "DoDelegateAdded: device_id " << device_id;
336   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
337
338   device_id_ = device_id;
339   for (ClientInfo::iterator it = clients_pending_on_filter_.begin();
340        it != clients_pending_on_filter_.end(); ) {
341     media::VideoCapture::EventHandler* handler = it->first;
342     const media::VideoCaptureParams params = it->second;
343     clients_pending_on_filter_.erase(it++);
344     StartCapture(handler, params);
345   }
346 }
347
348 void VideoCaptureImpl::DoSuspendCaptureOnCaptureThread(bool suspend) {
349   DVLOG(1) << "DoSuspendCapture: suspend " << (suspend ? "yes" : "no");
350   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
351
352   suspended_ = suspend;
353 }
354
355 void VideoCaptureImpl::StopDevice() {
356   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
357
358   if (state_ == VIDEO_CAPTURE_STATE_STARTED) {
359     state_ = VIDEO_CAPTURE_STATE_STOPPING;
360     Send(new VideoCaptureHostMsg_Stop(device_id_));
361     params_.requested_format.width = params_.requested_format.height = 0;
362   }
363 }
364
365 void VideoCaptureImpl::RestartCapture() {
366   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
367   DCHECK_EQ(state_, VIDEO_CAPTURE_STATE_STOPPED);
368
369   int width = 0;
370   int height = 0;
371   for (ClientInfo::iterator it = clients_.begin();
372        it != clients_.end(); ++it) {
373     width = std::max(width, it->second.requested_format.width);
374     height = std::max(height, it->second.requested_format.height);
375   }
376   for (ClientInfo::iterator it = clients_pending_on_restart_.begin();
377        it != clients_pending_on_restart_.end(); ) {
378     width = std::max(width, it->second.requested_format.width);
379     height = std::max(height, it->second.requested_format.height);
380     clients_[it->first] = it->second;
381     clients_pending_on_restart_.erase(it++);
382   }
383   params_.requested_format.width = width;
384   params_.requested_format.height = height;
385   DVLOG(1) << "RestartCapture, " << params_.requested_format.width << ", "
386            << params_.requested_format.height;
387   StartCaptureInternal();
388 }
389
390 void VideoCaptureImpl::StartCaptureInternal() {
391   DCHECK(capture_message_loop_proxy_->BelongsToCurrentThread());
392   DCHECK(device_id_);
393
394   Send(new VideoCaptureHostMsg_Start(device_id_, params_));
395   state_ = VIDEO_CAPTURE_STATE_STARTED;
396 }
397
398 void VideoCaptureImpl::AddDelegateOnIOThread() {
399   DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
400   message_filter_->AddDelegate(this);
401 }
402
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
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
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