OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/capture/video/linux/v4l2_capture_delegate.h" | 5 #include "media/capture/video/linux/v4l2_capture_delegate.h" |
6 | 6 |
7 #include <poll.h> | 7 #include <poll.h> |
8 #include <sys/fcntl.h> | 8 #include <sys/fcntl.h> |
9 #include <sys/ioctl.h> | 9 #include <sys/ioctl.h> |
10 #include <sys/mman.h> | 10 #include <sys/mman.h> |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 int height, | 173 int height, |
174 float frame_rate, | 174 float frame_rate, |
175 scoped_ptr<VideoCaptureDevice::Client> client) { | 175 scoped_ptr<VideoCaptureDevice::Client> client) { |
176 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 176 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
177 DCHECK(client); | 177 DCHECK(client); |
178 client_ = client.Pass(); | 178 client_ = client.Pass(); |
179 | 179 |
180 // Need to open camera with O_RDWR after Linux kernel 3.3. | 180 // Need to open camera with O_RDWR after Linux kernel 3.3. |
181 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR))); | 181 device_fd_.reset(HANDLE_EINTR(open(device_name_.id().c_str(), O_RDWR))); |
182 if (!device_fd_.is_valid()) { | 182 if (!device_fd_.is_valid()) { |
183 SetErrorState("Failed to open V4L2 device driver file."); | 183 SetErrorState(FROM_HERE, "Failed to open V4L2 device driver file."); |
184 return; | 184 return; |
185 } | 185 } |
186 | 186 |
187 v4l2_capability cap = {}; | 187 v4l2_capability cap = {}; |
188 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && | 188 if (!((HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QUERYCAP, &cap)) == 0) && |
189 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE || | 189 ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE || |
190 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && | 190 cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) && |
191 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && | 191 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) && |
192 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) { | 192 !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE)))) { |
193 device_fd_.reset(); | 193 device_fd_.reset(); |
194 SetErrorState("This is not a V4L2 video capture device"); | 194 SetErrorState(FROM_HERE, "This is not a V4L2 video capture device"); |
195 return; | 195 return; |
196 } | 196 } |
197 | 197 |
198 // Get supported video formats in preferred order. | 198 // Get supported video formats in preferred order. |
199 // For large resolutions, favour mjpeg over raw formats. | 199 // For large resolutions, favour mjpeg over raw formats. |
200 const std::list<uint32_t>& desired_v4l2_formats = | 200 const std::list<uint32_t>& desired_v4l2_formats = |
201 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight); | 201 GetListOfUsableFourCcs(width > kMjpegWidth || height > kMjpegHeight); |
202 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end(); | 202 std::list<uint32_t>::const_iterator best = desired_v4l2_formats.end(); |
203 | 203 |
204 v4l2_fmtdesc fmtdesc = {}; | 204 v4l2_fmtdesc fmtdesc = {}; |
205 fmtdesc.type = capture_type_; | 205 fmtdesc.type = capture_type_; |
206 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; | 206 for (; HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_ENUM_FMT, &fmtdesc)) == 0; |
207 ++fmtdesc.index) { | 207 ++fmtdesc.index) { |
208 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); | 208 best = std::find(desired_v4l2_formats.begin(), best, fmtdesc.pixelformat); |
209 } | 209 } |
210 if (best == desired_v4l2_formats.end()) { | 210 if (best == desired_v4l2_formats.end()) { |
211 SetErrorState("Failed to find a supported camera format."); | 211 SetErrorState(FROM_HERE, "Failed to find a supported camera format."); |
212 return; | 212 return; |
213 } | 213 } |
214 | 214 |
215 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best); | 215 DVLOG(1) << "Chosen pixel format is " << FourccToString(*best); |
216 | 216 |
217 video_fmt_.type = capture_type_; | 217 video_fmt_.type = capture_type_; |
218 if (!FillV4L2Format(&video_fmt_, width, height, *best)) { | 218 if (!FillV4L2Format(&video_fmt_, width, height, *best)) { |
219 SetErrorState("Failed filling in V4L2 Format"); | 219 SetErrorState(FROM_HERE, "Failed filling in V4L2 Format"); |
220 return; | 220 return; |
221 } | 221 } |
222 | 222 |
223 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) { | 223 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_FMT, &video_fmt_)) < 0) { |
224 SetErrorState("Failed to set video capture format"); | 224 SetErrorState(FROM_HERE, "Failed to set video capture format"); |
225 return; | 225 return; |
226 } | 226 } |
227 const VideoPixelFormat pixel_format = | 227 const VideoPixelFormat pixel_format = |
228 V4l2FourCcToChromiumPixelFormat(video_fmt_.fmt.pix.pixelformat); | 228 V4l2FourCcToChromiumPixelFormat(video_fmt_.fmt.pix.pixelformat); |
229 if (pixel_format == PIXEL_FORMAT_UNKNOWN) { | 229 if (pixel_format == PIXEL_FORMAT_UNKNOWN) { |
230 SetErrorState("Unsupported pixel format"); | 230 SetErrorState(FROM_HERE, "Unsupported pixel format"); |
231 return; | 231 return; |
232 } | 232 } |
233 | 233 |
234 // Set capture framerate in the form of capture interval. | 234 // Set capture framerate in the form of capture interval. |
235 v4l2_streamparm streamparm = {}; | 235 v4l2_streamparm streamparm = {}; |
236 streamparm.type = capture_type_; | 236 streamparm.type = capture_type_; |
237 // The following line checks that the driver knows about framerate get/set. | 237 // The following line checks that the driver knows about framerate get/set. |
238 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { | 238 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_G_PARM, &streamparm)) >= 0) { |
239 // Now check if the device is able to accept a capture framerate set. | 239 // Now check if the device is able to accept a capture framerate set. |
240 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { | 240 if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) { |
241 // |frame_rate| is float, approximate by a fraction. | 241 // |frame_rate| is float, approximate by a fraction. |
242 streamparm.parm.capture.timeperframe.numerator = | 242 streamparm.parm.capture.timeperframe.numerator = |
243 media::kFrameRatePrecision; | 243 media::kFrameRatePrecision; |
244 streamparm.parm.capture.timeperframe.denominator = | 244 streamparm.parm.capture.timeperframe.denominator = |
245 (frame_rate) ? (frame_rate * media::kFrameRatePrecision) | 245 (frame_rate) ? (frame_rate * media::kFrameRatePrecision) |
246 : (kTypicalFramerate * media::kFrameRatePrecision); | 246 : (kTypicalFramerate * media::kFrameRatePrecision); |
247 | 247 |
248 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < | 248 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_S_PARM, &streamparm)) < |
249 0) { | 249 0) { |
250 SetErrorState("Failed to set camera framerate"); | 250 SetErrorState(FROM_HERE, "Failed to set camera framerate"); |
251 return; | 251 return; |
252 } | 252 } |
253 DVLOG(2) << "Actual camera driverframerate: " | 253 DVLOG(2) << "Actual camera driverframerate: " |
254 << streamparm.parm.capture.timeperframe.denominator << "/" | 254 << streamparm.parm.capture.timeperframe.denominator << "/" |
255 << streamparm.parm.capture.timeperframe.numerator; | 255 << streamparm.parm.capture.timeperframe.numerator; |
256 } | 256 } |
257 } | 257 } |
258 // TODO(mcasas): what should be done if the camera driver does not allow | 258 // TODO(mcasas): what should be done if the camera driver does not allow |
259 // framerate configuration, or the actual one is different from the desired? | 259 // framerate configuration, or the actual one is different from the desired? |
260 | 260 |
(...skipping 14 matching lines...) Expand all Loading... |
275 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, | 275 capture_format_.frame_size.SetSize(video_fmt_.fmt.pix.width, |
276 video_fmt_.fmt.pix.height); | 276 video_fmt_.fmt.pix.height); |
277 capture_format_.frame_rate = frame_rate; | 277 capture_format_.frame_rate = frame_rate; |
278 capture_format_.pixel_format = pixel_format; | 278 capture_format_.pixel_format = pixel_format; |
279 | 279 |
280 v4l2_requestbuffers r_buffer = {}; | 280 v4l2_requestbuffers r_buffer = {}; |
281 r_buffer.type = capture_type_; | 281 r_buffer.type = capture_type_; |
282 r_buffer.memory = V4L2_MEMORY_MMAP; | 282 r_buffer.memory = V4L2_MEMORY_MMAP; |
283 r_buffer.count = kNumVideoBuffers; | 283 r_buffer.count = kNumVideoBuffers; |
284 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { | 284 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) { |
285 SetErrorState("Error requesting MMAP buffers from V4L2"); | 285 SetErrorState(FROM_HERE, "Error requesting MMAP buffers from V4L2"); |
286 return; | 286 return; |
287 } | 287 } |
288 for (unsigned int i = 0; i < r_buffer.count; ++i) { | 288 for (unsigned int i = 0; i < r_buffer.count; ++i) { |
289 if (!MapAndQueueBuffer(i)) { | 289 if (!MapAndQueueBuffer(i)) { |
290 SetErrorState("Allocate buffer failed"); | 290 SetErrorState(FROM_HERE, "Allocate buffer failed"); |
291 return; | 291 return; |
292 } | 292 } |
293 } | 293 } |
294 | 294 |
295 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) < | 295 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMON, &capture_type_)) < |
296 0) { | 296 0) { |
297 SetErrorState("VIDIOC_STREAMON failed"); | 297 SetErrorState(FROM_HERE, "VIDIOC_STREAMON failed"); |
298 return; | 298 return; |
299 } | 299 } |
300 | 300 |
301 is_capturing_ = true; | 301 is_capturing_ = true; |
302 // Post task to start fetching frames from v4l2. | 302 // Post task to start fetching frames from v4l2. |
303 v4l2_task_runner_->PostTask( | 303 v4l2_task_runner_->PostTask( |
304 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); | 304 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
305 } | 305 } |
306 | 306 |
307 void V4L2CaptureDelegate::StopAndDeAllocate() { | 307 void V4L2CaptureDelegate::StopAndDeAllocate() { |
308 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 308 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
309 // The order is important: stop streaming, clear |buffer_pool_|, | 309 // The order is important: stop streaming, clear |buffer_pool_|, |
310 // thus munmap()ing the v4l2_buffers, and then return them to the OS. | 310 // thus munmap()ing the v4l2_buffers, and then return them to the OS. |
311 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) < | 311 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_STREAMOFF, &capture_type_)) < |
312 0) { | 312 0) { |
313 SetErrorState("VIDIOC_STREAMOFF failed"); | 313 SetErrorState(FROM_HERE, "VIDIOC_STREAMOFF failed"); |
314 return; | 314 return; |
315 } | 315 } |
316 | 316 |
317 buffer_tracker_pool_.clear(); | 317 buffer_tracker_pool_.clear(); |
318 | 318 |
319 v4l2_requestbuffers r_buffer = {}; | 319 v4l2_requestbuffers r_buffer = {}; |
320 r_buffer.type = capture_type_; | 320 r_buffer.type = capture_type_; |
321 r_buffer.memory = V4L2_MEMORY_MMAP; | 321 r_buffer.memory = V4L2_MEMORY_MMAP; |
322 r_buffer.count = 0; | 322 r_buffer.count = 0; |
323 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) | 323 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_REQBUFS, &r_buffer)) < 0) |
324 SetErrorState("Failed to VIDIOC_REQBUFS with count = 0"); | 324 SetErrorState(FROM_HERE, "Failed to VIDIOC_REQBUFS with count = 0"); |
325 | 325 |
326 // At this point we can close the device. | 326 // At this point we can close the device. |
327 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. | 327 // This is also needed for correctly changing settings later via VIDIOC_S_FMT. |
328 device_fd_.reset(); | 328 device_fd_.reset(); |
329 is_capturing_ = false; | 329 is_capturing_ = false; |
330 client_.reset(); | 330 client_.reset(); |
331 } | 331 } |
332 | 332 |
333 void V4L2CaptureDelegate::SetRotation(int rotation) { | 333 void V4L2CaptureDelegate::SetRotation(int rotation) { |
334 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 334 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 void V4L2CaptureDelegate::DoCapture() { | 370 void V4L2CaptureDelegate::DoCapture() { |
371 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 371 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
372 if (!is_capturing_) | 372 if (!is_capturing_) |
373 return; | 373 return; |
374 | 374 |
375 pollfd device_pfd = {}; | 375 pollfd device_pfd = {}; |
376 device_pfd.fd = device_fd_.get(); | 376 device_pfd.fd = device_fd_.get(); |
377 device_pfd.events = POLLIN; | 377 device_pfd.events = POLLIN; |
378 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs)); | 378 const int result = HANDLE_EINTR(poll(&device_pfd, 1, kCaptureTimeoutMs)); |
379 if (result < 0) { | 379 if (result < 0) { |
380 SetErrorState("Poll failed"); | 380 SetErrorState(FROM_HERE, "Poll failed"); |
381 return; | 381 return; |
382 } | 382 } |
383 // Check if poll() timed out; track the amount of times it did in a row and | 383 // Check if poll() timed out; track the amount of times it did in a row and |
384 // throw an error if it times out too many times. | 384 // throw an error if it times out too many times. |
385 if (result == 0) { | 385 if (result == 0) { |
386 timeout_count_++; | 386 timeout_count_++; |
387 if (timeout_count_ >= kContinuousTimeoutLimit) { | 387 if (timeout_count_ >= kContinuousTimeoutLimit) { |
388 SetErrorState("Multiple continuous timeouts while read-polling."); | 388 SetErrorState(FROM_HERE, |
| 389 "Multiple continuous timeouts while read-polling."); |
389 timeout_count_ = 0; | 390 timeout_count_ = 0; |
390 return; | 391 return; |
391 } | 392 } |
392 } else { | 393 } else { |
393 timeout_count_ = 0; | 394 timeout_count_ = 0; |
394 } | 395 } |
395 | 396 |
396 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. | 397 // Deenqueue, send and reenqueue a buffer if the driver has filled one in. |
397 if (device_pfd.revents & POLLIN) { | 398 if (device_pfd.revents & POLLIN) { |
398 v4l2_buffer buffer; | 399 v4l2_buffer buffer; |
399 FillV4L2Buffer(&buffer, 0); | 400 FillV4L2Buffer(&buffer, 0); |
400 | 401 |
401 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { | 402 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_DQBUF, &buffer)) < 0) { |
402 SetErrorState("Failed to dequeue capture buffer"); | 403 SetErrorState(FROM_HERE, "Failed to dequeue capture buffer"); |
403 return; | 404 return; |
404 } | 405 } |
405 | 406 |
406 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); | 407 SetPayloadSize(buffer_tracker_pool_[buffer.index], buffer); |
407 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); | 408 SendBuffer(buffer_tracker_pool_[buffer.index], video_fmt_); |
408 | 409 |
409 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { | 410 if (HANDLE_EINTR(ioctl(device_fd_.get(), VIDIOC_QBUF, &buffer)) < 0) { |
410 SetErrorState("Failed to enqueue capture buffer"); | 411 SetErrorState(FROM_HERE, "Failed to enqueue capture buffer"); |
411 return; | 412 return; |
412 } | 413 } |
413 } | 414 } |
414 | 415 |
415 v4l2_task_runner_->PostTask( | 416 v4l2_task_runner_->PostTask( |
416 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); | 417 FROM_HERE, base::Bind(&V4L2CaptureDelegate::DoCapture, this)); |
417 } | 418 } |
418 | 419 |
419 void V4L2CaptureDelegate::SetErrorState(const std::string& reason) { | 420 void V4L2CaptureDelegate::SetErrorState( |
| 421 const tracked_objects::Location& from_here, |
| 422 const std::string& reason) { |
420 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); | 423 DCHECK(v4l2_task_runner_->BelongsToCurrentThread()); |
421 is_capturing_ = false; | 424 is_capturing_ = false; |
422 client_->OnError(reason); | 425 client_->OnError(from_here, reason); |
423 } | 426 } |
424 | 427 |
425 } // namespace media | 428 } // namespace media |
OLD | NEW |