OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 <errno.h> | |
6 #include <fcntl.h> | |
7 #include <linux/videodev2.h> | |
8 #include <poll.h> | |
9 #include <string.h> | |
10 #include <sys/eventfd.h> | |
11 #include <sys/ioctl.h> | |
12 #include <sys/mman.h> | |
13 | |
14 #include "base/bind.h" | |
15 #include "base/bind_helpers.h" | |
16 #include "base/callback.h" | |
17 #include "base/numerics/safe_conversions.h" | |
18 #include "base/thread_task_runner_handle.h" | |
19 #include "content/common/gpu/media/v4l2_image_processor.h" | |
20 | |
21 #define IOCTL_OR_ERROR_RETURN_VALUE(type, arg, value, type_str) \ | |
22 do { \ | |
23 if (device_->Ioctl(type, arg) != 0) { \ | |
24 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << type_str; \ | |
25 return value; \ | |
26 } \ | |
27 } while (0) | |
28 | |
29 #define IOCTL_OR_ERROR_RETURN(type, arg) \ | |
30 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, ((void)0), #type) | |
31 | |
32 #define IOCTL_OR_ERROR_RETURN_FALSE(type, arg) \ | |
33 IOCTL_OR_ERROR_RETURN_VALUE(type, arg, false, #type) | |
34 | |
35 #define IOCTL_OR_LOG_ERROR(type, arg) \ | |
36 do { \ | |
37 if (device_->Ioctl(type, arg) != 0) \ | |
38 PLOG(ERROR) << __func__ << "(): ioctl() failed: " << #type; \ | |
39 } while (0) | |
40 | |
41 namespace content { | |
42 | |
43 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) { | |
44 } | |
45 | |
46 V4L2ImageProcessor::InputRecord::~InputRecord() { | |
47 } | |
48 | |
49 V4L2ImageProcessor::OutputRecord::OutputRecord() : at_device(false) {} | |
50 | |
51 V4L2ImageProcessor::OutputRecord::~OutputRecord() { | |
52 } | |
53 | |
54 V4L2ImageProcessor::JobRecord::JobRecord() : output_buffer_index(-1) {} | |
55 | |
56 V4L2ImageProcessor::JobRecord::~JobRecord() { | |
57 } | |
58 | |
59 V4L2ImageProcessor::V4L2ImageProcessor(const scoped_refptr<V4L2Device>& device) | |
60 : input_format_(media::PIXEL_FORMAT_UNKNOWN), | |
61 output_format_(media::PIXEL_FORMAT_UNKNOWN), | |
62 input_memory_type_(V4L2_MEMORY_USERPTR), | |
63 input_format_fourcc_(0), | |
64 output_format_fourcc_(0), | |
65 input_planes_count_(0), | |
66 output_planes_count_(0), | |
67 child_task_runner_(base::ThreadTaskRunnerHandle::Get()), | |
68 device_(device), | |
69 device_thread_("V4L2ImageProcessorThread"), | |
70 device_poll_thread_("V4L2ImageProcessorDevicePollThread"), | |
71 input_streamon_(false), | |
72 input_buffer_queued_count_(0), | |
73 output_streamon_(false), | |
74 output_buffer_queued_count_(0), | |
75 num_buffers_(0), | |
76 weak_this_factory_(this) { | |
77 weak_this_ = weak_this_factory_.GetWeakPtr(); | |
78 } | |
79 | |
80 V4L2ImageProcessor::~V4L2ImageProcessor() { | |
81 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
82 DCHECK(!device_thread_.IsRunning()); | |
83 DCHECK(!device_poll_thread_.IsRunning()); | |
84 | |
85 DestroyInputBuffers(); | |
86 DestroyOutputBuffers(); | |
87 } | |
88 | |
89 void V4L2ImageProcessor::NotifyError() { | |
90 LOG(ERROR) << __func__; | |
91 DCHECK(!child_task_runner_->BelongsToCurrentThread()); | |
92 child_task_runner_->PostTask( | |
93 FROM_HERE, base::Bind(&V4L2ImageProcessor::NotifyErrorOnChildThread, | |
94 weak_this_, error_cb_)); | |
95 } | |
96 | |
97 void V4L2ImageProcessor::NotifyErrorOnChildThread( | |
98 const base::Closure& error_cb) { | |
99 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
100 error_cb_.Run(); | |
101 } | |
102 | |
103 bool V4L2ImageProcessor::Initialize(media::VideoPixelFormat input_format, | |
104 media::VideoPixelFormat output_format, | |
105 v4l2_memory input_memory_type, | |
106 gfx::Size input_visible_size, | |
107 gfx::Size input_allocated_size, | |
108 gfx::Size output_visible_size, | |
109 gfx::Size output_allocated_size, | |
110 int num_buffers, | |
111 const base::Closure& error_cb) { | |
112 DCHECK(!error_cb.is_null()); | |
113 DCHECK_GT(num_buffers, 0); | |
114 DCHECK(input_memory_type == V4L2_MEMORY_USERPTR || | |
115 input_memory_type == V4L2_MEMORY_DMABUF); | |
116 error_cb_ = error_cb; | |
117 | |
118 input_format_ = input_format; | |
119 output_format_ = output_format; | |
120 input_format_fourcc_ = V4L2Device::VideoPixelFormatToV4L2PixFmt(input_format); | |
121 output_format_fourcc_ = | |
122 V4L2Device::VideoPixelFormatToV4L2PixFmt(output_format); | |
123 num_buffers_ = num_buffers; | |
124 | |
125 if (!input_format_fourcc_ || !output_format_fourcc_) { | |
126 LOG(ERROR) << "Unrecognized format(s)"; | |
127 return false; | |
128 } | |
129 | |
130 input_memory_type_ = input_memory_type; | |
131 input_visible_size_ = input_visible_size; | |
132 input_allocated_size_ = input_allocated_size; | |
133 output_visible_size_ = output_visible_size; | |
134 output_allocated_size_ = output_allocated_size; | |
135 | |
136 // Capabilities check. | |
137 struct v4l2_capability caps; | |
138 memset(&caps, 0, sizeof(caps)); | |
139 const __u32 kCapsRequired = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; | |
140 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QUERYCAP, &caps); | |
141 if ((caps.capabilities & kCapsRequired) != kCapsRequired) { | |
142 LOG(ERROR) << "Initialize(): ioctl() failed: VIDIOC_QUERYCAP: " | |
143 "caps check failed: 0x" << std::hex << caps.capabilities; | |
144 return false; | |
145 } | |
146 | |
147 if (!CreateInputBuffers() || !CreateOutputBuffers()) | |
148 return false; | |
149 | |
150 if (!device_thread_.Start()) { | |
151 LOG(ERROR) << "Initialize(): encoder thread failed to start"; | |
152 return false; | |
153 } | |
154 | |
155 // StartDevicePoll will NotifyError on failure, so IgnoreResult is fine here. | |
156 device_thread_.message_loop()->PostTask( | |
157 FROM_HERE, | |
158 base::Bind(base::IgnoreResult(&V4L2ImageProcessor::StartDevicePoll), | |
159 base::Unretained(this))); | |
160 | |
161 DVLOG(1) << "V4L2ImageProcessor initialized for " | |
162 << " input_format:" << media::VideoPixelFormatToString(input_format) | |
163 << ", output_format:" | |
164 << media::VideoPixelFormatToString(output_format) | |
165 << ", input_visible_size: " << input_visible_size.ToString() | |
166 << ", input_allocated_size: " << input_allocated_size_.ToString() | |
167 << ", input_planes_count: " << input_planes_count_ | |
168 << ", output_visible_size: " << output_visible_size.ToString() | |
169 << ", output_allocated_size: " << output_allocated_size_.ToString() | |
170 << ", output_planes_count: " << output_planes_count_; | |
171 | |
172 return true; | |
173 } | |
174 | |
175 std::vector<base::ScopedFD> V4L2ImageProcessor::GetDmabufsForOutputBuffer( | |
176 int output_buffer_index) { | |
177 DCHECK_GE(output_buffer_index, 0); | |
178 DCHECK_LT(output_buffer_index, num_buffers_); | |
179 return device_->GetDmabufsForV4L2Buffer(output_buffer_index, | |
180 output_planes_count_, | |
181 V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); | |
182 } | |
183 | |
184 std::vector<uint32_t> V4L2ImageProcessor::GetSupportedInputFormats() { | |
185 std::vector<uint32_t> input_formats; | |
186 v4l2_fmtdesc fmtdesc; | |
187 memset(&fmtdesc, 0, sizeof(fmtdesc)); | |
188 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
189 for (; device_->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) { | |
190 input_formats.push_back(fmtdesc.pixelformat); | |
191 } | |
192 return input_formats; | |
193 } | |
194 | |
195 std::vector<uint32_t> V4L2ImageProcessor::GetSupportedOutputFormats() { | |
196 std::vector<uint32_t> output_formats; | |
197 v4l2_fmtdesc fmtdesc; | |
198 memset(&fmtdesc, 0, sizeof(fmtdesc)); | |
199 fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
200 for (; device_->Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) { | |
201 output_formats.push_back(fmtdesc.pixelformat); | |
202 } | |
203 return output_formats; | |
204 } | |
205 | |
206 bool V4L2ImageProcessor::TryOutputFormat(uint32_t pixelformat, | |
207 gfx::Size* size, | |
208 size_t* num_planes) { | |
209 DVLOG(1) << __func__ << ": size=" << size->ToString(); | |
210 struct v4l2_format format; | |
211 memset(&format, 0, sizeof(format)); | |
212 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
213 format.fmt.pix_mp.width = size->width(); | |
214 format.fmt.pix_mp.height = size->height(); | |
215 format.fmt.pix_mp.pixelformat = pixelformat; | |
216 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_TRY_FMT, &format); | |
217 | |
218 *num_planes = format.fmt.pix_mp.num_planes; | |
219 *size = V4L2Device::CodedSizeFromV4L2Format(format); | |
220 DVLOG(1) << __func__ << ": adjusted output coded size=" << size->ToString() | |
221 << ", num_planes=" << *num_planes; | |
222 return true; | |
223 } | |
224 | |
225 void V4L2ImageProcessor::Process(const scoped_refptr<media::VideoFrame>& frame, | |
226 int output_buffer_index, | |
227 const FrameReadyCB& cb) { | |
228 DVLOG(3) << __func__ << ": ts=" << frame->timestamp().InMilliseconds(); | |
229 | |
230 std::unique_ptr<JobRecord> job_record(new JobRecord()); | |
231 job_record->frame = frame; | |
232 job_record->output_buffer_index = output_buffer_index; | |
233 job_record->ready_cb = cb; | |
234 | |
235 device_thread_.message_loop()->PostTask( | |
236 FROM_HERE, | |
237 base::Bind(&V4L2ImageProcessor::ProcessTask, | |
238 base::Unretained(this), | |
239 base::Passed(&job_record))); | |
240 } | |
241 | |
242 void V4L2ImageProcessor::ProcessTask(std::unique_ptr<JobRecord> job_record) { | |
243 int index = job_record->output_buffer_index; | |
244 DVLOG(3) << __func__ << ": Reusing output buffer, index=" << index; | |
245 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
246 | |
247 EnqueueOutput(index); | |
248 input_queue_.push(make_linked_ptr(job_record.release())); | |
249 EnqueueInput(); | |
250 } | |
251 | |
252 void V4L2ImageProcessor::Destroy() { | |
253 DVLOG(3) << __func__; | |
254 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
255 | |
256 weak_this_factory_.InvalidateWeakPtrs(); | |
257 | |
258 // If the device thread is running, destroy using posted task. | |
259 if (device_thread_.IsRunning()) { | |
260 device_thread_.message_loop()->PostTask( | |
261 FROM_HERE, | |
262 base::Bind(&V4L2ImageProcessor::DestroyTask, base::Unretained(this))); | |
263 // Wait for tasks to finish/early-exit. | |
264 device_thread_.Stop(); | |
265 } else { | |
266 // Otherwise DestroyTask() is not needed. | |
267 DCHECK(!device_poll_thread_.IsRunning()); | |
268 } | |
269 | |
270 delete this; | |
271 } | |
272 | |
273 void V4L2ImageProcessor::DestroyTask() { | |
274 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
275 | |
276 // Stop streaming and the device_poll_thread_. | |
277 StopDevicePoll(); | |
278 } | |
279 | |
280 bool V4L2ImageProcessor::CreateInputBuffers() { | |
281 DVLOG(3) << __func__; | |
282 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
283 DCHECK(!input_streamon_); | |
284 | |
285 struct v4l2_control control; | |
286 memset(&control, 0, sizeof(control)); | |
287 control.id = V4L2_CID_ROTATE; | |
288 control.value = 0; | |
289 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); | |
290 | |
291 memset(&control, 0, sizeof(control)); | |
292 control.id = V4L2_CID_HFLIP; | |
293 control.value = 0; | |
294 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); | |
295 | |
296 memset(&control, 0, sizeof(control)); | |
297 control.id = V4L2_CID_VFLIP; | |
298 control.value = 0; | |
299 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); | |
300 | |
301 memset(&control, 0, sizeof(control)); | |
302 control.id = V4L2_CID_ALPHA_COMPONENT; | |
303 control.value = 255; | |
304 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); | |
305 | |
306 struct v4l2_format format; | |
307 memset(&format, 0, sizeof(format)); | |
308 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
309 format.fmt.pix_mp.width = input_allocated_size_.width(); | |
310 format.fmt.pix_mp.height = input_allocated_size_.height(); | |
311 format.fmt.pix_mp.pixelformat = input_format_fourcc_; | |
312 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format); | |
313 | |
314 input_planes_count_ = format.fmt.pix_mp.num_planes; | |
315 DCHECK_LE(input_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); | |
316 input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format); | |
317 DCHECK(gfx::Rect(input_allocated_size_).Contains( | |
318 gfx::Rect(input_visible_size_))); | |
319 | |
320 struct v4l2_crop crop; | |
321 memset(&crop, 0, sizeof(crop)); | |
322 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
323 crop.c.left = 0; | |
324 crop.c.top = 0; | |
325 crop.c.width = base::checked_cast<__u32>(input_visible_size_.width()); | |
326 crop.c.height = base::checked_cast<__u32>(input_visible_size_.height()); | |
327 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop); | |
328 | |
329 struct v4l2_requestbuffers reqbufs; | |
330 memset(&reqbufs, 0, sizeof(reqbufs)); | |
331 reqbufs.count = num_buffers_; | |
332 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
333 reqbufs.memory = input_memory_type_; | |
334 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs); | |
335 if (static_cast<int>(reqbufs.count) != num_buffers_) { | |
336 LOG(ERROR) << "Failed to allocate input buffers. reqbufs.count=" | |
337 << reqbufs.count << ", num_buffers=" << num_buffers_; | |
338 return false; | |
339 } | |
340 | |
341 DCHECK(input_buffer_map_.empty()); | |
342 input_buffer_map_.resize(reqbufs.count); | |
343 | |
344 for (size_t i = 0; i < input_buffer_map_.size(); ++i) | |
345 free_input_buffers_.push_back(i); | |
346 | |
347 return true; | |
348 } | |
349 | |
350 bool V4L2ImageProcessor::CreateOutputBuffers() { | |
351 DVLOG(3) << __func__; | |
352 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
353 DCHECK(!output_streamon_); | |
354 | |
355 struct v4l2_format format; | |
356 memset(&format, 0, sizeof(format)); | |
357 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
358 format.fmt.pix_mp.width = output_allocated_size_.width(); | |
359 format.fmt.pix_mp.height = output_allocated_size_.height(); | |
360 format.fmt.pix_mp.pixelformat = output_format_fourcc_; | |
361 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format); | |
362 | |
363 output_planes_count_ = format.fmt.pix_mp.num_planes; | |
364 DCHECK_LE(output_planes_count_, static_cast<size_t>(VIDEO_MAX_PLANES)); | |
365 gfx::Size adjusted_allocated_size = | |
366 V4L2Device::CodedSizeFromV4L2Format(format); | |
367 DCHECK(gfx::Rect(adjusted_allocated_size).Contains( | |
368 gfx::Rect(output_allocated_size_))); | |
369 output_allocated_size_ = adjusted_allocated_size; | |
370 | |
371 struct v4l2_crop crop; | |
372 memset(&crop, 0, sizeof(crop)); | |
373 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
374 crop.c.left = 0; | |
375 crop.c.top = 0; | |
376 crop.c.width = base::checked_cast<__u32>(output_visible_size_.width()); | |
377 crop.c.height = base::checked_cast<__u32>(output_visible_size_.height()); | |
378 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop); | |
379 | |
380 struct v4l2_requestbuffers reqbufs; | |
381 memset(&reqbufs, 0, sizeof(reqbufs)); | |
382 reqbufs.count = num_buffers_; | |
383 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
384 reqbufs.memory = V4L2_MEMORY_MMAP; | |
385 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs); | |
386 if (static_cast<int>(reqbufs.count) != num_buffers_) { | |
387 LOG(ERROR) << "Failed to allocate output buffers. reqbufs.count=" | |
388 << reqbufs.count << ", num_buffers=" << num_buffers_; | |
389 return false; | |
390 } | |
391 | |
392 DCHECK(output_buffer_map_.empty()); | |
393 output_buffer_map_.resize(reqbufs.count); | |
394 | |
395 return true; | |
396 } | |
397 | |
398 void V4L2ImageProcessor::DestroyInputBuffers() { | |
399 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
400 DCHECK(!input_streamon_); | |
401 | |
402 struct v4l2_requestbuffers reqbufs; | |
403 memset(&reqbufs, 0, sizeof(reqbufs)); | |
404 reqbufs.count = 0; | |
405 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
406 reqbufs.memory = input_memory_type_; | |
407 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); | |
408 | |
409 input_buffer_map_.clear(); | |
410 free_input_buffers_.clear(); | |
411 } | |
412 | |
413 void V4L2ImageProcessor::DestroyOutputBuffers() { | |
414 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
415 DCHECK(!output_streamon_); | |
416 | |
417 struct v4l2_requestbuffers reqbufs; | |
418 memset(&reqbufs, 0, sizeof(reqbufs)); | |
419 reqbufs.count = 0; | |
420 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
421 reqbufs.memory = V4L2_MEMORY_MMAP; | |
422 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); | |
423 | |
424 output_buffer_map_.clear(); | |
425 } | |
426 | |
427 void V4L2ImageProcessor::DevicePollTask(bool poll_device) { | |
428 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current()); | |
429 | |
430 bool event_pending; | |
431 if (!device_->Poll(poll_device, &event_pending)) { | |
432 NotifyError(); | |
433 return; | |
434 } | |
435 | |
436 // All processing should happen on ServiceDeviceTask(), since we shouldn't | |
437 // touch encoder state from this thread. | |
438 device_thread_.message_loop()->PostTask( | |
439 FROM_HERE, | |
440 base::Bind(&V4L2ImageProcessor::ServiceDeviceTask, | |
441 base::Unretained(this))); | |
442 } | |
443 | |
444 void V4L2ImageProcessor::ServiceDeviceTask() { | |
445 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
446 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(), | |
447 // so either: | |
448 // * device_poll_thread_ is running normally | |
449 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down, | |
450 // in which case we should early-out. | |
451 if (!device_poll_thread_.message_loop()) | |
452 return; | |
453 | |
454 Dequeue(); | |
455 EnqueueInput(); | |
456 | |
457 if (!device_->ClearDevicePollInterrupt()) | |
458 return; | |
459 | |
460 bool poll_device = | |
461 (input_buffer_queued_count_ > 0 && output_buffer_queued_count_ > 0); | |
462 | |
463 device_poll_thread_.message_loop()->PostTask( | |
464 FROM_HERE, | |
465 base::Bind(&V4L2ImageProcessor::DevicePollTask, | |
466 base::Unretained(this), | |
467 poll_device)); | |
468 | |
469 DVLOG(2) << __func__ << ": buffer counts: INPUT[" << input_queue_.size() | |
470 << "] => DEVICE[" << free_input_buffers_.size() << "+" | |
471 << input_buffer_queued_count_ << "/" << input_buffer_map_.size() | |
472 << "->" << output_buffer_map_.size() - output_buffer_queued_count_ | |
473 << "+" << output_buffer_queued_count_ << "/" | |
474 << output_buffer_map_.size() << "]"; | |
475 } | |
476 | |
477 void V4L2ImageProcessor::EnqueueInput() { | |
478 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
479 | |
480 const int old_inputs_queued = input_buffer_queued_count_; | |
481 while (!input_queue_.empty() && !free_input_buffers_.empty()) { | |
482 if (!EnqueueInputRecord()) | |
483 return; | |
484 } | |
485 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) { | |
486 // We started up a previously empty queue. | |
487 // Queue state changed; signal interrupt. | |
488 if (!device_->SetDevicePollInterrupt()) | |
489 return; | |
490 // VIDIOC_STREAMON if we haven't yet. | |
491 if (!input_streamon_) { | |
492 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
493 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); | |
494 input_streamon_ = true; | |
495 } | |
496 } | |
497 } | |
498 | |
499 void V4L2ImageProcessor::EnqueueOutput(int index) { | |
500 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
501 | |
502 const int old_outputs_queued = output_buffer_queued_count_; | |
503 if (!EnqueueOutputRecord(index)) | |
504 return; | |
505 | |
506 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) { | |
507 // We just started up a previously empty queue. | |
508 // Queue state changed; signal interrupt. | |
509 if (!device_->SetDevicePollInterrupt()) | |
510 return; | |
511 // Start VIDIOC_STREAMON if we haven't yet. | |
512 if (!output_streamon_) { | |
513 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
514 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); | |
515 output_streamon_ = true; | |
516 } | |
517 } | |
518 } | |
519 | |
520 void V4L2ImageProcessor::Dequeue() { | |
521 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
522 | |
523 // Dequeue completed input (VIDEO_OUTPUT) buffers, | |
524 // and recycle to the free list. | |
525 struct v4l2_buffer dqbuf; | |
526 struct v4l2_plane planes[VIDEO_MAX_PLANES]; | |
527 while (input_buffer_queued_count_ > 0) { | |
528 DCHECK(input_streamon_); | |
529 memset(&dqbuf, 0, sizeof(dqbuf)); | |
530 memset(&planes, 0, sizeof(planes)); | |
531 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
532 dqbuf.memory = input_memory_type_; | |
533 dqbuf.m.planes = planes; | |
534 dqbuf.length = input_planes_count_; | |
535 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { | |
536 if (errno == EAGAIN) { | |
537 // EAGAIN if we're just out of buffers to dequeue. | |
538 break; | |
539 } | |
540 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; | |
541 NotifyError(); | |
542 return; | |
543 } | |
544 InputRecord& input_record = input_buffer_map_[dqbuf.index]; | |
545 DCHECK(input_record.at_device); | |
546 input_record.at_device = false; | |
547 input_record.frame = NULL; | |
548 free_input_buffers_.push_back(dqbuf.index); | |
549 input_buffer_queued_count_--; | |
550 } | |
551 | |
552 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list. | |
553 // Return the finished buffer to the client via the job ready callback. | |
554 while (output_buffer_queued_count_ > 0) { | |
555 DCHECK(output_streamon_); | |
556 memset(&dqbuf, 0, sizeof(dqbuf)); | |
557 memset(&planes, 0, sizeof(planes)); | |
558 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
559 dqbuf.memory = V4L2_MEMORY_MMAP; | |
560 dqbuf.m.planes = planes; | |
561 dqbuf.length = output_planes_count_; | |
562 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { | |
563 if (errno == EAGAIN) { | |
564 // EAGAIN if we're just out of buffers to dequeue. | |
565 break; | |
566 } | |
567 PLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; | |
568 NotifyError(); | |
569 return; | |
570 } | |
571 OutputRecord& output_record = output_buffer_map_[dqbuf.index]; | |
572 DCHECK(output_record.at_device); | |
573 output_record.at_device = false; | |
574 output_buffer_queued_count_--; | |
575 | |
576 // Jobs are always processed in FIFO order. | |
577 DCHECK(!running_jobs_.empty()); | |
578 linked_ptr<JobRecord> job_record = running_jobs_.front(); | |
579 running_jobs_.pop(); | |
580 | |
581 DVLOG(3) << "Processing finished, returning frame, index=" << dqbuf.index; | |
582 | |
583 child_task_runner_->PostTask( | |
584 FROM_HERE, base::Bind(&V4L2ImageProcessor::FrameReady, weak_this_, | |
585 job_record->ready_cb, dqbuf.index)); | |
586 } | |
587 } | |
588 | |
589 bool V4L2ImageProcessor::EnqueueInputRecord() { | |
590 DCHECK(!input_queue_.empty()); | |
591 DCHECK(!free_input_buffers_.empty()); | |
592 | |
593 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame. | |
594 linked_ptr<JobRecord> job_record = input_queue_.front(); | |
595 input_queue_.pop(); | |
596 const int index = free_input_buffers_.back(); | |
597 InputRecord& input_record = input_buffer_map_[index]; | |
598 DCHECK(!input_record.at_device); | |
599 input_record.frame = job_record->frame; | |
600 struct v4l2_buffer qbuf; | |
601 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES]; | |
602 memset(&qbuf, 0, sizeof(qbuf)); | |
603 memset(qbuf_planes, 0, sizeof(qbuf_planes)); | |
604 qbuf.index = index; | |
605 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
606 qbuf.memory = input_memory_type_; | |
607 qbuf.m.planes = qbuf_planes; | |
608 qbuf.length = input_planes_count_; | |
609 for (size_t i = 0; i < input_planes_count_; ++i) { | |
610 qbuf.m.planes[i].bytesused = media::VideoFrame::PlaneSize( | |
611 input_record.frame->format(), i, input_allocated_size_).GetArea(); | |
612 qbuf.m.planes[i].length = qbuf.m.planes[i].bytesused; | |
613 if (input_memory_type_ == V4L2_MEMORY_USERPTR) { | |
614 qbuf.m.planes[i].m.userptr = | |
615 reinterpret_cast<unsigned long>(input_record.frame->data(i)); | |
616 } else { | |
617 qbuf.m.planes[i].m.fd = input_record.frame->dmabuf_fd(i); | |
618 } | |
619 } | |
620 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); | |
621 input_record.at_device = true; | |
622 running_jobs_.push(job_record); | |
623 free_input_buffers_.pop_back(); | |
624 input_buffer_queued_count_++; | |
625 | |
626 DVLOG(3) << __func__ << ": enqueued frame ts=" | |
627 << job_record->frame->timestamp().InMilliseconds() << " to device."; | |
628 | |
629 return true; | |
630 } | |
631 | |
632 bool V4L2ImageProcessor::EnqueueOutputRecord(int index) { | |
633 DCHECK_GE(index, 0); | |
634 DCHECK_LT(static_cast<size_t>(index), output_buffer_map_.size()); | |
635 // Enqueue an output (VIDEO_CAPTURE) buffer. | |
636 OutputRecord& output_record = output_buffer_map_[index]; | |
637 DCHECK(!output_record.at_device); | |
638 struct v4l2_buffer qbuf; | |
639 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES]; | |
640 memset(&qbuf, 0, sizeof(qbuf)); | |
641 memset(qbuf_planes, 0, sizeof(qbuf_planes)); | |
642 qbuf.index = index; | |
643 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
644 qbuf.memory = V4L2_MEMORY_MMAP; | |
645 qbuf.m.planes = qbuf_planes; | |
646 qbuf.length = output_planes_count_; | |
647 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); | |
648 output_record.at_device = true; | |
649 output_buffer_queued_count_++; | |
650 return true; | |
651 } | |
652 | |
653 bool V4L2ImageProcessor::StartDevicePoll() { | |
654 DVLOG(3) << __func__ << ": starting device poll"; | |
655 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
656 DCHECK(!device_poll_thread_.IsRunning()); | |
657 | |
658 // Start up the device poll thread and schedule its first DevicePollTask(). | |
659 if (!device_poll_thread_.Start()) { | |
660 LOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; | |
661 NotifyError(); | |
662 return false; | |
663 } | |
664 // Enqueue a poll task with no devices to poll on - will wait only for the | |
665 // poll interrupt | |
666 device_poll_thread_.message_loop()->PostTask( | |
667 FROM_HERE, | |
668 base::Bind( | |
669 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false)); | |
670 | |
671 return true; | |
672 } | |
673 | |
674 bool V4L2ImageProcessor::StopDevicePoll() { | |
675 DVLOG(3) << __func__ << ": stopping device poll"; | |
676 if (device_thread_.IsRunning()) | |
677 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); | |
678 | |
679 // Signal the DevicePollTask() to stop, and stop the device poll thread. | |
680 if (!device_->SetDevicePollInterrupt()) | |
681 return false; | |
682 device_poll_thread_.Stop(); | |
683 | |
684 // Clear the interrupt now, to be sure. | |
685 if (!device_->ClearDevicePollInterrupt()) | |
686 return false; | |
687 | |
688 if (input_streamon_) { | |
689 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; | |
690 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type); | |
691 } | |
692 input_streamon_ = false; | |
693 | |
694 if (output_streamon_) { | |
695 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; | |
696 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type); | |
697 } | |
698 output_streamon_ = false; | |
699 | |
700 // Reset all our accounting info. | |
701 while (!input_queue_.empty()) | |
702 input_queue_.pop(); | |
703 | |
704 while (!running_jobs_.empty()) | |
705 running_jobs_.pop(); | |
706 | |
707 free_input_buffers_.clear(); | |
708 for (size_t i = 0; i < input_buffer_map_.size(); ++i) { | |
709 InputRecord& input_record = input_buffer_map_[i]; | |
710 input_record.at_device = false; | |
711 input_record.frame = NULL; | |
712 free_input_buffers_.push_back(i); | |
713 } | |
714 input_buffer_queued_count_ = 0; | |
715 | |
716 output_buffer_map_.clear(); | |
717 output_buffer_map_.resize(num_buffers_); | |
718 output_buffer_queued_count_ = 0; | |
719 | |
720 return true; | |
721 } | |
722 | |
723 void V4L2ImageProcessor::FrameReady(const FrameReadyCB& cb, | |
724 int output_buffer_index) { | |
725 DCHECK(child_task_runner_->BelongsToCurrentThread()); | |
726 cb.Run(output_buffer_index); | |
727 } | |
728 | |
729 } // namespace content | |
OLD | NEW |