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