Home | History | Annotate | Download | only in media
      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:
     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 
     30   virtual ~ClientBuffer() {}
     31 
     32   DISALLOW_COPY_AND_ASSIGN(ClientBuffer);
     33 };
     34 
     35 bool VideoCaptureImpl::CaptureStarted() {
     36   return state_ == VIDEO_CAPTURE_STATE_STARTED;
     37 }
     38 
     39 int VideoCaptureImpl::CaptureFrameRate() {
     40   return last_frame_format_.frame_rate;
     41 }
     42 
     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 
     59 VideoCaptureImpl::~VideoCaptureImpl() {}
     60 
     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 
     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 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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
    432