• Home
  • History
  • Annotate
  • 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 "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 
FakeVideoCaptureDevice()23 FakeVideoCaptureDevice::FakeVideoCaptureDevice()
24     : capture_thread_("CaptureThread"),
25       frame_count_(0),
26       format_roster_index_(0) {}
27 
~FakeVideoCaptureDevice()28 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
29   DCHECK(thread_checker_.CalledOnValidThread());
30   DCHECK(!capture_thread_.IsRunning());
31 }
32 
AllocateAndStart(const VideoCaptureParams & params,scoped_ptr<VideoCaptureDevice::Client> client)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 
StopAndDeAllocate()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 
PopulateVariableFormatsRoster(const VideoCaptureFormats & formats)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 
OnAllocateAndStart(const VideoCaptureParams & params,scoped_ptr<VideoCaptureDevice::Client> client)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 
OnStopAndDeAllocate()94 void FakeVideoCaptureDevice::OnStopAndDeAllocate() {
95   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
96   client_.reset();
97 }
98 
OnCaptureTask()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   SkImageInfo info = SkImageInfo::MakeA8(capture_format_.frame_size.width(),
108                                          capture_format_.frame_size.height());
109   SkBitmap bitmap;
110   bitmap.installPixels(info, fake_frame_.get(), info.width());
111   SkCanvas canvas(bitmap);
112 
113   // Draw a sweeping circle to show an animation.
114   int radius = std::min(capture_format_.frame_size.width(),
115                         capture_format_.frame_size.height()) / 4;
116   SkRect rect =
117       SkRect::MakeXYWH(capture_format_.frame_size.width() / 2 - radius,
118                        capture_format_.frame_size.height() / 2 - radius,
119                        2 * radius,
120                        2 * radius);
121 
122   SkPaint paint;
123   paint.setStyle(SkPaint::kFill_Style);
124 
125   // Only Y plane is being drawn and this gives 50% grey on the Y
126   // plane. The result is a light green color in RGB space.
127   paint.setAlpha(128);
128 
129   int end_angle = (frame_count_ % kFakeCaptureBeepCycle * 360) /
130       kFakeCaptureBeepCycle;
131   if (!end_angle)
132     end_angle = 360;
133   canvas.drawArc(rect, 0, end_angle, true, paint);
134 
135   // Draw current time.
136   int elapsed_ms = kFakeCaptureTimeoutMs * frame_count_;
137   int milliseconds = elapsed_ms % 1000;
138   int seconds = (elapsed_ms / 1000) % 60;
139   int minutes = (elapsed_ms / 1000 / 60) % 60;
140   int hours = (elapsed_ms / 1000 / 60 / 60) % 60;
141 
142   std::string time_string =
143       base::StringPrintf("%d:%02d:%02d:%03d %d", hours, minutes,
144                          seconds, milliseconds, frame_count_);
145   canvas.scale(3, 3);
146   canvas.drawText(time_string.data(), time_string.length(), 30, 20,
147                   paint);
148 
149   if (frame_count_ % kFakeCaptureBeepCycle == 0) {
150     // Generate a synchronized beep sound if there is one audio input
151     // stream created.
152     FakeAudioInputStream::BeepOnce();
153   }
154 
155   frame_count_++;
156 
157   // Give the captured frame to the client.
158   client_->OnIncomingCapturedData(fake_frame_.get(),
159                                   frame_size,
160                                   capture_format_,
161                                   0,
162                                   base::TimeTicks::Now());
163   if (!(frame_count_ % kFakeCaptureCapabilityChangePeriod) &&
164       format_roster_.size() > 0U) {
165     Reallocate();
166   }
167   // Reschedule next CaptureTask.
168   capture_thread_.message_loop()->PostDelayedTask(
169       FROM_HERE,
170       base::Bind(&FakeVideoCaptureDevice::OnCaptureTask,
171                  base::Unretained(this)),
172       base::TimeDelta::FromMilliseconds(kFakeCaptureTimeoutMs));
173 }
174 
Reallocate()175 void FakeVideoCaptureDevice::Reallocate() {
176   DCHECK_EQ(capture_thread_.message_loop(), base::MessageLoop::current());
177   capture_format_ =
178       format_roster_.at(++format_roster_index_ % format_roster_.size());
179   DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_I420);
180   DVLOG(3) << "Reallocating FakeVideoCaptureDevice, new capture resolution "
181            << capture_format_.frame_size.ToString();
182 
183   const size_t fake_frame_size =
184       VideoFrame::AllocationSize(VideoFrame::I420, capture_format_.frame_size);
185   fake_frame_.reset(new uint8[fake_frame_size]);
186 }
187 
188 }  // namespace media
189