Fix query devices on linux

This commit is contained in:
John Zhao 2016-02-12 21:30:31 +08:00
parent 03d6043c08
commit 460c707253

72
src/uvc/uvc-v4l2.cc Normal file → Executable file
View File

@ -29,15 +29,31 @@ namespace uvc {
} while (0) } while (0)
/* /*
class device_error : public std::exception {
public:
explicit device_error(const std::string &what_arg) noexcept
: what_message_(std::move(what_arg)) {}
explicit device_error(const char *what_arg) noexcept
: what_message_(std::move(what_arg)) {}
const char *what() const noexcept {
return what_message_.c_str();
}
private:
std::string what_message_;
};
*/
struct throw_error { struct throw_error {
throw_error() {} throw_error() = default;
explicit throw_error(const std::string &s) { explicit throw_error(const std::string &s) {
ss << s; ss << s;
} }
~throw_error() { ~throw_error() noexcept(false) {
throw std::runtime_error(ss.str()); throw std::runtime_error(ss.str());
// throw device_error(ss.str());
} }
template<class T> template<class T>
@ -48,7 +64,6 @@ struct throw_error {
std::ostringstream ss; std::ostringstream ss;
}; };
*/
static int xioctl(int fh, int request, void *arg) { static int xioctl(int fh, int request, void *arg) {
int r; int r;
@ -77,7 +92,6 @@ struct device {
const std::shared_ptr<context> parent; const std::shared_ptr<context> parent;
std::string dev_name; // Device name (typically of the form /dev/video*) std::string dev_name; // Device name (typically of the form /dev/video*)
int busnum, devnum, parent_devnum; // USB device bus number and device number
std::string name; // Device description name std::string name; // Device description name
int vid, pid, mi; // Vendor ID, product ID, and multiple interface index int vid, pid, mi; // Vendor ID, product ID, and multiple interface index
@ -98,72 +112,52 @@ struct device {
struct stat st; struct stat st;
if (stat(dev_name.c_str(), &st) < 0) { // file status if (stat(dev_name.c_str(), &st) < 0) { // file status
LOG(FATAL) << "Cannot identify '" << dev_name << "': " << errno << ", " throw_error() << "Cannot identify '" << dev_name << "': " << errno << ", "
<< strerror(errno); << strerror(errno);
} }
if (!S_ISCHR(st.st_mode)) { // character device? if (!S_ISCHR(st.st_mode)) { // character device?
LOG(FATAL) << dev_name << " is no device"; throw_error() << dev_name << " is no device";
} }
// Search directory and up to three parent directories to find busnum/devnum
std::ostringstream ss;
ss << "/sys/dev/char/" << major(st.st_rdev) << ":" << minor(st.st_rdev)
<< "/device/";
auto path = ss.str();
bool good = false;
for (int i = 0; i <= 3; ++i) {
if (std::ifstream(path + "busnum") >> busnum) {
if (std::ifstream(path + "devnum") >> devnum) {
if (std::ifstream(path + "../devnum") >> parent_devnum) {
good = true;
break;
}
}
}
path += "../";
}
if (!good)
LOG(FATAL) << "Failed to read busnum/devnum";
if (!(std::ifstream("/sys/class/video4linux/" + name + "/name") >> if (!(std::ifstream("/sys/class/video4linux/" + name + "/name") >>
this->name)) this->name))
LOG(FATAL) << "Failed to read name"; throw_error() << "Failed to read name";
std::string modalias; std::string modalias;
if (!(std::ifstream( if (!(std::ifstream(
"/sys/class/video4linux/" + name + "/device/modalias") >> "/sys/class/video4linux/" + name + "/device/modalias") >>
modalias)) modalias))
LOG(FATAL) << "Failed to read modalias"; throw_error() << "Failed to read modalias";
if (modalias.size() < 14 || modalias.substr(0, 5) != "usb:v" || if (modalias.size() < 14 || modalias.substr(0, 5) != "usb:v" ||
modalias[9] != 'p') modalias[9] != 'p')
LOG(FATAL) << "Not a usb format modalias"; throw_error() << "Not a usb format modalias";
if (!(std::istringstream(modalias.substr(5, 4)) >> std::hex >> vid)) if (!(std::istringstream(modalias.substr(5, 4)) >> std::hex >> vid))
LOG(FATAL) << "Failed to read vendor ID"; throw_error() << "Failed to read vendor ID";
if (!(std::istringstream(modalias.substr(10, 4)) >> std::hex >> pid)) if (!(std::istringstream(modalias.substr(10, 4)) >> std::hex >> pid))
LOG(FATAL) << "Failed to read product ID"; throw_error() << "Failed to read product ID";
if (!(std::ifstream( if (!(std::ifstream(
"/sys/class/video4linux/" + name + "/device/bInterfaceNumber") >> "/sys/class/video4linux/" + name + "/device/bInterfaceNumber") >>
std::hex >> mi)) std::hex >> mi))
LOG(FATAL) << "Failed to read interface number"; throw_error() << "Failed to read interface number";
fd = open(dev_name.c_str(), O_RDWR | O_NONBLOCK, 0); fd = open(dev_name.c_str(), O_RDWR | O_NONBLOCK, 0);
if (fd < 0) { if (fd < 0) {
LOG(FATAL) << "Cannot open '" << dev_name << "': " << errno << ", " throw_error() << "Cannot open '" << dev_name << "': " << errno << ", "
<< strerror(errno); << strerror(errno);
} }
v4l2_capability cap = {}; v4l2_capability cap = {};
if (xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) { if (xioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
if (errno == EINVAL) if (errno == EINVAL)
LOG(FATAL) << dev_name << " is no V4L2 device"; throw_error() << dev_name << " is no V4L2 device";
else else
LOG_ERROR(FATAL, "VIDIOC_QUERYCAP"); throw_error() << "VIDIOC_QUERYCAP error " << errno << ", "
<< strerror(errno);
} }
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
LOG(FATAL) << dev_name + " is no video capture device"; throw_error() << dev_name + " is no video capture device";
if (!(cap.capabilities & V4L2_CAP_STREAMING)) if (!(cap.capabilities & V4L2_CAP_STREAMING))
LOG(FATAL) << dev_name + " does not support streaming I/O"; throw_error() << dev_name + " does not support streaming I/O";
// Select video input, video standard and tune here. // Select video input, video standard and tune here.
v4l2_cropcap cropcap = {}; v4l2_cropcap cropcap = {};
@ -437,7 +431,7 @@ std::vector<std::shared_ptr<device>> query_devices(
try { try {
devices.push_back(std::make_shared<device>(context, name)); devices.push_back(std::make_shared<device>(context, name));
} catch (const std::exception &e) { } catch (const std::exception &e) {
LOG(INFO) << "Not a USB video device: " << e.what(); VLOG(2) << "Not a USB video device: " << e.what();
} }
} }
closedir(dir); closedir(dir);