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 46 namespace content { 47 48 V4L2ImageProcessor::InputRecord::InputRecord() : at_device(false) { 49 } 50 51 V4L2ImageProcessor::OutputRecord::OutputRecord() 52 : at_device(false), at_client(false) { 53 } 54 55 V4L2ImageProcessor::JobRecord::JobRecord() { 56 } 57 58 V4L2ImageProcessor::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 76 V4L2ImageProcessor::~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 85 void 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 92 bool 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 165 void 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 180 void 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 187 void 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 207 void 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 216 bool V4L2ImageProcessor::CreateInputBuffers() { 217 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); 218 DCHECK(!input_streamon_); 219 220 struct v4l2_control control; 221 memset(&control, 0, sizeof(control)); 222 control.id = V4L2_CID_ROTATE; 223 control.value = 0; 224 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); 225 226 memset(&control, 0, sizeof(control)); 227 control.id = V4L2_CID_HFLIP; 228 control.value = 0; 229 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); 230 231 memset(&control, 0, sizeof(control)); 232 control.id = V4L2_CID_VFLIP; 233 control.value = 0; 234 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); 235 236 memset(&control, 0, sizeof(control)); 237 control.id = V4L2_CID_ALPHA_COMPONENT; 238 control.value = 255; 239 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CTRL, &control); 240 241 struct v4l2_format format; 242 memset(&format, 0, sizeof(format)); 243 format.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 244 format.fmt.pix_mp.width = input_visible_size_.width(); 245 format.fmt.pix_mp.height = input_visible_size_.height(); 246 format.fmt.pix_mp.pixelformat = input_format_fourcc_; 247 format.fmt.pix_mp.num_planes = input_planes_count_; 248 for (size_t i = 0; i < input_planes_count_; ++i) { 249 format.fmt.pix_mp.plane_fmt[i].sizeimage = 250 media::VideoFrame::PlaneAllocationSize( 251 input_format_, i, input_allocated_size_); 252 format.fmt.pix_mp.plane_fmt[i].bytesperline = 253 base::checked_cast<__u32>(input_allocated_size_.width()); 254 } 255 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format); 256 257 input_allocated_size_ = V4L2Device::CodedSizeFromV4L2Format(format); 258 DCHECK(gfx::Rect(input_allocated_size_).Contains( 259 gfx::Rect(input_visible_size_))); 260 261 struct v4l2_crop crop; 262 memset(&crop, 0, sizeof(crop)); 263 crop.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 264 crop.c.left = 0; 265 crop.c.top = 0; 266 crop.c.width = base::checked_cast<__u32>(input_visible_size_.width()); 267 crop.c.height = base::checked_cast<__u32>(input_visible_size_.height()); 268 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop); 269 270 struct v4l2_requestbuffers reqbufs; 271 memset(&reqbufs, 0, sizeof(reqbufs)); 272 reqbufs.count = kInputBufferCount; 273 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 274 reqbufs.memory = V4L2_MEMORY_USERPTR; 275 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs); 276 277 DCHECK(input_buffer_map_.empty()); 278 input_buffer_map_.resize(reqbufs.count); 279 280 for (size_t i = 0; i < input_buffer_map_.size(); ++i) 281 free_input_buffers_.push_back(i); 282 283 return true; 284 } 285 286 bool V4L2ImageProcessor::CreateOutputBuffers() { 287 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); 288 DCHECK(!output_streamon_); 289 290 struct v4l2_format format; 291 memset(&format, 0, sizeof(format)); 292 format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 293 format.fmt.pix_mp.width = output_allocated_size_.width(); 294 format.fmt.pix_mp.height = output_allocated_size_.height(); 295 format.fmt.pix_mp.pixelformat = output_format_fourcc_; 296 format.fmt.pix_mp.num_planes = output_planes_count_; 297 for (size_t i = 0; i < output_planes_count_; ++i) { 298 format.fmt.pix_mp.plane_fmt[i].sizeimage = 299 media::VideoFrame::PlaneAllocationSize( 300 output_format_, i, output_allocated_size_); 301 format.fmt.pix_mp.plane_fmt[i].bytesperline = 302 base::checked_cast<__u32>(output_allocated_size_.width()); 303 } 304 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_FMT, &format); 305 306 gfx::Size adjusted_allocated_size = 307 V4L2Device::CodedSizeFromV4L2Format(format); 308 DCHECK(gfx::Rect(adjusted_allocated_size).Contains( 309 gfx::Rect(output_allocated_size_))); 310 output_allocated_size_ = adjusted_allocated_size; 311 312 struct v4l2_crop crop; 313 memset(&crop, 0, sizeof(crop)); 314 crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 315 crop.c.left = 0; 316 crop.c.top = 0; 317 crop.c.width = base::checked_cast<__u32>(output_visible_size_.width()); 318 crop.c.height = base::checked_cast<__u32>(output_visible_size_.height()); 319 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_S_CROP, &crop); 320 321 struct v4l2_requestbuffers reqbufs; 322 memset(&reqbufs, 0, sizeof(reqbufs)); 323 reqbufs.count = kOutputBufferCount; 324 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 325 reqbufs.memory = V4L2_MEMORY_MMAP; 326 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_REQBUFS, &reqbufs); 327 328 DCHECK(output_buffer_map_.empty()); 329 output_buffer_map_.resize(reqbufs.count); 330 for (size_t i = 0; i < output_buffer_map_.size(); ++i) { 331 OutputRecord& output_record = output_buffer_map_[i]; 332 output_record.fds.resize(output_planes_count_); 333 for (size_t j = 0; j < output_planes_count_; ++j) { 334 struct v4l2_exportbuffer expbuf; 335 memset(&expbuf, 0, sizeof(expbuf)); 336 expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 337 expbuf.index = i; 338 expbuf.plane = j; 339 expbuf.flags = O_CLOEXEC; 340 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_EXPBUF, &expbuf); 341 output_record.fds[j] = expbuf.fd; 342 } 343 free_output_buffers_.push_back(i); 344 } 345 346 return true; 347 } 348 349 void V4L2ImageProcessor::DestroyInputBuffers() { 350 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); 351 DCHECK(!input_streamon_); 352 353 struct v4l2_requestbuffers reqbufs; 354 memset(&reqbufs, 0, sizeof(reqbufs)); 355 reqbufs.count = 0; 356 reqbufs.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 357 reqbufs.memory = V4L2_MEMORY_USERPTR; 358 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); 359 360 input_buffer_map_.clear(); 361 free_input_buffers_.clear(); 362 } 363 364 void V4L2ImageProcessor::DestroyOutputBuffers() { 365 DCHECK(child_message_loop_proxy_->BelongsToCurrentThread()); 366 DCHECK(!output_streamon_); 367 368 for (size_t buf = 0; buf < output_buffer_map_.size(); ++buf) { 369 OutputRecord& output_record = output_buffer_map_[buf]; 370 for (size_t plane = 0; plane < output_record.fds.size(); ++plane) 371 close(output_record.fds[plane]); 372 output_record.fds.clear(); 373 } 374 375 struct v4l2_requestbuffers reqbufs; 376 memset(&reqbufs, 0, sizeof(reqbufs)); 377 reqbufs.count = 0; 378 reqbufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 379 reqbufs.memory = V4L2_MEMORY_MMAP; 380 IOCTL_OR_LOG_ERROR(VIDIOC_REQBUFS, &reqbufs); 381 382 output_buffer_map_.clear(); 383 free_output_buffers_.clear(); 384 } 385 386 void V4L2ImageProcessor::DevicePollTask(bool poll_device) { 387 DCHECK_EQ(device_poll_thread_.message_loop(), base::MessageLoop::current()); 388 389 bool event_pending; 390 if (!device_->Poll(poll_device, &event_pending)) { 391 NOTIFY_ERROR(); 392 return; 393 } 394 395 // All processing should happen on ServiceDeviceTask(), since we shouldn't 396 // touch encoder state from this thread. 397 device_thread_.message_loop()->PostTask( 398 FROM_HERE, 399 base::Bind(&V4L2ImageProcessor::ServiceDeviceTask, 400 base::Unretained(this))); 401 } 402 403 void V4L2ImageProcessor::ServiceDeviceTask() { 404 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); 405 // ServiceDeviceTask() should only ever be scheduled from DevicePollTask(), 406 // so either: 407 // * device_poll_thread_ is running normally 408 // * device_poll_thread_ scheduled us, but then a DestroyTask() shut it down, 409 // in which case we should early-out. 410 if (!device_poll_thread_.message_loop()) 411 return; 412 413 Dequeue(); 414 Enqueue(); 415 416 if (!device_->ClearDevicePollInterrupt()) 417 return; 418 419 bool poll_device = 420 (input_buffer_queued_count_ > 0 && output_buffer_queued_count_ > 0); 421 422 device_poll_thread_.message_loop()->PostTask( 423 FROM_HERE, 424 base::Bind(&V4L2ImageProcessor::DevicePollTask, 425 base::Unretained(this), 426 poll_device)); 427 428 DVLOG(2) << __func__ << ": buffer counts: INPUT[" 429 << input_queue_.size() << "] => DEVICE[" 430 << free_input_buffers_.size() << "+" 431 << input_buffer_queued_count_ << "/" 432 << input_buffer_map_.size() << "->" 433 << free_output_buffers_.size() << "+" 434 << output_buffer_queued_count_ << "/" 435 << output_buffer_map_.size() << "] => CLIENT[" 436 << output_buffer_map_.size() - output_buffer_queued_count_ - 437 free_output_buffers_.size() << "]"; 438 } 439 440 void V4L2ImageProcessor::Enqueue() { 441 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); 442 443 const int old_inputs_queued = input_buffer_queued_count_; 444 while (!input_queue_.empty() && !free_input_buffers_.empty()) { 445 if (!EnqueueInputRecord()) 446 return; 447 } 448 if (old_inputs_queued == 0 && input_buffer_queued_count_ != 0) { 449 // We started up a previously empty queue. 450 // Queue state changed; signal interrupt. 451 if (!device_->SetDevicePollInterrupt()) 452 return; 453 // VIDIOC_STREAMON if we haven't yet. 454 if (!input_streamon_) { 455 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 456 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); 457 input_streamon_ = true; 458 } 459 } 460 461 // TODO(posciak): Fix this to be non-Exynos specific. 462 // Exynos GSC is liable to race conditions if more than one output buffer is 463 // simultaneously enqueued, so enqueue just one. 464 if (output_buffer_queued_count_ == 0 && !free_output_buffers_.empty()) { 465 const int old_outputs_queued = output_buffer_queued_count_; 466 if (!EnqueueOutputRecord()) 467 return; 468 if (old_outputs_queued == 0 && output_buffer_queued_count_ != 0) { 469 // We just started up a previously empty queue. 470 // Queue state changed; signal interrupt. 471 if (!device_->SetDevicePollInterrupt()) 472 return; 473 // Start VIDIOC_STREAMON if we haven't yet. 474 if (!output_streamon_) { 475 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 476 IOCTL_OR_ERROR_RETURN(VIDIOC_STREAMON, &type); 477 output_streamon_ = true; 478 } 479 } 480 } 481 DCHECK_LE(output_buffer_queued_count_, 1); 482 } 483 484 void V4L2ImageProcessor::Dequeue() { 485 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); 486 487 // Dequeue completed input (VIDEO_OUTPUT) buffers, 488 // and recycle to the free list. 489 struct v4l2_buffer dqbuf; 490 struct v4l2_plane planes[VIDEO_MAX_PLANES]; 491 while (input_buffer_queued_count_ > 0) { 492 DCHECK(input_streamon_); 493 memset(&dqbuf, 0, sizeof(dqbuf)); 494 memset(&planes, 0, sizeof(planes)); 495 dqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 496 dqbuf.memory = V4L2_MEMORY_USERPTR; 497 dqbuf.m.planes = planes; 498 dqbuf.length = input_planes_count_; 499 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { 500 if (errno == EAGAIN) { 501 // EAGAIN if we're just out of buffers to dequeue. 502 break; 503 } 504 DPLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; 505 NOTIFY_ERROR(); 506 return; 507 } 508 InputRecord& input_record = input_buffer_map_[dqbuf.index]; 509 DCHECK(input_record.at_device); 510 input_record.at_device = false; 511 input_record.frame = NULL; 512 free_input_buffers_.push_back(dqbuf.index); 513 input_buffer_queued_count_--; 514 } 515 516 // Dequeue completed output (VIDEO_CAPTURE) buffers, recycle to the free list. 517 // Return the finished buffer to the client via the job ready callback. 518 while (output_buffer_queued_count_ > 0) { 519 DCHECK(output_streamon_); 520 memset(&dqbuf, 0, sizeof(dqbuf)); 521 memset(&planes, 0, sizeof(planes)); 522 dqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 523 dqbuf.memory = V4L2_MEMORY_DMABUF; 524 dqbuf.m.planes = planes; 525 dqbuf.length = output_planes_count_; 526 if (device_->Ioctl(VIDIOC_DQBUF, &dqbuf) != 0) { 527 if (errno == EAGAIN) { 528 // EAGAIN if we're just out of buffers to dequeue. 529 break; 530 } 531 DPLOG(ERROR) << "ioctl() failed: VIDIOC_DQBUF"; 532 NOTIFY_ERROR(); 533 return; 534 } 535 OutputRecord& output_record = output_buffer_map_[dqbuf.index]; 536 DCHECK(output_record.at_device); 537 output_record.at_device = false; 538 output_record.at_client = true; 539 output_buffer_queued_count_--; 540 541 // Jobs are always processed in FIFO order. 542 DCHECK(!running_jobs_.empty()); 543 linked_ptr<JobRecord> job_record = running_jobs_.front(); 544 running_jobs_.pop(); 545 546 scoped_refptr<media::VideoFrame> output_frame = 547 media::VideoFrame::WrapExternalDmabufs( 548 output_format_, 549 output_allocated_size_, 550 gfx::Rect(output_visible_size_), 551 output_visible_size_, 552 output_record.fds, 553 job_record->frame->timestamp(), 554 media::BindToCurrentLoop( 555 base::Bind(&V4L2ImageProcessor::ReuseOutputBuffer, 556 device_weak_factory_.GetWeakPtr(), 557 dqbuf.index))); 558 559 DVLOG(3) << "Processing finished, returning frame, ts=" 560 << output_frame->timestamp().InMilliseconds(); 561 562 child_message_loop_proxy_->PostTask( 563 FROM_HERE, base::Bind(job_record->ready_cb, output_frame)); 564 } 565 } 566 567 void V4L2ImageProcessor::ReuseOutputBuffer(int index) { 568 DVLOG(3) << "Reusing output buffer, index=" << index; 569 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); 570 571 OutputRecord& output_record = output_buffer_map_[index]; 572 DCHECK(output_record.at_client); 573 DCHECK(!output_record.at_device); 574 output_record.at_client = false; 575 free_output_buffers_.push_back(index); 576 577 Enqueue(); 578 } 579 580 bool V4L2ImageProcessor::EnqueueInputRecord() { 581 DCHECK(!input_queue_.empty()); 582 DCHECK(!free_input_buffers_.empty()); 583 584 // Enqueue an input (VIDEO_OUTPUT) buffer for an input video frame. 585 linked_ptr<JobRecord> job_record = input_queue_.front(); 586 input_queue_.pop(); 587 const int index = free_input_buffers_.back(); 588 InputRecord& input_record = input_buffer_map_[index]; 589 DCHECK(!input_record.at_device); 590 input_record.frame = job_record->frame; 591 struct v4l2_buffer qbuf; 592 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES]; 593 memset(&qbuf, 0, sizeof(qbuf)); 594 memset(qbuf_planes, 0, sizeof(qbuf_planes)); 595 qbuf.index = index; 596 qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 597 qbuf.memory = V4L2_MEMORY_USERPTR; 598 qbuf.m.planes = qbuf_planes; 599 qbuf.length = input_planes_count_; 600 for (size_t i = 0; i < input_planes_count_; ++i) { 601 qbuf.m.planes[i].bytesused = media::VideoFrame::PlaneAllocationSize( 602 input_record.frame->format(), i, input_allocated_size_); 603 qbuf.m.planes[i].length = qbuf.m.planes[i].bytesused; 604 qbuf.m.planes[i].m.userptr = 605 reinterpret_cast<unsigned long>(input_record.frame->data(i)); 606 } 607 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); 608 input_record.at_device = true; 609 running_jobs_.push(job_record); 610 free_input_buffers_.pop_back(); 611 input_buffer_queued_count_++; 612 613 DVLOG(3) << __func__ << ": enqueued frame ts=" 614 << job_record->frame->timestamp().InMilliseconds() << " to device."; 615 616 return true; 617 } 618 619 bool V4L2ImageProcessor::EnqueueOutputRecord() { 620 DCHECK(!free_output_buffers_.empty()); 621 622 // Enqueue an output (VIDEO_CAPTURE) buffer. 623 const int index = free_output_buffers_.back(); 624 OutputRecord& output_record = output_buffer_map_[index]; 625 DCHECK(!output_record.at_device); 626 struct v4l2_buffer qbuf; 627 struct v4l2_plane qbuf_planes[VIDEO_MAX_PLANES]; 628 memset(&qbuf, 0, sizeof(qbuf)); 629 memset(qbuf_planes, 0, sizeof(qbuf_planes)); 630 qbuf.index = index; 631 qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 632 qbuf.memory = V4L2_MEMORY_MMAP; 633 qbuf.m.planes = qbuf_planes; 634 qbuf.length = output_planes_count_; 635 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_QBUF, &qbuf); 636 output_record.at_device = true; 637 free_output_buffers_.pop_back(); 638 output_buffer_queued_count_++; 639 return true; 640 } 641 642 bool V4L2ImageProcessor::StartDevicePoll() { 643 DVLOG(3) << __func__ << ": starting device poll"; 644 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); 645 DCHECK(!device_poll_thread_.IsRunning()); 646 647 // Start up the device poll thread and schedule its first DevicePollTask(). 648 if (!device_poll_thread_.Start()) { 649 DLOG(ERROR) << "StartDevicePoll(): Device thread failed to start"; 650 NOTIFY_ERROR(); 651 return false; 652 } 653 // Enqueue a poll task with no devices to poll on - will wait only for the 654 // poll interrupt 655 device_poll_thread_.message_loop()->PostTask( 656 FROM_HERE, 657 base::Bind( 658 &V4L2ImageProcessor::DevicePollTask, base::Unretained(this), false)); 659 660 return true; 661 } 662 663 bool V4L2ImageProcessor::StopDevicePoll() { 664 DVLOG(3) << __func__ << ": stopping device poll"; 665 if (device_thread_.IsRunning()) 666 DCHECK_EQ(device_thread_.message_loop(), base::MessageLoop::current()); 667 668 // Signal the DevicePollTask() to stop, and stop the device poll thread. 669 if (!device_->SetDevicePollInterrupt()) 670 return false; 671 device_poll_thread_.Stop(); 672 673 // Clear the interrupt now, to be sure. 674 if (!device_->ClearDevicePollInterrupt()) 675 return false; 676 677 if (input_streamon_) { 678 __u32 type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 679 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type); 680 } 681 input_streamon_ = false; 682 683 if (output_streamon_) { 684 __u32 type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 685 IOCTL_OR_ERROR_RETURN_FALSE(VIDIOC_STREAMOFF, &type); 686 } 687 output_streamon_ = false; 688 689 // Reset all our accounting info. 690 while (!input_queue_.empty()) 691 input_queue_.pop(); 692 693 while (!running_jobs_.empty()) 694 running_jobs_.pop(); 695 696 free_input_buffers_.clear(); 697 for (size_t i = 0; i < input_buffer_map_.size(); ++i) { 698 InputRecord& input_record = input_buffer_map_[i]; 699 input_record.at_device = false; 700 input_record.frame = NULL; 701 free_input_buffers_.push_back(i); 702 } 703 input_buffer_queued_count_ = 0; 704 705 free_output_buffers_.clear(); 706 for (size_t i = 0; i < output_buffer_map_.size(); ++i) { 707 OutputRecord& output_record = output_buffer_map_[i]; 708 output_record.at_device = false; 709 if (!output_record.at_client) 710 free_output_buffers_.push_back(i); 711 } 712 output_buffer_queued_count_ = 0; 713 714 return true; 715 } 716 717 } // namespace content 718