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