blob: e40620d31d1d465141d19924b1d1f5e7deb05b07 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media_capabilities/camera.h"
#include <fcntl.h>
#include <linux/media.h>
#include <linux/videodev2.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <vector>
#include <base/files/file.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include "media_capabilities/common.h"
namespace {
enum class CameraType {
kUnknown,
kUVC,
kVivid,
};
CameraType GetCameraType(int device_fd) {
struct v4l2_capability cap;
memset(&cap, 0, sizeof(cap));
if (Ioctl(device_fd, VIDIOC_QUERYCAP, &cap))
return CameraType::kUnknown;
if (strcmp((const char*)cap.driver, "uvcvideo") == 0)
return CameraType::kUVC;
if (strcmp((const char*)cap.driver, "vivid") == 0)
return CameraType::kVivid;
return CameraType::kUnknown;
}
bool IsBuiltinMipiCamera(int device_fd) {
struct media_entity_desc desc;
memset(&desc, 0, sizeof(desc));
for (desc.id = MEDIA_ENT_ID_FLAG_NEXT;
!Ioctl(device_fd, MEDIA_IOC_ENUM_ENTITIES, &desc);
desc.id |= MEDIA_ENT_ID_FLAG_NEXT) {
if (desc.type == MEDIA_ENT_T_V4L2_SUBDEV_SENSOR)
return true;
}
return false;
}
bool IsBuiltinUSBCamera(int device_fd, const base::FilePath& device_path) {
if (GetCameraType(device_fd) != CameraType::kUVC)
return false;
const base::FilePath base_name = device_path.BaseName();
if (base_name.empty()) {
LOG(ERROR) << "base file is empty, path=" << device_path;
return false;
}
const base::FilePath vendorid_path = base::FilePath("/sys/class/video4linux/")
.Append(base_name)
.Append("device/../idVendor");
base::FilePath normalized_vendorid_path;
if (!base::NormalizeFilePath(vendorid_path, &normalized_vendorid_path)) {
LOG(ERROR) << "Failed to normalize vendor id path: " << vendorid_path;
return false;
}
std::string vendor_id;
if (!base::ReadFileToString(normalized_vendorid_path, &vendor_id)) {
LOG(ERROR) << "Failed to read vendor id file: " << normalized_vendorid_path;
return false;
}
// Check if the camera is not an external one. The vendor IDs of external
// cameras used in the lab need to be listed here. If there are many kinds of
// external cameras, we might want to have a list of vid:pid of builtin
// cameras instead.
const char* kExternalCameraVendorIds[] = {
"046d", // Logitech
"2bd9", // Huddly GO
};
for (const char* external_vendor_id : kExternalCameraVendorIds) {
if (vendor_id == external_vendor_id)
return false;
}
return true;
}
bool IsVividCamera(int device_fd) {
if (GetCameraType(device_fd) != CameraType::kVivid)
return false;
struct v4l2_capability cap;
memset(&cap, 0, sizeof(cap));
if (Ioctl(device_fd, VIDIOC_QUERYCAP, &cap)) {
PLOG(FATAL) << "VIDIOC_QUERYCAP failed: ";
return false;
}
// Check if vivid is emulating a video capture device.
const uint32_t mask = cap.capabilities;
constexpr uint32_t kCaptureMask =
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE;
constexpr uint32_t kOutputMask =
V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE;
constexpr uint32_t kM2mMask = V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE;
return (mask & kCaptureMask) && !(mask & kOutputMask) && !(mask & kM2mMask);
}
} // namespace
std::vector<Capability> DetectCameraCapabilities() {
const base::FilePath kVideoDeviceName("/dev/video");
bool has_builtin_usb_camera = false;
bool has_vivid_camera = false;
for (const base::FilePath& path : GetAllFilesWithPrefix(kVideoDeviceName)) {
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WRITE);
if (!file.IsValid())
continue;
const int fd = file.GetPlatformFile();
has_builtin_usb_camera |= IsBuiltinUSBCamera(fd, path);
has_vivid_camera |= IsVividCamera(fd);
}
const base::FilePath kMediaDeviceName("/dev/media");
bool has_builtin_mipi_camera = false;
for (const base::FilePath& path : GetAllFilesWithPrefix(kMediaDeviceName)) {
base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WRITE);
if (!file.IsValid())
continue;
const int fd = file.GetPlatformFile();
has_builtin_mipi_camera |= IsBuiltinMipiCamera(fd);
}
std::vector<Capability> capabilities;
if (has_builtin_usb_camera)
capabilities.push_back(Capability(CameraDescription::kBuiltinUSBCamera));
if (has_builtin_mipi_camera)
capabilities.push_back(Capability(CameraDescription::kBuiltinMIPICamera));
if (has_vivid_camera)
capabilities.push_back(Capability(CameraDescription::kVividCamera));
if (has_builtin_mipi_camera || has_builtin_usb_camera)
capabilities.push_back(Capability(CameraDescription::kBuiltinCamera));
if (has_builtin_mipi_camera || has_builtin_usb_camera || has_vivid_camera) {
capabilities.push_back(
Capability(CameraDescription::kBuiltinOrVividCamera));
}
return capabilities;
}