diff options
Diffstat (limited to 'widget/gtk/v4l2test/v4l2test.cpp')
-rw-r--r-- | widget/gtk/v4l2test/v4l2test.cpp | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/widget/gtk/v4l2test/v4l2test.cpp b/widget/gtk/v4l2test/v4l2test.cpp new file mode 100644 index 0000000000..6ee685d9ed --- /dev/null +++ b/widget/gtk/v4l2test/v4l2test.cpp @@ -0,0 +1,188 @@ +/* -*- 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 <cstdio> +#include <cstdlib> +#include <errno.h> +#include <fcntl.h> +#if defined(__NetBSD__) || defined(__OpenBSD__) +# include <sys/videoio.h> +#elif defined(__sun) +# include <sys/videodev2.h> +#else +# include <linux/videodev2.h> +#endif +#include <sys/ioctl.h> +#include <unistd.h> +#include <string.h> +#include <getopt.h> +#include <stdint.h> +#include <stdarg.h> + +#if defined(MOZ_ASAN) || defined(FUZZING) +# include <signal.h> +#endif + +#include "mozilla/ScopeExit.h" + +#ifdef __SUNPRO_CC +# include <stdio.h> +#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; +} |