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.
5 #include "media/video/capture/fake_video_capture_device.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 "third_party/skia/include/core/SkBitmap.h"
14 #include "third_party/skia/include/core/SkCanvas.h"
15 #include "third_party/skia/include/core/SkPaint.h"
19 static const int kFakeCaptureTimeoutMs
= 50;
20 static const int kFakeCaptureBeepCycle
= 20; // Visual beep every 1s.
21 static const int kFakeCaptureCapabilityChangePeriod
= 30;
22 enum { kNumberOfFakeDevices
= 2 };
24 bool FakeVideoCaptureDevice::fail_next_create_
= false;
26 void FakeVideoCaptureDevice::GetDeviceNames(Names
* const device_names
) {
27 // Empty the name list.
28 device_names
->erase(device_names
->begin(), device_names
->end());
30 for (int n
= 0; n
< kNumberOfFakeDevices
; n
++) {
31 Name
name(base::StringPrintf("fake_device_%d", n
),
32 base::StringPrintf("/dev/video%d", n
));
33 device_names
->push_back(name
);
37 VideoCaptureDevice
* FakeVideoCaptureDevice::Create(const Name
& device_name
) {
38 if (fail_next_create_
) {
39 fail_next_create_
= false;
42 for (int n
= 0; n
< kNumberOfFakeDevices
; ++n
) {
43 std::string possible_id
= base::StringPrintf("/dev/video%d", n
);
44 if (device_name
.id().compare(possible_id
) == 0) {
45 return new FakeVideoCaptureDevice(device_name
);
51 void FakeVideoCaptureDevice::SetFailNextCreate() {
52 fail_next_create_
= true;
55 FakeVideoCaptureDevice::FakeVideoCaptureDevice(const Name
& device_name
)
56 : device_name_(device_name
),
59 capture_thread_("CaptureThread"),
61 capabilities_roster_index_(0) {
64 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
65 // Check if the thread is running.
66 // This means that the device have not been DeAllocated properly.
67 DCHECK(!capture_thread_
.IsRunning());
70 void FakeVideoCaptureDevice::Allocate(
71 const VideoCaptureCapability
& capture_format
,
72 EventHandler
* observer
) {
73 capture_format_
.frame_size_type
= capture_format
.frame_size_type
;
74 if (capture_format
.frame_size_type
== VariableResolutionVideoCaptureDevice
)
75 PopulateCapabilitiesRoster();
77 if (state_
!= kIdle
) {
78 return; // Wrong state.
82 capture_format_
.color
= VideoCaptureCapability::kI420
;
83 capture_format_
.expected_capture_delay
= 0;
84 capture_format_
.interlaced
= false;
85 if (capture_format
.width
> 320) { // VGA
86 capture_format_
.width
= 640;
87 capture_format_
.height
= 480;
88 capture_format_
.frame_rate
= 30;
90 capture_format_
.width
= 320;
91 capture_format_
.height
= 240;
92 capture_format_
.frame_rate
= 30;
95 const size_t fake_frame_size
= VideoFrame::AllocationSize(
97 gfx::Size(capture_format_
.width
, capture_format_
.height
));
98 fake_frame_
.reset(new uint8
[fake_frame_size
]);
101 observer_
->OnFrameInfo(capture_format_
);
104 void FakeVideoCaptureDevice::Reallocate() {
105 DCHECK_EQ(state_
, kCapturing
);
106 capture_format_
= capabilities_roster_
.at(++capabilities_roster_index_
%
107 capabilities_roster_
.size());
108 DCHECK_EQ(capture_format_
.color
, VideoCaptureCapability::kI420
);
109 DVLOG(3) << "Reallocating FakeVideoCaptureDevice, new capture resolution ("
110 << capture_format_
.width
<< "x" << capture_format_
.height
<< ")";
112 const size_t fake_frame_size
= VideoFrame::AllocationSize(
114 gfx::Size(capture_format_
.width
, capture_format_
.height
));
115 fake_frame_
.reset(new uint8
[fake_frame_size
]);
117 observer_
->OnFrameInfoChanged(capture_format_
);
120 void FakeVideoCaptureDevice::Start() {
121 if (state_
!= kAllocated
) {
122 return; // Wrong state.
125 capture_thread_
.Start();
126 capture_thread_
.message_loop()->PostTask(
128 base::Bind(&FakeVideoCaptureDevice::OnCaptureTask
,
129 base::Unretained(this)));
132 void FakeVideoCaptureDevice::Stop() {
133 if (state_
!= kCapturing
) {
134 return; // Wrong state.
136 capture_thread_
.Stop();
140 void FakeVideoCaptureDevice::DeAllocate() {
141 if (state_
!= kAllocated
&& state_
!= kCapturing
) {
142 return; // Wrong state.
144 capture_thread_
.Stop();
148 const VideoCaptureDevice::Name
& FakeVideoCaptureDevice::device_name() {
152 void FakeVideoCaptureDevice::OnCaptureTask() {
153 if (state_
!= kCapturing
) {
157 const size_t frame_size
= VideoFrame::AllocationSize(
159 gfx::Size(capture_format_
.width
, capture_format_
.height
));
160 memset(fake_frame_
.get(), 0, frame_size
);
163 bitmap
.setConfig(SkBitmap::kA8_Config
,
164 capture_format_
.width
,
165 capture_format_
.height
,
166 capture_format_
.width
);
167 bitmap
.setPixels(fake_frame_
.get());
169 SkCanvas
canvas(bitmap
);
171 // Draw a sweeping circle to show an animation.
172 int radius
= std::min(capture_format_
.width
, capture_format_
.height
) / 4;
173 SkRect rect
= SkRect::MakeXYWH(
174 capture_format_
.width
/ 2 - radius
, capture_format_
.height
/ 2 - radius
,
175 2 * radius
, 2 * radius
);
178 paint
.setStyle(SkPaint::kFill_Style
);
180 // Only Y plane is being drawn and this gives 50% grey on the Y
181 // plane. The result is a light green color in RGB space.
184 int end_angle
= (frame_count_
% kFakeCaptureBeepCycle
* 360) /
185 kFakeCaptureBeepCycle
;
188 canvas
.drawArc(rect
, 0, end_angle
, true, paint
);
190 // Draw current time.
191 int elapsed_ms
= kFakeCaptureTimeoutMs
* frame_count_
;
192 int milliseconds
= elapsed_ms
% 1000;
193 int seconds
= (elapsed_ms
/ 1000) % 60;
194 int minutes
= (elapsed_ms
/ 1000 / 60) % 60;
195 int hours
= (elapsed_ms
/ 1000 / 60 / 60) % 60;
197 std::string time_string
=
198 base::StringPrintf("%d:%02d:%02d:%03d %d", hours
, minutes
,
199 seconds
, milliseconds
, frame_count_
);
201 canvas
.drawText(time_string
.data(), time_string
.length(), 30, 20,
204 if (frame_count_
% kFakeCaptureBeepCycle
== 0) {
205 // Generate a synchronized beep sound if there is one audio input
207 FakeAudioInputStream::BeepOnce();
212 // Give the captured frame to the observer.
213 observer_
->OnIncomingCapturedFrame(
214 fake_frame_
.get(), frame_size
, base::Time::Now(), 0, false, false);
215 if (!(frame_count_
% kFakeCaptureCapabilityChangePeriod
) &&
216 (capture_format_
.frame_size_type
==
217 VariableResolutionVideoCaptureDevice
)) {
220 // Reschedule next CaptureTask.
221 capture_thread_
.message_loop()->PostDelayedTask(
223 base::Bind(&FakeVideoCaptureDevice::OnCaptureTask
,
224 base::Unretained(this)),
225 base::TimeDelta::FromMilliseconds(kFakeCaptureTimeoutMs
));
228 void FakeVideoCaptureDevice::PopulateCapabilitiesRoster() {
229 capabilities_roster_
.push_back(
230 media::VideoCaptureCapability(320,
233 VideoCaptureCapability::kI420
,
236 VariableResolutionVideoCaptureDevice
));
237 capabilities_roster_
.push_back(
238 media::VideoCaptureCapability(640,
241 VideoCaptureCapability::kI420
,
244 VariableResolutionVideoCaptureDevice
));
245 capabilities_roster_
.push_back(
246 media::VideoCaptureCapability(800,
249 VideoCaptureCapability::kI420
,
252 VariableResolutionVideoCaptureDevice
));
254 capabilities_roster_index_
= 0;