Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(569)

Side by Side Diff: media/capture/video/fake_video_capture_device.cc

Issue 2143903003: [WIP] Move media/capture to device/capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/capture/video/fake_video_capture_device.h"
6
7 #include <stddef.h>
8 #include <algorithm>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/single_thread_task_runner.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/threading/thread_task_runner_handle.h"
16 #include "media/audio/fake_audio_input_stream.h"
17 #include "media/base/video_frame.h"
18 #include "mojo/public/cpp/bindings/string.h"
19 #include "third_party/skia/include/core/SkBitmap.h"
20 #include "third_party/skia/include/core/SkCanvas.h"
21 #include "third_party/skia/include/core/SkMatrix.h"
22 #include "third_party/skia/include/core/SkPaint.h"
23 #include "ui/gfx/codec/png_codec.h"
24
25 namespace media {
26
27 // Sweep at 600 deg/sec.
28 static const float kPacmanAngularVelocity = 600;
29 // Beep every 500 ms.
30 static const int kBeepInterval = 500;
31
32 static const uint32_t kMinZoom = 100;
33 static const uint32_t kMaxZoom = 400;
34
35 void DrawPacman(bool use_argb,
36 uint8_t* const data,
37 base::TimeDelta elapsed_time,
38 float frame_rate,
39 const gfx::Size& frame_size,
40 uint32_t zoom) {
41 // |kN32_SkColorType| stands for the appropriate RGBA/BGRA format.
42 const SkColorType colorspace =
43 use_argb ? kN32_SkColorType : kAlpha_8_SkColorType;
44 const SkImageInfo info = SkImageInfo::Make(
45 frame_size.width(), frame_size.height(), colorspace, kOpaque_SkAlphaType);
46 SkBitmap bitmap;
47 bitmap.setInfo(info);
48 bitmap.setPixels(data);
49 SkPaint paint;
50 paint.setStyle(SkPaint::kFill_Style);
51 SkCanvas canvas(bitmap);
52
53 const SkScalar unscaled_zoom = zoom / 100.f;
54 SkMatrix matrix;
55 matrix.setScale(unscaled_zoom, unscaled_zoom, frame_size.width() / 2,
56 frame_size.height() / 2);
57 canvas.setMatrix(matrix);
58
59 // Equalize Alpha_8 that has light green background while RGBA has white.
60 if (use_argb) {
61 const SkRect full_frame =
62 SkRect::MakeWH(frame_size.width(), frame_size.height());
63 paint.setARGB(255, 0, 127, 0);
64 canvas.drawRect(full_frame, paint);
65 }
66 paint.setColor(SK_ColorGREEN);
67
68 // Draw a sweeping circle to show an animation.
69 const float end_angle =
70 fmod(kPacmanAngularVelocity * elapsed_time.InSecondsF(), 361);
71 const int radius = std::min(frame_size.width(), frame_size.height()) / 4;
72 const SkRect rect = SkRect::MakeXYWH(frame_size.width() / 2 - radius,
73 frame_size.height() / 2 - radius,
74 2 * radius, 2 * radius);
75 canvas.drawArc(rect, 0, end_angle, true, paint);
76
77 // Draw current time.
78 const int milliseconds = elapsed_time.InMilliseconds() % 1000;
79 const int seconds = elapsed_time.InSeconds() % 60;
80 const int minutes = elapsed_time.InMinutes() % 60;
81 const int hours = elapsed_time.InHours();
82 const int frame_count = elapsed_time.InMilliseconds() * frame_rate / 1000;
83
84 const std::string time_string =
85 base::StringPrintf("%d:%02d:%02d:%03d %d", hours, minutes, seconds,
86 milliseconds, frame_count);
87 canvas.scale(3, 3);
88 canvas.drawText(time_string.data(), time_string.length(), 30, 20, paint);
89 }
90
91 // Creates a PNG-encoded frame and sends it back to |callback|. The other
92 // parameters are used to replicate the PacMan rendering.
93 void DoTakeFakePhoto(VideoCaptureDevice::TakePhotoCallback callback,
94 const VideoCaptureFormat& capture_format,
95 base::TimeDelta elapsed_time,
96 float fake_capture_rate,
97 uint32_t zoom) {
98 std::unique_ptr<uint8_t[]> buffer(new uint8_t[VideoFrame::AllocationSize(
99 PIXEL_FORMAT_ARGB, capture_format.frame_size)]);
100
101 DrawPacman(true /* use_argb */, buffer.get(), elapsed_time, fake_capture_rate,
102 capture_format.frame_size, zoom);
103
104 std::vector<uint8_t> encoded_data;
105 const bool result = gfx::PNGCodec::Encode(
106 buffer.get(), gfx::PNGCodec::FORMAT_RGBA, capture_format.frame_size,
107 capture_format.frame_size.width() * 4, true /* discard_transparency */,
108 std::vector<gfx::PNGCodec::Comment>(), &encoded_data);
109 DCHECK(result);
110
111 callback.Run(mojo::String::From("image/png"),
112 mojo::Array<uint8_t>::From(encoded_data));
113 }
114
115 FakeVideoCaptureDevice::FakeVideoCaptureDevice(BufferOwnership buffer_ownership,
116 float fake_capture_rate)
117 : buffer_ownership_(buffer_ownership),
118 fake_capture_rate_(fake_capture_rate),
119 current_zoom_(kMinZoom),
120 weak_factory_(this) {}
121
122 FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
123 DCHECK(thread_checker_.CalledOnValidThread());
124 }
125
126 void FakeVideoCaptureDevice::AllocateAndStart(
127 const VideoCaptureParams& params,
128 std::unique_ptr<VideoCaptureDevice::Client> client) {
129 DCHECK(thread_checker_.CalledOnValidThread());
130
131 client_ = std::move(client);
132
133 // Incoming |params| can be none of the supported formats, so we get the
134 // closest thing rounded up. TODO(mcasas): Use the |params|, if they belong to
135 // the supported ones, when http://crbug.com/309554 is verified.
136 capture_format_.frame_rate = fake_capture_rate_;
137 if (params.requested_format.frame_size.width() > 1280)
138 capture_format_.frame_size.SetSize(1920, 1080);
139 else if (params.requested_format.frame_size.width() > 640)
140 capture_format_.frame_size.SetSize(1280, 720);
141 else if (params.requested_format.frame_size.width() > 320)
142 capture_format_.frame_size.SetSize(640, 480);
143 else
144 capture_format_.frame_size.SetSize(320, 240);
145
146 if (buffer_ownership_ == BufferOwnership::CLIENT_BUFFERS) {
147 capture_format_.pixel_storage = PIXEL_STORAGE_CPU;
148 capture_format_.pixel_format = PIXEL_FORMAT_ARGB;
149 DVLOG(1) << "starting with client argb buffers";
150 } else if (buffer_ownership_ == BufferOwnership::OWN_BUFFERS) {
151 capture_format_.pixel_storage = PIXEL_STORAGE_CPU;
152 capture_format_.pixel_format = PIXEL_FORMAT_I420;
153 DVLOG(1) << "starting with own I420 buffers";
154 }
155
156 if (capture_format_.pixel_format == PIXEL_FORMAT_I420) {
157 fake_frame_.reset(new uint8_t[VideoFrame::AllocationSize(
158 PIXEL_FORMAT_I420, capture_format_.frame_size)]);
159 }
160
161 beep_time_ = base::TimeDelta();
162 elapsed_time_ = base::TimeDelta();
163
164 if (buffer_ownership_ == BufferOwnership::CLIENT_BUFFERS)
165 BeepAndScheduleNextCapture(
166 base::TimeTicks::Now(),
167 base::Bind(&FakeVideoCaptureDevice::CaptureUsingClientBuffers,
168 weak_factory_.GetWeakPtr()));
169 else if (buffer_ownership_ == BufferOwnership::OWN_BUFFERS)
170 BeepAndScheduleNextCapture(
171 base::TimeTicks::Now(),
172 base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers,
173 weak_factory_.GetWeakPtr()));
174 }
175
176 void FakeVideoCaptureDevice::StopAndDeAllocate() {
177 DCHECK(thread_checker_.CalledOnValidThread());
178 client_.reset();
179 }
180
181 void FakeVideoCaptureDevice::GetPhotoCapabilities(
182 GetPhotoCapabilitiesCallback callback) {
183 mojom::PhotoCapabilitiesPtr photo_capabilities =
184 mojom::PhotoCapabilities::New();
185 photo_capabilities->zoom = mojom::Range::New();
186 photo_capabilities->zoom->current = current_zoom_;
187 photo_capabilities->zoom->max = kMaxZoom;
188 photo_capabilities->zoom->min = kMinZoom;
189 callback.Run(std::move(photo_capabilities));
190 }
191
192 void FakeVideoCaptureDevice::SetPhotoOptions(mojom::PhotoSettingsPtr settings,
193 SetPhotoOptionsCallback callback) {
194 if (settings->has_zoom)
195 current_zoom_ = std::max(kMinZoom, std::min(settings->zoom, kMaxZoom));
196 callback.Run(true);
197 }
198
199 void FakeVideoCaptureDevice::TakePhoto(TakePhotoCallback callback) {
200 base::ThreadTaskRunnerHandle::Get()->PostTask(
201 FROM_HERE,
202 base::Bind(&DoTakeFakePhoto, base::Passed(&callback), capture_format_,
203 elapsed_time_, fake_capture_rate_, current_zoom_));
204 }
205
206 void FakeVideoCaptureDevice::CaptureUsingOwnBuffers(
207 base::TimeTicks expected_execution_time) {
208 DCHECK(thread_checker_.CalledOnValidThread());
209 const size_t frame_size = capture_format_.ImageAllocationSize();
210 memset(fake_frame_.get(), 0, frame_size);
211
212 DrawPacman(false /* use_argb */, fake_frame_.get(), elapsed_time_,
213 fake_capture_rate_, capture_format_.frame_size, current_zoom_);
214
215 // Give the captured frame to the client.
216 base::TimeTicks now = base::TimeTicks::Now();
217 if (first_ref_time_.is_null())
218 first_ref_time_ = now;
219 client_->OnIncomingCapturedData(fake_frame_.get(), frame_size,
220 capture_format_, 0 /* rotation */, now,
221 now - first_ref_time_);
222 BeepAndScheduleNextCapture(
223 expected_execution_time,
224 base::Bind(&FakeVideoCaptureDevice::CaptureUsingOwnBuffers,
225 weak_factory_.GetWeakPtr()));
226 }
227
228 void FakeVideoCaptureDevice::CaptureUsingClientBuffers(
229 base::TimeTicks expected_execution_time) {
230 DCHECK(thread_checker_.CalledOnValidThread());
231
232 std::unique_ptr<VideoCaptureDevice::Client::Buffer> capture_buffer(
233 client_->ReserveOutputBuffer(capture_format_.frame_size,
234 capture_format_.pixel_format,
235 capture_format_.pixel_storage));
236 DLOG_IF(ERROR, !capture_buffer) << "Couldn't allocate Capture Buffer";
237 DCHECK(capture_buffer->data()) << "Buffer has NO backing memory";
238
239 if (capture_format_.pixel_storage == PIXEL_STORAGE_GPUMEMORYBUFFER &&
240 capture_format_.pixel_format == media::PIXEL_FORMAT_I420) {
241 // Since SkBitmap expects a packed&continuous memory region for I420, we
242 // need to use |fake_frame_| to draw onto.
243 memset(fake_frame_.get(), 0, capture_format_.ImageAllocationSize());
244 DrawPacman(false /* use_argb */, fake_frame_.get(), elapsed_time_,
245 fake_capture_rate_, capture_format_.frame_size, current_zoom_);
246
247 // Copy data from |fake_frame_| into the reserved planes of GpuMemoryBuffer.
248 size_t offset = 0;
249 for (size_t i = 0; i < VideoFrame::NumPlanes(PIXEL_FORMAT_I420); ++i) {
250 const size_t plane_size =
251 VideoFrame::PlaneSize(PIXEL_FORMAT_I420, i,
252 capture_format_.frame_size)
253 .GetArea();
254 memcpy(capture_buffer->data(i), fake_frame_.get() + offset, plane_size);
255 offset += plane_size;
256 }
257 } else {
258 DCHECK_EQ(capture_format_.pixel_storage, PIXEL_STORAGE_CPU);
259 DCHECK_EQ(capture_format_.pixel_format, PIXEL_FORMAT_ARGB);
260 uint8_t* data_ptr = static_cast<uint8_t*>(capture_buffer->data());
261 memset(data_ptr, 0, capture_buffer->mapped_size());
262 DrawPacman(true /* use_argb */, data_ptr, elapsed_time_, fake_capture_rate_,
263 capture_format_.frame_size, current_zoom_);
264 }
265
266 // Give the captured frame to the client.
267 base::TimeTicks now = base::TimeTicks::Now();
268 if (first_ref_time_.is_null())
269 first_ref_time_ = now;
270 client_->OnIncomingCapturedBuffer(std::move(capture_buffer), capture_format_,
271 now, now - first_ref_time_);
272
273 BeepAndScheduleNextCapture(
274 expected_execution_time,
275 base::Bind(&FakeVideoCaptureDevice::CaptureUsingClientBuffers,
276 weak_factory_.GetWeakPtr()));
277 }
278
279 void FakeVideoCaptureDevice::BeepAndScheduleNextCapture(
280 base::TimeTicks expected_execution_time,
281 const base::Callback<void(base::TimeTicks)>& next_capture) {
282 const base::TimeDelta beep_interval =
283 base::TimeDelta::FromMilliseconds(kBeepInterval);
284 const base::TimeDelta frame_interval =
285 base::TimeDelta::FromMicroseconds(1e6 / fake_capture_rate_);
286 beep_time_ += frame_interval;
287 elapsed_time_ += frame_interval;
288
289 // Generate a synchronized beep twice per second.
290 if (beep_time_ >= beep_interval) {
291 FakeAudioInputStream::BeepOnce();
292 beep_time_ -= beep_interval;
293 }
294
295 // Reschedule next CaptureTask.
296 const base::TimeTicks current_time = base::TimeTicks::Now();
297 // Don't accumulate any debt if we are lagging behind - just post the next
298 // frame immediately and continue as normal.
299 const base::TimeTicks next_execution_time =
300 std::max(current_time, expected_execution_time + frame_interval);
301 const base::TimeDelta delay = next_execution_time - current_time;
302 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
303 FROM_HERE, base::Bind(next_capture, next_execution_time), delay);
304 }
305
306 } // namespace media
OLDNEW
« no previous file with comments | « media/capture/video/fake_video_capture_device.h ('k') | media/capture/video/fake_video_capture_device_factory.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698