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/capture/video/fake_video_capture_device.h"
10 #include "base/strings/stringprintf.h"
11 #include "media/audio/fake_audio_input_stream.h"
12 #include "media/base/video_frame.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 kFakeCaptureBeepCycle
= 10; // Visual beep every 0.5s.
21 void DrawPacman(bool use_argb
,
25 const gfx::Size
& frame_size
) {
26 // |kN32_SkColorType| stands for the appropriiate RGBA/BGRA format.
27 const SkColorType colorspace
=
28 use_argb
? kN32_SkColorType
: kAlpha_8_SkColorType
;
29 const SkImageInfo info
= SkImageInfo::Make(
30 frame_size
.width(), frame_size
.height(), colorspace
, kOpaque_SkAlphaType
);
33 bitmap
.setPixels(data
);
35 paint
.setStyle(SkPaint::kFill_Style
);
36 SkCanvas
canvas(bitmap
);
38 // Equalize Alpha_8 that has light green background while RGBA has white.
40 const SkRect full_frame
=
41 SkRect::MakeWH(frame_size
.width(), frame_size
.height());
42 paint
.setARGB(255, 0, 127, 0);
43 canvas
.drawRect(full_frame
, paint
);
45 paint
.setColor(SK_ColorGREEN
);
47 // Draw a sweeping circle to show an animation.
48 const int end_angle
= (3 * kFakeCaptureBeepCycle
* frame_count
% 361);
49 const int radius
= std::min(frame_size
.width(), frame_size
.height()) / 4;
50 const SkRect rect
= SkRect::MakeXYWH(frame_size
.width() / 2 - radius
,
51 frame_size
.height() / 2 - radius
,
52 2 * radius
, 2 * radius
);
53 canvas
.drawArc(rect
, 0, end_angle
, true, paint
);
56 const int elapsed_ms
= frame_interval
* frame_count
;
57 const int milliseconds
= elapsed_ms
% 1000;
58 const int seconds
= (elapsed_ms
/ 1000) % 60;
59 const int minutes
= (elapsed_ms
/ 1000 / 60) % 60;
60 const int hours
= (elapsed_ms
/ 1000 / 60 / 60) % 60;
62 const std::string time_string
=
63 base::StringPrintf("%d:%02d:%02d:%03d %d", hours
, minutes
, seconds
,
64 milliseconds
, frame_count
);
66 canvas
.drawText(time_string
.data(), time_string
.length(), 30, 20, paint
);
69 FakeVideoCaptureDevice::FakeVideoCaptureDevice(
70 FakeVideoCaptureDeviceType device_type
)
71 : device_type_(device_type
), frame_count_(0), weak_factory_(this) {
74 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
75 DCHECK(thread_checker_
.CalledOnValidThread());
78 void FakeVideoCaptureDevice::AllocateAndStart(
79 const VideoCaptureParams
& params
,
80 scoped_ptr
<VideoCaptureDevice::Client
> client
) {
81 DCHECK(thread_checker_
.CalledOnValidThread());
83 client_
= client
.Pass();
85 // Incoming |params| can be none of the supported formats, so we get the
86 // closest thing rounded up. TODO(mcasas): Use the |params|, if they belong to
87 // the supported ones, when http://crbug.com/309554 is verified.
88 DCHECK_EQ(params
.requested_format
.pixel_format
,
89 VIDEO_CAPTURE_PIXEL_FORMAT_I420
);
90 capture_format_
.pixel_format
= params
.requested_format
.pixel_format
;
91 capture_format_
.frame_rate
= 30.0;
92 if (params
.requested_format
.frame_size
.width() > 1280)
93 capture_format_
.frame_size
.SetSize(1920, 1080);
94 else if (params
.requested_format
.frame_size
.width() > 640)
95 capture_format_
.frame_size
.SetSize(1280, 720);
96 else if (params
.requested_format
.frame_size
.width() > 320)
97 capture_format_
.frame_size
.SetSize(640, 480);
99 capture_format_
.frame_size
.SetSize(320, 240);
101 if (device_type_
== USING_OWN_BUFFERS
||
102 device_type_
== USING_OWN_BUFFERS_TRIPLANAR
) {
103 capture_format_
.pixel_storage
= PIXEL_STORAGE_CPU
;
104 fake_frame_
.reset(new uint8
[VideoFrame::AllocationSize(
105 PIXEL_FORMAT_I420
, capture_format_
.frame_size
)]);
106 BeepAndScheduleNextCapture(
107 base::TimeTicks::Now(),
108 base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers
,
109 weak_factory_
.GetWeakPtr()));
110 } else if (device_type_
== USING_CLIENT_BUFFERS
) {
111 DVLOG(1) << "starting with "
112 << (params
.use_gpu_memory_buffers
? "GMB" : "ShMem");
113 BeepAndScheduleNextCapture(
114 base::TimeTicks::Now(),
115 base::Bind(&FakeVideoCaptureDevice::CaptureUsingClientBuffers
,
116 weak_factory_
.GetWeakPtr(),
117 params
.use_gpu_memory_buffers
118 ? VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
119 : VIDEO_CAPTURE_PIXEL_FORMAT_I420
,
120 params
.use_gpu_memory_buffers
? PIXEL_STORAGE_GPUMEMORYBUFFER
121 : PIXEL_STORAGE_CPU
));
123 client_
->OnError("Unknown Fake Video Capture Device type.");
127 void FakeVideoCaptureDevice::StopAndDeAllocate() {
128 DCHECK(thread_checker_
.CalledOnValidThread());
132 void FakeVideoCaptureDevice::CaptureUsingOwnBuffers(
133 base::TimeTicks expected_execution_time
) {
134 DCHECK(thread_checker_
.CalledOnValidThread());
135 const size_t frame_size
= capture_format_
.ImageAllocationSize();
136 memset(fake_frame_
.get(), 0, frame_size
);
138 DrawPacman(false /* use_argb */, fake_frame_
.get(), frame_count_
,
139 kFakeCapturePeriodMs
, capture_format_
.frame_size
);
141 // Give the captured frame to the client.
142 if (device_type_
== USING_OWN_BUFFERS
) {
143 client_
->OnIncomingCapturedData(fake_frame_
.get(), frame_size
,
144 capture_format_
, 0 /* rotation */,
145 base::TimeTicks::Now());
146 } else if (device_type_
== USING_OWN_BUFFERS_TRIPLANAR
) {
147 client_
->OnIncomingCapturedYuvData(
149 fake_frame_
.get() + capture_format_
.frame_size
.GetArea(),
150 fake_frame_
.get() + capture_format_
.frame_size
.GetArea() * 5 / 4,
151 capture_format_
.frame_size
.width(),
152 capture_format_
.frame_size
.width() / 2,
153 capture_format_
.frame_size
.width() / 2, capture_format_
,
154 0 /* rotation */, base::TimeTicks::Now());
156 BeepAndScheduleNextCapture(
157 expected_execution_time
,
158 base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers
,
159 weak_factory_
.GetWeakPtr()));
162 void FakeVideoCaptureDevice::CaptureUsingClientBuffers(
163 VideoCapturePixelFormat pixel_format
,
164 VideoPixelStorage pixel_storage
,
165 base::TimeTicks expected_execution_time
) {
166 DCHECK(thread_checker_
.CalledOnValidThread());
168 scoped_ptr
<VideoCaptureDevice::Client::Buffer
> capture_buffer(
169 client_
->ReserveOutputBuffer(capture_format_
.frame_size
, pixel_format
,
171 DLOG_IF(ERROR
, !capture_buffer
) << "Couldn't allocate Capture Buffer";
173 if (capture_buffer
.get()) {
174 uint8_t* const data_ptr
= static_cast<uint8_t*>(capture_buffer
->data());
175 DCHECK(data_ptr
) << "Buffer has NO backing memory";
176 memset(data_ptr
, 0, capture_buffer
->size());
179 (pixel_format
== media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
), /* use_argb */
180 data_ptr
, frame_count_
, kFakeCapturePeriodMs
,
181 capture_format_
.frame_size
);
183 // Give the captured frame to the client.
184 const VideoCaptureFormat
format(capture_format_
.frame_size
,
185 capture_format_
.frame_rate
, pixel_format
,
187 client_
->OnIncomingCapturedBuffer(capture_buffer
.Pass(), format
,
188 base::TimeTicks::Now());
191 BeepAndScheduleNextCapture(
192 expected_execution_time
,
193 base::Bind(&FakeVideoCaptureDevice::CaptureUsingClientBuffers
,
194 weak_factory_
.GetWeakPtr(), pixel_format
, pixel_storage
));
197 void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
198 base::TimeTicks expected_execution_time
,
199 const base::Callback
<void(base::TimeTicks
)>& next_capture
) {
200 // Generate a synchronized beep sound every so many frames.
201 if (frame_count_
++ % kFakeCaptureBeepCycle
== 0)
202 FakeAudioInputStream::BeepOnce();
204 // Reschedule next CaptureTask.
205 const base::TimeTicks current_time
= base::TimeTicks::Now();
206 const base::TimeDelta frame_interval
=
207 base::TimeDelta::FromMilliseconds(kFakeCapturePeriodMs
);
208 // Don't accumulate any debt if we are lagging behind - just post the next
209 // frame immediately and continue as normal.
210 const base::TimeTicks next_execution_time
=
211 std::max(current_time
, expected_execution_time
+ frame_interval
);
212 const base::TimeDelta delay
= next_execution_time
- current_time
;
213 base::MessageLoop::current()->PostDelayedTask(
214 FROM_HERE
, base::Bind(next_capture
, next_execution_time
), delay
);