Home | History | Annotate | Download | only in capture
      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 "media/video/capture/fake_video_capture_device.h"
      6 
      7 #include <string>
      8 
      9 #include "base/bind.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/strings/stringprintf.h"
     12 #include "media/audio/fake_audio_input_stream.h"
     13 #include "media/base/video_frame.h"
     14 #include "third_party/skia/include/core/SkBitmap.h"
     15 #include "third_party/skia/include/core/SkCanvas.h"
     16 #include "third_party/skia/include/core/SkPaint.h"
     17 
     18 namespace media {
     19 
     20 static const int kFakeCaptureBeepCycle = 10;  // Visual beep every 0.5s.
     21 static const int kFakeCaptureCapabilityChangePeriod = 30;
     22 
     23 FakeVideoCaptureDevice::FakeVideoCaptureDevice()
     24     : capture_thread_("CaptureThread"),
     25       frame_count_(0),
     26       format_roster_index_(0) {}
     27 
     28 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
     29   DCHECK(thread_checker_.CalledOnValidThread());
     30   DCHECK(!capture_thread_.IsRunning());
     31 }
     32 
     33 void FakeVideoCaptureDevice::AllocateAndStart(
     34     const VideoCaptureParams& params,
     35     scoped_ptr<VideoCaptureDevice::Client> client) {
     36   DCHECK(thread_checker_.CalledOnValidThread());
     37   DCHECK(!capture_thread_.IsRunning());
     38 
     39   capture_thread_.Start();
     40   capture_thread_.message_loop()->PostTask(
     41       FROM_HERE,
     42       base::Bind(&FakeVideoCaptureDevice::OnAllocateAndStart,
     43                  base::Unretained(this),
     44                  params,
     45                  base::Passed(&client)));
     46 }
     47 
     48 void FakeVideoCaptureDevice::StopAndDeAllocate() {
     49   DCHECK(thread_checker_.CalledOnValidThread());
     50   DCHECK(capture_thread_.IsRunning());
     51   capture_thread_.message_loop()->PostTask(
     52       FROM_HERE,
     53       base::Bind(&FakeVideoCaptureDevice::OnStopAndDeAllocate,
     54                  base::Unretained(this)));
     55   capture_thread_.Stop();
     56 }
     57 
     58 void FakeVideoCaptureDevice::PopulateVariableFormatsRoster(
     59     const VideoCaptureFormats& formats) {
     60   DCHECK(thread_checker_.CalledOnValidThread());
     61   DCHECK(!capture_thread_.IsRunning());
     62   format_roster_ = formats;
     63   format_roster_index_ = 0;
     64 }
     65 
     66 void FakeVideoCaptureDevice::OnAllocateAndStart(
     67     const VideoCaptureParams& params,
     68     scoped_ptr<VideoCaptureDevice::Client> client) {
     69   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
     70   client_ = client.Pass();
     71 
     72   // Incoming |params| can be none of the supported formats, so we get the
     73   // closest thing rounded up. TODO(mcasas): Use the |params|, if they belong to
     74   // the supported ones, when http://crbug.com/309554 is verified.
     75   DCHECK_EQ(params.requested_format.pixel_format, PIXEL_FORMAT_I420);
     76   capture_format_.pixel_format = params.requested_format.pixel_format;
     77   capture_format_.frame_rate = 30;
     78   if (params.requested_format.frame_size.width() > 640)
     79       capture_format_.frame_size.SetSize(1280, 720);
     80   else if (params.requested_format.frame_size.width() > 320)
     81     capture_format_.frame_size.SetSize(640, 480);
     82   else
     83     capture_format_.frame_size.SetSize(320, 240);
     84   const size_t fake_frame_size =
     85       VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size);
     86   fake_frame_.reset(new uint8[fake_frame_size]);
     87 
     88   capture_thread_.message_loop()->PostTask(
     89       FROM_HERE,
     90       base::Bind(&FakeVideoCaptureDevice::OnCaptureTask,
     91                  base::Unretained(this)));
     92 }
     93 
     94 void FakeVideoCaptureDevice::OnStopAndDeAllocate() {
     95   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
     96   client_.reset();
     97 }
     98 
     99 void FakeVideoCaptureDevice::OnCaptureTask() {
    100   if (!client_)
    101     return;
    102 
    103   const size_t frame_size =
    104       VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size);
    105   memset(fake_frame_.get(), 0, frame_size);
    106 
    107   SkBitmap bitmap;
    108   bitmap.setConfig(SkBitmap::kA8_Config,
    109                    capture_format_.frame_size.width(),
    110                    capture_format_.frame_size.height(),
    111                    capture_format_.frame_size.width()),
    112       bitmap.setPixels(fake_frame_.get());
    113   SkCanvas canvas(bitmap);
    114 
    115   // Draw a sweeping circle to show an animation.
    116   int radius = std::min(capture_format_.frame_size.width(),
    117                         capture_format_.frame_size.height()) / 4;
    118   SkRect rect =
    119       SkRect::MakeXYWH(capture_format_.frame_size.width() / 2 - radius,
    120                        capture_format_.frame_size.height() / 2 - radius,
    121                        2 * radius,
    122                        2 * radius);
    123 
    124   SkPaint paint;
    125   paint.setStyle(SkPaint::kFill_Style);
    126 
    127   // Only Y plane is being drawn and this gives 50% grey on the Y
    128   // plane. The result is a light green color in RGB space.
    129   paint.setAlpha(128);
    130 
    131   int end_angle = (frame_count_ % kFakeCaptureBeepCycle * 360) /
    132       kFakeCaptureBeepCycle;
    133   if (!end_angle)
    134     end_angle = 360;
    135   canvas.drawArc(rect, 0, end_angle, true, paint);
    136 
    137   // Draw current time.
    138   int elapsed_ms = kFakeCaptureTimeoutMs * frame_count_;
    139   int milliseconds = elapsed_ms % 1000;
    140   int seconds = (elapsed_ms / 1000) % 60;
    141   int minutes = (elapsed_ms / 1000 / 60) % 60;
    142   int hours = (elapsed_ms / 1000 / 60 / 60) % 60;
    143 
    144   std::string time_string =
    145       base::StringPrintf("%d:%02d:%02d:%03d %d", hours, minutes,
    146                          seconds, milliseconds, frame_count_);
    147   canvas.scale(3, 3);
    148   canvas.drawText(time_string.data(), time_string.length(), 30, 20,
    149                   paint);
    150 
    151   if (frame_count_ % kFakeCaptureBeepCycle == 0) {
    152     // Generate a synchronized beep sound if there is one audio input
    153     // stream created.
    154     FakeAudioInputStream::BeepOnce();
    155   }
    156 
    157   frame_count_++;
    158 
    159   // Give the captured frame to the client.
    160   client_->OnIncomingCapturedData(fake_frame_.get(),
    161                                   frame_size,
    162                                   capture_format_,
    163                                   0,
    164                                   base::TimeTicks::Now());
    165   if (!(frame_count_ % kFakeCaptureCapabilityChangePeriod) &&
    166       format_roster_.size() > 0U) {
    167     Reallocate();
    168   }
    169   // Reschedule next CaptureTask.
    170   capture_thread_.message_loop()->PostDelayedTask(
    171       FROM_HERE,
    172       base::Bind(&FakeVideoCaptureDevice::OnCaptureTask,
    173                  base::Unretained(this)),
    174       base::TimeDelta::FromMilliseconds(kFakeCaptureTimeoutMs));
    175 }
    176 
    177 void FakeVideoCaptureDevice::Reallocate() {
    178   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
    179   capture_format_ =
    180       format_roster_.at(++format_roster_index_ % format_roster_.size());
    181   DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
    182   DVLOG(3) << "Reallocating FakeVideoCaptureDevice, new capture resolution "
    183            << capture_format_.frame_size.ToString();
    184 
    185   const size_t fake_frame_size =
    186       VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size);
    187   fake_frame_.reset(new uint8[fake_frame_size]);
    188 }
    189 
    190 }  // namespace media
    191