| 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 |