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