Home | History | Annotate | Download | only in media
      1 // Copyright 2013 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/browser/renderer_host/media/video_capture_device_impl.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/bind.h"
      9 #include "base/callback_forward.h"
     10 #include "base/callback_helpers.h"
     11 #include "base/debug/trace_event.h"
     12 #include "base/logging.h"
     13 #include "base/memory/scoped_ptr.h"
     14 #include "base/memory/weak_ptr.h"
     15 #include "base/message_loop/message_loop_proxy.h"
     16 #include "base/metrics/histogram.h"
     17 #include "base/sequenced_task_runner.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/synchronization/lock.h"
     20 #include "base/threading/thread.h"
     21 #include "base/threading/thread_checker.h"
     22 #include "base/time/time.h"
     23 #include "content/public/browser/browser_thread.h"
     24 #include "media/base/bind_to_loop.h"
     25 #include "media/base/video_frame.h"
     26 #include "media/base/video_util.h"
     27 #include "media/video/capture/video_capture_types.h"
     28 #include "ui/gfx/rect.h"
     29 
     30 namespace content {
     31 
     32 namespace {
     33 
     34 void DeleteCaptureMachineOnUIThread(
     35     scoped_ptr<VideoCaptureMachine> capture_machine) {
     36   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     37   if (capture_machine) {
     38     capture_machine->Stop();
     39     capture_machine.reset();
     40   }
     41 }
     42 
     43 }  // namespace
     44 
     45 ThreadSafeCaptureOracle::ThreadSafeCaptureOracle(
     46     scoped_ptr<media::VideoCaptureDevice::Client> client,
     47     scoped_ptr<VideoCaptureOracle> oracle,
     48     const media::VideoCaptureParams& params)
     49     : client_(client.Pass()),
     50       oracle_(oracle.Pass()),
     51       params_(params),
     52       capture_size_updated_(false) {
     53   // Frame dimensions must each be an even integer since the client wants (or
     54   // will convert to) YUV420.
     55   capture_size_ = gfx::Size(
     56       MakeEven(params.requested_format.frame_size.width()),
     57       MakeEven(params.requested_format.frame_size.height()));
     58   frame_rate_ = params.requested_format.frame_rate;
     59 }
     60 
     61 ThreadSafeCaptureOracle::~ThreadSafeCaptureOracle() {}
     62 
     63 bool ThreadSafeCaptureOracle::ObserveEventAndDecideCapture(
     64     VideoCaptureOracle::Event event,
     65     base::Time event_time,
     66     scoped_refptr<media::VideoFrame>* storage,
     67     CaptureFrameCallback* callback) {
     68   base::AutoLock guard(lock_);
     69 
     70   if (!client_)
     71     return false;  // Capture is stopped.
     72 
     73   scoped_refptr<media::VideoCaptureDevice::Client::Buffer> output_buffer =
     74       client_->ReserveOutputBuffer(media::VideoFrame::I420, capture_size_);
     75   const bool should_capture =
     76       oracle_->ObserveEventAndDecideCapture(event, event_time);
     77   const bool content_is_dirty =
     78       (event == VideoCaptureOracle::kCompositorUpdate ||
     79        event == VideoCaptureOracle::kSoftwarePaint);
     80   const char* event_name =
     81       (event == VideoCaptureOracle::kTimerPoll ? "poll" :
     82        (event == VideoCaptureOracle::kCompositorUpdate ? "gpu" :
     83        "paint"));
     84 
     85   // Consider the various reasons not to initiate a capture.
     86   if (should_capture && !output_buffer) {
     87     TRACE_EVENT_INSTANT1("mirroring",
     88                          "EncodeLimited",
     89                          TRACE_EVENT_SCOPE_THREAD,
     90                          "trigger",
     91                          event_name);
     92     return false;
     93   } else if (!should_capture && output_buffer) {
     94     if (content_is_dirty) {
     95       // This is a normal and acceptable way to drop a frame. We've hit our
     96       // capture rate limit: for example, the content is animating at 60fps but
     97       // we're capturing at 30fps.
     98       TRACE_EVENT_INSTANT1("mirroring", "FpsRateLimited",
     99                            TRACE_EVENT_SCOPE_THREAD,
    100                            "trigger", event_name);
    101     }
    102     return false;
    103   } else if (!should_capture && !output_buffer) {
    104     // We decided not to capture, but we wouldn't have been able to if we wanted
    105     // to because no output buffer was available.
    106     TRACE_EVENT_INSTANT1("mirroring", "NearlyEncodeLimited",
    107                          TRACE_EVENT_SCOPE_THREAD,
    108                          "trigger", event_name);
    109     return false;
    110   }
    111   int frame_number = oracle_->RecordCapture();
    112   TRACE_EVENT_ASYNC_BEGIN2("mirroring", "Capture", output_buffer.get(),
    113                            "frame_number", frame_number,
    114                            "trigger", event_name);
    115   *callback = base::Bind(&ThreadSafeCaptureOracle::DidCaptureFrame,
    116                          this,
    117                          output_buffer,
    118                          frame_number);
    119   *storage = media::VideoFrame::WrapExternalPackedMemory(
    120       media::VideoFrame::I420,
    121       capture_size_,
    122       gfx::Rect(capture_size_),
    123       capture_size_,
    124       static_cast<uint8*>(output_buffer->data()),
    125       output_buffer->size(),
    126       base::SharedMemory::NULLHandle(),
    127       base::TimeDelta(),
    128       base::Closure());
    129   return true;
    130 }
    131 
    132 void ThreadSafeCaptureOracle::UpdateCaptureSize(const gfx::Size& source_size) {
    133   base::AutoLock guard(lock_);
    134 
    135   // If this is the first call to UpdateCaptureSize(), or the receiver supports
    136   // variable resolution, then determine the capture size by treating the
    137   // requested width and height as maxima.
    138   if (!capture_size_updated_ || params_.allow_resolution_change) {
    139     // The capture resolution should not exceed the source frame size.
    140     // In other words it should downscale the image but not upscale it.
    141     if (source_size.width() > params_.requested_format.frame_size.width() ||
    142         source_size.height() > params_.requested_format.frame_size.height()) {
    143       gfx::Rect capture_rect = media::ComputeLetterboxRegion(
    144           gfx::Rect(params_.requested_format.frame_size), source_size);
    145       capture_size_ = gfx::Size(MakeEven(capture_rect.width()),
    146                                 MakeEven(capture_rect.height()));
    147     } else {
    148       capture_size_ = gfx::Size(MakeEven(source_size.width()),
    149                                 MakeEven(source_size.height()));
    150     }
    151     capture_size_updated_ = true;
    152   }
    153 }
    154 
    155 void ThreadSafeCaptureOracle::Stop() {
    156   base::AutoLock guard(lock_);
    157   client_.reset();
    158 }
    159 
    160 void ThreadSafeCaptureOracle::ReportError() {
    161   base::AutoLock guard(lock_);
    162   if (client_)
    163     client_->OnError();
    164 }
    165 
    166 void ThreadSafeCaptureOracle::DidCaptureFrame(
    167     scoped_refptr<media::VideoCaptureDevice::Client::Buffer> buffer,
    168     int frame_number,
    169     base::Time timestamp,
    170     bool success) {
    171   base::AutoLock guard(lock_);
    172   TRACE_EVENT_ASYNC_END2("mirroring", "Capture", buffer.get(),
    173                          "success", success,
    174                          "timestamp", timestamp.ToInternalValue());
    175 
    176   if (!client_)
    177     return;  // Capture is stopped.
    178 
    179   if (success) {
    180     if (oracle_->CompleteCapture(frame_number, timestamp)) {
    181       client_->OnIncomingCapturedBuffer(buffer,
    182                                         media::VideoFrame::I420,
    183                                         capture_size_,
    184                                         timestamp,
    185                                         frame_rate_);
    186     }
    187   }
    188 }
    189 
    190 void VideoCaptureDeviceImpl::AllocateAndStart(
    191     const media::VideoCaptureParams& params,
    192     scoped_ptr<media::VideoCaptureDevice::Client> client) {
    193   DCHECK(thread_checker_.CalledOnValidThread());
    194 
    195   if (state_ != kIdle) {
    196     DVLOG(1) << "Allocate() invoked when not in state Idle.";
    197     return;
    198   }
    199 
    200   if (params.requested_format.frame_rate <= 0) {
    201     DVLOG(1) << "invalid frame_rate: " << params.requested_format.frame_rate;
    202     client->OnError();
    203     return;
    204   }
    205 
    206   if (params.requested_format.frame_size.width() < kMinFrameWidth ||
    207       params.requested_format.frame_size.height() < kMinFrameHeight) {
    208     DVLOG(1) << "invalid frame size: "
    209              << params.requested_format.frame_size.ToString();
    210     client->OnError();
    211     return;
    212   }
    213 
    214   base::TimeDelta capture_period = base::TimeDelta::FromMicroseconds(
    215       1000000.0 / params.requested_format.frame_rate + 0.5);
    216 
    217   scoped_ptr<VideoCaptureOracle> oracle(
    218       new VideoCaptureOracle(capture_period,
    219                              kAcceleratedSubscriberIsSupported));
    220   oracle_proxy_ =
    221       new ThreadSafeCaptureOracle(client.Pass(), oracle.Pass(), params);
    222 
    223   // Starts the capture machine asynchronously.
    224   BrowserThread::PostTaskAndReplyWithResult(
    225       BrowserThread::UI, FROM_HERE,
    226       base::Bind(&VideoCaptureMachine::Start,
    227                  base::Unretained(capture_machine_.get()),
    228                  oracle_proxy_),
    229       base::Bind(&VideoCaptureDeviceImpl::CaptureStarted,
    230                  AsWeakPtr()));
    231 
    232   TransitionStateTo(kCapturing);
    233 }
    234 
    235 void VideoCaptureDeviceImpl::StopAndDeAllocate() {
    236   DCHECK(thread_checker_.CalledOnValidThread());
    237 
    238   if (state_ != kCapturing)
    239     return;
    240 
    241   oracle_proxy_->Stop();
    242   oracle_proxy_ = NULL;
    243 
    244   TransitionStateTo(kIdle);
    245 
    246   // Stops the capture machine asynchronously.
    247   BrowserThread::PostTask(
    248       BrowserThread::UI, FROM_HERE, base::Bind(
    249           &VideoCaptureMachine::Stop,
    250           base::Unretained(capture_machine_.get())));
    251 }
    252 
    253 void VideoCaptureDeviceImpl::CaptureStarted(bool success) {
    254   DCHECK(thread_checker_.CalledOnValidThread());
    255   if (!success) {
    256     DVLOG(1) << "Failed to start capture machine.";
    257     Error();
    258   }
    259 }
    260 
    261 VideoCaptureDeviceImpl::VideoCaptureDeviceImpl(
    262     scoped_ptr<VideoCaptureMachine> capture_machine)
    263     : state_(kIdle),
    264       capture_machine_(capture_machine.Pass()) {}
    265 
    266 VideoCaptureDeviceImpl::~VideoCaptureDeviceImpl() {
    267   // If capture_machine is not NULL, then we need to return to the UI thread to
    268   // safely stop the capture machine.
    269   if (capture_machine_) {
    270     BrowserThread::PostTask(
    271         BrowserThread::UI, FROM_HERE, base::Bind(
    272             &DeleteCaptureMachineOnUIThread, base::Passed(&capture_machine_)));
    273   }
    274   DVLOG(1) << "VideoCaptureDeviceImpl@" << this << " destroying.";
    275 }
    276 
    277 void VideoCaptureDeviceImpl::TransitionStateTo(State next_state) {
    278   DCHECK(thread_checker_.CalledOnValidThread());
    279 
    280 #ifndef NDEBUG
    281   static const char* kStateNames[] = {
    282     "Idle", "Allocated", "Capturing", "Error"
    283   };
    284   DVLOG(1) << "State change: " << kStateNames[state_]
    285            << " --> " << kStateNames[next_state];
    286 #endif
    287 
    288   state_ = next_state;
    289 }
    290 
    291 void VideoCaptureDeviceImpl::Error() {
    292   DCHECK(thread_checker_.CalledOnValidThread());
    293 
    294   if (state_ == kIdle)
    295     return;
    296 
    297   if (oracle_proxy_)
    298     oracle_proxy_->ReportError();
    299 
    300   StopAndDeAllocate();
    301   TransitionStateTo(kError);
    302 }
    303 
    304 }  // namespace content
    305