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