/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=8 et : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #if defined(__NetBSD__) || defined(__OpenBSD__) # include #elif defined(__sun) # include #else # include #endif #include #include #include #include #include #include #if defined(MOZ_ASAN) || defined(FUZZING) # include #endif #include "mozilla/ScopeExit.h" #ifdef __SUNPRO_CC # include #endif #include "mozilla/GfxInfoUtils.h" // Print test results to stdout and logging to stderr #define OUTPUT_PIPE 1 // Convert an integer pixfmt to a 4-character string. str must have a length // of at least 5 to include null-termination. static void v4l2_pixfmt_to_str(uint32_t pixfmt, char* str) { for (int i = 0; i < 4; i++) { str[i] = (pixfmt >> (i * 8)) & 0xff; } str[4] = 0; } // Enumerate the buffer formats supported on a V4L2 buffer queue. aTypeStr // is the queue type, i.e. CAPTURE or OUTPUT. static void v4l2_enumfmt(int aFd, int aType, const char* aTypeStr) { struct v4l2_fmtdesc fmt {}; char pix_fmt_str[5]; fmt.type = aType; record_value("V4L2_%s_FMTS\n", aTypeStr); for (fmt.index = 0;; fmt.index++) { int result = ioctl(aFd, VIDIOC_ENUM_FMT, &fmt); if (result < 0) { break; } v4l2_pixfmt_to_str(fmt.pixelformat, pix_fmt_str); record_value(" %s", pix_fmt_str); } record_value("\n"); } // Probe a V4L2 device to work out what it supports static void v4l2_check_device(const char* aVideoDevice) { int fd = -1; int result = -1; log("v4l2test probing device '%s'\n", aVideoDevice); auto autoRelease = mozilla::MakeScopeExit([&] { if (fd >= 0) { close(fd); } }); fd = open(aVideoDevice, O_RDWR | O_NONBLOCK, 0); if (fd < 0) { record_value("ERROR\nV4L2 failed to open device %s: %s\n", aVideoDevice, strerror(errno)); return; } struct v4l2_capability cap {}; result = ioctl(fd, VIDIOC_QUERYCAP, &cap); if (result < 0) { record_value("ERROR\nV4L2 device %s failed to query capabilities\n", aVideoDevice); return; } log("v4l2test driver %s card %s bus_info %s version %d\n", cap.driver, cap.card, cap.bus_info, cap.version); if (!(cap.capabilities & V4L2_CAP_DEVICE_CAPS)) { record_value("ERROR\nV4L2 device %s does not support DEVICE_CAPS\n", aVideoDevice); return; } if (!(cap.device_caps & V4L2_CAP_STREAMING)) { record_value("ERROR\nV4L2 device %s does not support V4L2_CAP_STREAMING\n", aVideoDevice); return; } // Work out whether the device supports planar or multiplaner bitbuffers and // framebuffers bool splane = cap.device_caps & V4L2_CAP_VIDEO_M2M; bool mplane = cap.device_caps & V4L2_CAP_VIDEO_M2M_MPLANE; if (!splane && !mplane) { record_value("ERROR\nV4L2 device %s does not support M2M modes\n", aVideoDevice); // (It's probably a webcam!) return; } record_value("V4L2_SPLANE\n%s\n", splane ? "TRUE" : "FALSE"); record_value("V4L2_MPLANE\n%s\n", mplane ? "TRUE" : "FALSE"); // Now check the formats supported for CAPTURE and OUTPUT buffers. // For a V4L2-M2M decoder, OUTPUT is actually the bitbuffers we put in and // CAPTURE is the framebuffers we get out. v4l2_enumfmt( fd, mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE : V4L2_BUF_TYPE_VIDEO_CAPTURE, "CAPTURE"); v4l2_enumfmt( fd, mplane ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT, "OUTPUT"); record_value("V4L2_SUPPORTED\nTRUE\n"); } static void PrintUsage() { printf( "Firefox V4L2-M2M probe utility\n" "\n" "usage: v4l2test [options]\n" "\n" "Options:\n" "\n" " -h --help show this message\n" " -d --device device Probe a v4l2 device (e.g. /dev/video10)\n" "\n"); } int main(int argc, char** argv) { struct option longOptions[] = {{"help", no_argument, nullptr, 'h'}, {"device", required_argument, nullptr, 'd'}, {nullptr, 0, nullptr, 0}}; const char* shortOptions = "hd:"; int c; const char* device = nullptr; while ((c = getopt_long(argc, argv, shortOptions, longOptions, nullptr)) != -1) { switch (c) { case 'd': device = optarg; break; case 'h': default: break; } } if (device) { #if defined(MOZ_ASAN) || defined(FUZZING) // If handle_segv=1 (default), then glxtest crash will print a sanitizer // report which can confuse the harness in fuzzing automation. signal(SIGSEGV, SIG_DFL); #endif const char* env = getenv("MOZ_GFX_DEBUG"); enable_logging = env && *env == '1'; output_pipe = OUTPUT_PIPE; if (!enable_logging) { close_logging(); } v4l2_check_device(device); record_flush(); return EXIT_SUCCESS; } PrintUsage(); return 0; }