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 // Note: ported from Chromium commit head: 09ea0d2 5 // Note: it's also merged with generic_v4l2_device.cc (head: a9d98e6) 6 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <poll.h> 10 #include <string.h> 11 #include <sys/eventfd.h> 12 #include <sys/ioctl.h> 13 #include <sys/mman.h> 14 15 #include "base/numerics/safe_conversions.h" 16 #include "base/posix/eintr_wrapper.h" 17 #include "base/strings/stringprintf.h" 18 #include "v4l2_device.h" 19 20 #define DVLOGF(level) DVLOG(level) << __func__ << "(): " 21 #define VLOGF(level) VLOG(level) << __func__ << "(): " 22 #define VPLOGF(level) VPLOG(level) << __func__ << "(): " 23 24 namespace media { 25 26 V4L2Device::V4L2Device() {} 27 28 V4L2Device::~V4L2Device() { 29 CloseDevice(); 30 } 31 32 // static 33 VideoPixelFormat V4L2Device::V4L2PixFmtToVideoPixelFormat(uint32_t pix_fmt) { 34 switch (pix_fmt) { 35 case V4L2_PIX_FMT_NV12: 36 case V4L2_PIX_FMT_NV12M: 37 return PIXEL_FORMAT_NV12; 38 39 case V4L2_PIX_FMT_MT21: 40 return PIXEL_FORMAT_MT21; 41 42 case V4L2_PIX_FMT_YUV420: 43 case V4L2_PIX_FMT_YUV420M: 44 return PIXEL_FORMAT_I420; 45 46 case V4L2_PIX_FMT_YVU420: 47 return PIXEL_FORMAT_YV12; 48 49 case V4L2_PIX_FMT_YUV422M: 50 return PIXEL_FORMAT_I422; 51 52 case V4L2_PIX_FMT_RGB32: 53 return PIXEL_FORMAT_ARGB; 54 55 default: 56 DVLOGF(1) << "Add more cases as needed"; 57 return PIXEL_FORMAT_UNKNOWN; 58 } 59 } 60 61 // static 62 uint32_t V4L2Device::VideoPixelFormatToV4L2PixFmt(VideoPixelFormat format) { 63 switch (format) { 64 case PIXEL_FORMAT_NV12: 65 return V4L2_PIX_FMT_NV12M; 66 67 case PIXEL_FORMAT_MT21: 68 return V4L2_PIX_FMT_MT21; 69 70 case PIXEL_FORMAT_I420: 71 return V4L2_PIX_FMT_YUV420M; 72 73 case PIXEL_FORMAT_YV12: 74 return V4L2_PIX_FMT_YVU420; 75 76 default: 77 LOG(FATAL) << "Add more cases as needed"; 78 return 0; 79 } 80 } 81 82 // static 83 uint32_t V4L2Device::VideoCodecProfileToV4L2PixFmt(VideoCodecProfile profile, 84 bool slice_based) { 85 if (profile >= H264PROFILE_MIN && profile <= H264PROFILE_MAX) { 86 if (slice_based) 87 return V4L2_PIX_FMT_H264_SLICE; 88 else 89 return V4L2_PIX_FMT_H264; 90 } else if (profile >= VP8PROFILE_MIN && profile <= VP8PROFILE_MAX) { 91 if (slice_based) 92 return V4L2_PIX_FMT_VP8_FRAME; 93 else 94 return V4L2_PIX_FMT_VP8; 95 } else if (profile >= VP9PROFILE_MIN && profile <= VP9PROFILE_MAX) { 96 if (slice_based) 97 return V4L2_PIX_FMT_VP9_FRAME; 98 else 99 return V4L2_PIX_FMT_VP9; 100 } else { 101 LOG(FATAL) << "Add more cases as needed"; 102 return 0; 103 } 104 } 105 106 // static 107 std::vector<VideoCodecProfile> V4L2Device::V4L2PixFmtToVideoCodecProfiles( 108 uint32_t pix_fmt, 109 bool is_encoder) { 110 VideoCodecProfile min_profile, max_profile; 111 std::vector<VideoCodecProfile> profiles; 112 113 switch (pix_fmt) { 114 case V4L2_PIX_FMT_H264: 115 case V4L2_PIX_FMT_H264_SLICE: 116 if (is_encoder) { 117 // TODO(posciak): need to query the device for supported H.264 profiles, 118 // for now choose Main as a sensible default. 119 min_profile = H264PROFILE_MAIN; 120 max_profile = H264PROFILE_MAIN; 121 } else { 122 min_profile = H264PROFILE_MIN; 123 max_profile = H264PROFILE_MAX; 124 } 125 break; 126 127 case V4L2_PIX_FMT_VP8: 128 case V4L2_PIX_FMT_VP8_FRAME: 129 min_profile = VP8PROFILE_MIN; 130 max_profile = VP8PROFILE_MAX; 131 break; 132 133 case V4L2_PIX_FMT_VP9: 134 case V4L2_PIX_FMT_VP9_FRAME: 135 min_profile = VP9PROFILE_MIN; 136 max_profile = VP9PROFILE_MAX; 137 break; 138 139 default: 140 VLOGF(1) << "Unhandled pixelformat " << std::hex << "0x" << pix_fmt; 141 return profiles; 142 } 143 144 for (int profile = min_profile; profile <= max_profile; ++profile) 145 profiles.push_back(static_cast<VideoCodecProfile>(profile)); 146 147 return profiles; 148 } 149 150 int V4L2Device::Ioctl(int request, void* arg) { 151 DCHECK(device_fd_.is_valid()); 152 return HANDLE_EINTR(ioctl(device_fd_.get(), request, arg)); 153 } 154 155 bool V4L2Device::Poll(bool poll_device, bool* event_pending) { 156 struct pollfd pollfds[2]; 157 nfds_t nfds; 158 int pollfd = -1; 159 160 pollfds[0].fd = device_poll_interrupt_fd_.get(); 161 pollfds[0].events = POLLIN | POLLERR; 162 nfds = 1; 163 164 if (poll_device) { 165 DVLOGF(5) << "Poll(): adding device fd to poll() set"; 166 pollfds[nfds].fd = device_fd_.get(); 167 pollfds[nfds].events = POLLIN | POLLOUT | POLLERR | POLLPRI; 168 pollfd = nfds; 169 nfds++; 170 } 171 172 if (HANDLE_EINTR(poll(pollfds, nfds, -1)) == -1) { 173 VPLOGF(1) << "poll() failed"; 174 return false; 175 } 176 *event_pending = (pollfd != -1 && pollfds[pollfd].revents & POLLPRI); 177 return true; 178 } 179 180 void* V4L2Device::Mmap(void* addr, 181 unsigned int len, 182 int prot, 183 int flags, 184 unsigned int offset) { 185 DCHECK(device_fd_.is_valid()); 186 return mmap(addr, len, prot, flags, device_fd_.get(), offset); 187 } 188 189 void V4L2Device::Munmap(void* addr, unsigned int len) { 190 munmap(addr, len); 191 } 192 193 bool V4L2Device::SetDevicePollInterrupt() { 194 DVLOGF(4); 195 196 const uint64_t buf = 1; 197 if (HANDLE_EINTR(write(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) == 198 -1) { 199 VPLOGF(1) << "write() failed"; 200 return false; 201 } 202 return true; 203 } 204 205 bool V4L2Device::ClearDevicePollInterrupt() { 206 DVLOGF(5); 207 208 uint64_t buf; 209 if (HANDLE_EINTR(read(device_poll_interrupt_fd_.get(), &buf, sizeof(buf))) == 210 -1) { 211 if (errno == EAGAIN) { 212 // No interrupt flag set, and we're reading nonblocking. Not an error. 213 return true; 214 } else { 215 VPLOGF(1) << "read() failed"; 216 return false; 217 } 218 } 219 return true; 220 } 221 222 bool V4L2Device::Open(Type type, uint32_t v4l2_pixfmt) { 223 VLOGF(2); 224 std::string path = GetDevicePathFor(type, v4l2_pixfmt); 225 226 if (path.empty()) { 227 VLOGF(1) << "No devices supporting " << std::hex << "0x" << v4l2_pixfmt 228 << " for type: " << static_cast<int>(type); 229 return false; 230 } 231 232 if (!OpenDevicePath(path, type)) { 233 VLOGF(1) << "Failed opening " << path; 234 return false; 235 } 236 237 device_poll_interrupt_fd_.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)); 238 if (!device_poll_interrupt_fd_.is_valid()) { 239 VLOGF(1) << "Failed creating a poll interrupt fd"; 240 return false; 241 } 242 243 return true; 244 } 245 246 std::vector<base::ScopedFD> V4L2Device::GetDmabufsForV4L2Buffer( 247 int index, 248 size_t num_planes, 249 enum v4l2_buf_type buf_type) { 250 VLOGF(2); 251 DCHECK(V4L2_TYPE_IS_MULTIPLANAR(buf_type)); 252 253 std::vector<base::ScopedFD> dmabuf_fds; 254 for (size_t i = 0; i < num_planes; ++i) { 255 struct v4l2_exportbuffer expbuf; 256 memset(&expbuf, 0, sizeof(expbuf)); 257 expbuf.type = buf_type; 258 expbuf.index = index; 259 expbuf.plane = i; 260 expbuf.flags = O_CLOEXEC; 261 if (Ioctl(VIDIOC_EXPBUF, &expbuf) != 0) { 262 dmabuf_fds.clear(); 263 break; 264 } 265 266 dmabuf_fds.push_back(base::ScopedFD(expbuf.fd)); 267 } 268 269 return dmabuf_fds; 270 } 271 272 VideoDecodeAccelerator::SupportedProfiles 273 V4L2Device::GetSupportedDecodeProfiles(const size_t num_formats, 274 const uint32_t pixelformats[]) { 275 VideoDecodeAccelerator::SupportedProfiles supported_profiles; 276 277 Type type = Type::kDecoder; 278 const auto& devices = GetDevicesForType(type); 279 for (const auto& device : devices) { 280 if (!OpenDevicePath(device.first, type)) { 281 VLOGF(1) << "Failed opening " << device.first; 282 continue; 283 } 284 285 const auto& profiles = 286 EnumerateSupportedDecodeProfiles(num_formats, pixelformats); 287 supported_profiles.insert(supported_profiles.end(), profiles.begin(), 288 profiles.end()); 289 CloseDevice(); 290 } 291 292 return supported_profiles; 293 } 294 295 void V4L2Device::GetSupportedResolution(uint32_t pixelformat, 296 Size* min_resolution, 297 Size* max_resolution) { 298 max_resolution->SetSize(0, 0); 299 min_resolution->SetSize(0, 0); 300 v4l2_frmsizeenum frame_size; 301 memset(&frame_size, 0, sizeof(frame_size)); 302 frame_size.pixel_format = pixelformat; 303 for (; Ioctl(VIDIOC_ENUM_FRAMESIZES, &frame_size) == 0; ++frame_size.index) { 304 if (frame_size.type == V4L2_FRMSIZE_TYPE_DISCRETE) { 305 if (frame_size.discrete.width >= 306 base::checked_cast<uint32_t>(max_resolution->width()) && 307 frame_size.discrete.height >= 308 base::checked_cast<uint32_t>(max_resolution->height())) { 309 max_resolution->SetSize(frame_size.discrete.width, 310 frame_size.discrete.height); 311 } 312 if (min_resolution->IsEmpty() || 313 (frame_size.discrete.width <= 314 base::checked_cast<uint32_t>(min_resolution->width()) && 315 frame_size.discrete.height <= 316 base::checked_cast<uint32_t>(min_resolution->height()))) { 317 min_resolution->SetSize(frame_size.discrete.width, 318 frame_size.discrete.height); 319 } 320 } else if (frame_size.type == V4L2_FRMSIZE_TYPE_STEPWISE || 321 frame_size.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) { 322 max_resolution->SetSize(frame_size.stepwise.max_width, 323 frame_size.stepwise.max_height); 324 min_resolution->SetSize(frame_size.stepwise.min_width, 325 frame_size.stepwise.min_height); 326 break; 327 } 328 } 329 if (max_resolution->IsEmpty()) { 330 max_resolution->SetSize(1920, 1088); 331 VLOGF(1) << "GetSupportedResolution failed to get maximum resolution for " 332 << "fourcc " << std::hex << pixelformat 333 << ", fall back to " << max_resolution->ToString(); 334 } 335 if (min_resolution->IsEmpty()) { 336 min_resolution->SetSize(16, 16); 337 VLOGF(1) << "GetSupportedResolution failed to get minimum resolution for " 338 << "fourcc " << std::hex << pixelformat 339 << ", fall back to " << min_resolution->ToString(); 340 } 341 } 342 343 std::vector<uint32_t> V4L2Device::EnumerateSupportedPixelformats( 344 v4l2_buf_type buf_type) { 345 std::vector<uint32_t> pixelformats; 346 347 v4l2_fmtdesc fmtdesc; 348 memset(&fmtdesc, 0, sizeof(fmtdesc)); 349 fmtdesc.type = buf_type; 350 351 for (; Ioctl(VIDIOC_ENUM_FMT, &fmtdesc) == 0; ++fmtdesc.index) { 352 DVLOGF(3) << "Found " << fmtdesc.description << std::hex << " (0x" 353 << fmtdesc.pixelformat << ")"; 354 pixelformats.push_back(fmtdesc.pixelformat); 355 } 356 357 return pixelformats; 358 } 359 360 VideoDecodeAccelerator::SupportedProfiles 361 V4L2Device::EnumerateSupportedDecodeProfiles(const size_t num_formats, 362 const uint32_t pixelformats[]) { 363 VideoDecodeAccelerator::SupportedProfiles profiles; 364 365 const auto& supported_pixelformats = 366 EnumerateSupportedPixelformats(V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 367 368 for (uint32_t pixelformat : supported_pixelformats) { 369 if (std::find(pixelformats, pixelformats + num_formats, pixelformat) == 370 pixelformats + num_formats) 371 continue; 372 373 VideoDecodeAccelerator::SupportedProfile profile; 374 GetSupportedResolution(pixelformat, &profile.min_resolution, 375 &profile.max_resolution); 376 377 const auto video_codec_profiles = 378 V4L2PixFmtToVideoCodecProfiles(pixelformat, false); 379 380 for (const auto& video_codec_profile : video_codec_profiles) { 381 profile.profile = video_codec_profile; 382 profiles.push_back(profile); 383 384 DVLOGF(3) << "Found decoder profile " << GetProfileName(profile.profile) 385 << ", resolutions: " << profile.min_resolution.ToString() << " " 386 << profile.max_resolution.ToString(); 387 } 388 } 389 390 return profiles; 391 } 392 393 bool V4L2Device::OpenDevicePath(const std::string& path, Type type) { 394 DCHECK(!device_fd_.is_valid()); 395 396 device_fd_.reset( 397 HANDLE_EINTR(open(path.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC))); 398 if (!device_fd_.is_valid()) 399 return false; 400 401 return true; 402 } 403 404 void V4L2Device::CloseDevice() { 405 VLOGF(2); 406 device_fd_.reset(); 407 } 408 409 void V4L2Device::EnumerateDevicesForType(Type type) { 410 static const std::string kDecoderDevicePattern = "/dev/video-dec"; 411 std::string device_pattern; 412 v4l2_buf_type buf_type; 413 switch (type) { 414 case Type::kDecoder: 415 device_pattern = kDecoderDevicePattern; 416 buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 417 break; 418 default: 419 LOG(ERROR) << "Only decoder type is supported!!"; 420 return; 421 } 422 423 std::vector<std::string> candidate_paths; 424 425 // TODO(posciak): Remove this legacy unnumbered device once 426 // all platforms are updated to use numbered devices. 427 candidate_paths.push_back(device_pattern); 428 429 // We are sandboxed, so we can't query directory contents to check which 430 // devices are actually available. Try to open the first 10; if not present, 431 // we will just fail to open immediately. 432 for (int i = 0; i < 10; ++i) { 433 candidate_paths.push_back( 434 base::StringPrintf("%s%d", device_pattern.c_str(), i)); 435 } 436 437 Devices devices; 438 for (const auto& path : candidate_paths) { 439 if (!OpenDevicePath(path, type)) 440 continue; 441 442 const auto& supported_pixelformats = 443 EnumerateSupportedPixelformats(buf_type); 444 if (!supported_pixelformats.empty()) { 445 DVLOGF(3) << "Found device: " << path; 446 devices.push_back(std::make_pair(path, supported_pixelformats)); 447 } 448 449 CloseDevice(); 450 } 451 452 DCHECK_EQ(devices_by_type_.count(type), 0u); 453 devices_by_type_[type] = devices; 454 } 455 456 const V4L2Device::Devices& V4L2Device::GetDevicesForType(Type type) { 457 if (devices_by_type_.count(type) == 0) 458 EnumerateDevicesForType(type); 459 460 DCHECK_NE(devices_by_type_.count(type), 0u); 461 return devices_by_type_[type]; 462 } 463 464 std::string V4L2Device::GetDevicePathFor(Type type, uint32_t pixfmt) { 465 const Devices& devices = GetDevicesForType(type); 466 467 for (const auto& device : devices) { 468 if (std::find(device.second.begin(), device.second.end(), pixfmt) != 469 device.second.end()) 470 return device.first; 471 } 472 473 return std::string(); 474 } 475 476 } // namespace media 477