blob: 1c433355c17b4dc225dd4964aa662a262db77005 [file] [log] [blame]
/*
Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo dot com>
Cleanup and VBI and audio in/out options, introduction in v4l-dvb,
support for most new APIs since 2006.
Copyright (C) 2004, 2006, 2007 Hans Verkuil <hverkuil@xs4all.nl>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
*/
#include <cctype>
#include <dirent.h>
#include <getopt.h>
#include <sys/epoll.h>
#include <linux/media.h>
#include "v4l2-ctl.h"
#include <media-info.h>
#ifdef HAVE_SYS_KLOG_H
#include <sys/klog.h>
#endif
char options[OptLast];
static int app_result;
int verbose;
unsigned capabilities;
unsigned out_capabilities;
unsigned priv_magic;
unsigned out_priv_magic;
bool is_multiplanar;
__u32 vidcap_buftype;
__u32 vidout_buftype;
static struct option long_options[] = {
{"list-audio-inputs", no_argument, nullptr, OptListAudioInputs},
{"list-audio-outputs", no_argument, nullptr, OptListAudioOutputs},
{"all", no_argument, nullptr, OptAll},
{"device", required_argument, nullptr, OptSetDevice},
{"out-device", required_argument, nullptr, OptSetOutDevice},
{"export-device", required_argument, nullptr, OptSetExportDevice},
{"media-bus-info", required_argument, nullptr, OptMediaBusInfo},
{"get-fmt-video", no_argument, nullptr, OptGetVideoFormat},
{"set-fmt-video", required_argument, nullptr, OptSetVideoFormat},
{"try-fmt-video", required_argument, nullptr, OptTryVideoFormat},
{"get-fmt-video-out", no_argument, nullptr, OptGetVideoOutFormat},
{"set-fmt-video-out", required_argument, nullptr, OptSetVideoOutFormat},
{"try-fmt-video-out", required_argument, nullptr, OptTryVideoOutFormat},
{"help", no_argument, nullptr, OptHelp},
{"help-tuner", no_argument, nullptr, OptHelpTuner},
{"help-io", no_argument, nullptr, OptHelpIO},
{"help-stds", no_argument, nullptr, OptHelpStds},
{"help-vidcap", no_argument, nullptr, OptHelpVidCap},
{"help-vidout", no_argument, nullptr, OptHelpVidOut},
{"help-overlay", no_argument, nullptr, OptHelpOverlay},
{"help-vbi", no_argument, nullptr, OptHelpVbi},
{"help-sdr", no_argument, nullptr, OptHelpSdr},
{"help-meta", no_argument, nullptr, OptHelpMeta},
{"help-subdev", no_argument, nullptr, OptHelpSubDev},
{"help-selection", no_argument, nullptr, OptHelpSelection},
{"help-misc", no_argument, nullptr, OptHelpMisc},
{"help-streaming", no_argument, nullptr, OptHelpStreaming},
{"help-edid", no_argument, nullptr, OptHelpEdid},
{"help-all", no_argument, nullptr, OptHelpAll},
#ifndef NO_LIBV4L2
{"wrapper", no_argument, nullptr, OptUseWrapper},
#endif
{"concise", no_argument, nullptr, OptConcise},
{"get-output", no_argument, nullptr, OptGetOutput},
{"set-output", required_argument, nullptr, OptSetOutput},
{"list-outputs", no_argument, nullptr, OptListOutputs},
{"list-inputs", no_argument, nullptr, OptListInputs},
{"get-input", no_argument, nullptr, OptGetInput},
{"set-input", required_argument, nullptr, OptSetInput},
{"get-audio-input", no_argument, nullptr, OptGetAudioInput},
{"set-audio-input", required_argument, nullptr, OptSetAudioInput},
{"get-audio-output", no_argument, nullptr, OptGetAudioOutput},
{"set-audio-output", required_argument, nullptr, OptSetAudioOutput},
{"get-freq", no_argument, nullptr, OptGetFreq},
{"set-freq", required_argument, nullptr, OptSetFreq},
{"list-standards", no_argument, nullptr, OptListStandards},
{"list-formats", optional_argument, nullptr, OptListFormats},
{"list-formats-ext", optional_argument, nullptr, OptListFormatsExt},
{"list-fields", no_argument, nullptr, OptListFields},
{"list-framesizes", required_argument, nullptr, OptListFrameSizes},
{"list-frameintervals", required_argument, nullptr, OptListFrameIntervals},
{"list-formats-overlay", no_argument, nullptr, OptListOverlayFormats},
{"list-formats-sdr", no_argument, nullptr, OptListSdrFormats},
{"list-formats-sdr-out", no_argument, nullptr, OptListSdrOutFormats},
{"list-formats-out", optional_argument, nullptr, OptListOutFormats},
{"list-formats-out-ext", optional_argument, nullptr, OptListOutFormatsExt},
{"list-formats-meta", optional_argument, nullptr, OptListMetaFormats},
{"list-formats-meta-out", optional_argument, nullptr, OptListMetaOutFormats},
{"list-subdev-mbus-codes", optional_argument, nullptr, OptListSubDevMBusCodes},
{"list-subdev-framesizes", required_argument, nullptr, OptListSubDevFrameSizes},
{"list-subdev-frameintervals", required_argument, nullptr, OptListSubDevFrameIntervals},
{"list-fields-out", no_argument, nullptr, OptListOutFields},
{"clear-clips", no_argument, nullptr, OptClearClips},
{"clear-bitmap", no_argument, nullptr, OptClearBitmap},
{"add-clip", required_argument, nullptr, OptAddClip},
{"add-bitmap", required_argument, nullptr, OptAddBitmap},
{"find-fb", no_argument, nullptr, OptFindFb},
{"subset", required_argument, nullptr, OptSubset},
{"get-standard", no_argument, nullptr, OptGetStandard},
{"set-standard", required_argument, nullptr, OptSetStandard},
{"get-detected-standard", no_argument, nullptr, OptQueryStandard},
{"get-parm", no_argument, nullptr, OptGetParm},
{"set-parm", required_argument, nullptr, OptSetParm},
{"get-output-parm", no_argument, nullptr, OptGetOutputParm},
{"set-output-parm", required_argument, nullptr, OptSetOutputParm},
{"info", no_argument, nullptr, OptGetDriverInfo},
{"list-ctrls", no_argument, nullptr, OptListCtrls},
{"list-ctrls-menus", no_argument, nullptr, OptListCtrlsMenus},
{"set-ctrl", required_argument, nullptr, OptSetCtrl},
{"get-ctrl", required_argument, nullptr, OptGetCtrl},
{"get-tuner", no_argument, nullptr, OptGetTuner},
{"set-tuner", required_argument, nullptr, OptSetTuner},
{"list-freq-bands", no_argument, nullptr, OptListFreqBands},
{"silent", no_argument, nullptr, OptSilent},
{"verbose", no_argument, nullptr, OptVerbose},
{"log-status", no_argument, nullptr, OptLogStatus},
{"get-fmt-overlay", no_argument, nullptr, OptGetOverlayFormat},
{"set-fmt-overlay", optional_argument, nullptr, OptSetOverlayFormat},
{"try-fmt-overlay", optional_argument, nullptr, OptTryOverlayFormat},
{"get-fmt-sliced-vbi", no_argument, nullptr, OptGetSlicedVbiFormat},
{"set-fmt-sliced-vbi", required_argument, nullptr, OptSetSlicedVbiFormat},
{"try-fmt-sliced-vbi", required_argument, nullptr, OptTrySlicedVbiFormat},
{"get-fmt-sliced-vbi-out", no_argument, nullptr, OptGetSlicedVbiOutFormat},
{"set-fmt-sliced-vbi-out", required_argument, nullptr, OptSetSlicedVbiOutFormat},
{"try-fmt-sliced-vbi-out", required_argument, nullptr, OptTrySlicedVbiOutFormat},
{"get-fmt-vbi", no_argument, nullptr, OptGetVbiFormat},
{"set-fmt-vbi", required_argument, nullptr, OptSetVbiFormat},
{"try-fmt-vbi", required_argument, nullptr, OptTryVbiFormat},
{"get-fmt-vbi-out", no_argument, nullptr, OptGetVbiOutFormat},
{"set-fmt-vbi-out", required_argument, nullptr, OptSetVbiOutFormat},
{"try-fmt-vbi-out", required_argument, nullptr, OptTryVbiOutFormat},
{"get-fmt-sdr", no_argument, nullptr, OptGetSdrFormat},
{"set-fmt-sdr", required_argument, nullptr, OptSetSdrFormat},
{"try-fmt-sdr", required_argument, nullptr, OptTrySdrFormat},
{"get-fmt-sdr-out", no_argument, nullptr, OptGetSdrOutFormat},
{"set-fmt-sdr-out", required_argument, nullptr, OptSetSdrOutFormat},
{"try-fmt-sdr-out", required_argument, nullptr, OptTrySdrOutFormat},
{"get-fmt-meta", no_argument, nullptr, OptGetMetaFormat},
{"set-fmt-meta", required_argument, nullptr, OptSetMetaFormat},
{"try-fmt-meta", required_argument, nullptr, OptTryMetaFormat},
{"get-fmt-meta-out", no_argument, nullptr, OptGetMetaOutFormat},
{"set-fmt-meta-out", required_argument, nullptr, OptSetMetaOutFormat},
{"try-fmt-meta-out", required_argument, nullptr, OptTryMetaOutFormat},
{"get-subdev-fmt", optional_argument, nullptr, OptGetSubDevFormat},
{"set-subdev-fmt", required_argument, nullptr, OptSetSubDevFormat},
{"try-subdev-fmt", required_argument, nullptr, OptTrySubDevFormat},
{"get-sliced-vbi-cap", no_argument, nullptr, OptGetSlicedVbiCap},
{"get-sliced-vbi-out-cap", no_argument, nullptr, OptGetSlicedVbiOutCap},
{"get-fbuf", no_argument, nullptr, OptGetFBuf},
{"set-fbuf", required_argument, nullptr, OptSetFBuf},
{"get-cropcap", no_argument, nullptr, OptGetCropCap},
{"get-crop", no_argument, nullptr, OptGetCrop},
{"set-crop", required_argument, nullptr, OptSetCrop},
{"get-cropcap-output", no_argument, nullptr, OptGetOutputCropCap},
{"get-crop-output", no_argument, nullptr, OptGetOutputCrop},
{"set-crop-output", required_argument, nullptr, OptSetOutputCrop},
{"get-cropcap-overlay", no_argument, nullptr, OptGetOverlayCropCap},
{"get-crop-overlay", no_argument, nullptr, OptGetOverlayCrop},
{"set-crop-overlay", required_argument, nullptr, OptSetOverlayCrop},
{"get-cropcap-output-overlay", no_argument, nullptr, OptGetOutputOverlayCropCap},
{"get-crop-output-overlay", no_argument, nullptr, OptGetOutputOverlayCrop},
{"set-crop-output-overlay", required_argument, nullptr, OptSetOutputOverlayCrop},
{"get-selection", required_argument, nullptr, OptGetSelection},
{"set-selection", required_argument, nullptr, OptSetSelection},
{"get-selection-output", required_argument, nullptr, OptGetOutputSelection},
{"set-selection-output", required_argument, nullptr, OptSetOutputSelection},
{"get-subdev-selection", required_argument, nullptr, OptGetSubDevSelection},
{"set-subdev-selection", required_argument, nullptr, OptSetSubDevSelection},
{"try-subdev-selection", required_argument, nullptr, OptTrySubDevSelection},
{"get-subdev-fps", optional_argument, nullptr, OptGetSubDevFPS},
{"set-subdev-fps", required_argument, nullptr, OptSetSubDevFPS},
{"get-jpeg-comp", no_argument, nullptr, OptGetJpegComp},
{"set-jpeg-comp", required_argument, nullptr, OptSetJpegComp},
{"get-modulator", no_argument, nullptr, OptGetModulator},
{"set-modulator", required_argument, nullptr, OptSetModulator},
{"get-priority", no_argument, nullptr, OptGetPriority},
{"set-priority", required_argument, nullptr, OptSetPriority},
{"wait-for-event", required_argument, nullptr, OptWaitForEvent},
{"poll-for-event", required_argument, nullptr, OptPollForEvent},
{"epoll-for-event", required_argument, nullptr, OptEPollForEvent},
{"overlay", required_argument, nullptr, OptOverlay},
{"sleep", required_argument, nullptr, OptSleep},
{"list-devices", no_argument, nullptr, OptListDevices},
{"list-dv-timings", optional_argument, nullptr, OptListDvTimings},
{"query-dv-timings", no_argument, nullptr, OptQueryDvTimings},
{"get-dv-timings", no_argument, nullptr, OptGetDvTimings},
{"set-dv-bt-timings", required_argument, nullptr, OptSetDvBtTimings},
{"get-dv-timings-cap", optional_argument, nullptr, OptGetDvTimingsCap},
{"freq-seek", required_argument, nullptr, OptFreqSeek},
{"encoder-cmd", required_argument, nullptr, OptEncoderCmd},
{"try-encoder-cmd", required_argument, nullptr, OptTryEncoderCmd},
{"decoder-cmd", required_argument, nullptr, OptDecoderCmd},
{"try-decoder-cmd", required_argument, nullptr, OptTryDecoderCmd},
{"set-edid", required_argument, nullptr, OptSetEdid},
{"clear-edid", optional_argument, nullptr, OptClearEdid},
{"get-edid", optional_argument, nullptr, OptGetEdid},
{"info-edid", optional_argument, nullptr, OptInfoEdid},
{"show-edid", required_argument, nullptr, OptShowEdid},
{"fix-edid-checksums", no_argument, nullptr, OptFixEdidChecksums},
{"tuner-index", required_argument, nullptr, OptTunerIndex},
{"list-buffers", no_argument, nullptr, OptListBuffers},
{"list-buffers-out", no_argument, nullptr, OptListBuffersOut},
{"list-buffers-vbi", no_argument, nullptr, OptListBuffersVbi},
{"list-buffers-sliced-vbi", no_argument, nullptr, OptListBuffersSlicedVbi},
{"list-buffers-vbi-out", no_argument, nullptr, OptListBuffersVbiOut},
{"list-buffers-sliced-vbi-out", no_argument, nullptr, OptListBuffersSlicedVbiOut},
{"list-buffers-sdr", no_argument, nullptr, OptListBuffersSdr},
{"list-buffers-sdr-out", no_argument, nullptr, OptListBuffersSdrOut},
{"list-buffers-meta", no_argument, nullptr, OptListBuffersMeta},
{"list-buffers-meta-out", no_argument, nullptr, OptListBuffersMetaOut},
{"stream-count", required_argument, nullptr, OptStreamCount},
{"stream-skip", required_argument, nullptr, OptStreamSkip},
{"stream-loop", no_argument, nullptr, OptStreamLoop},
{"stream-sleep", required_argument, nullptr, OptStreamSleep},
{"stream-poll", no_argument, nullptr, OptStreamPoll},
{"stream-no-query", no_argument, nullptr, OptStreamNoQuery},
#ifndef NO_STREAM_TO
{"stream-to", required_argument, nullptr, OptStreamTo},
{"stream-to-hdr", required_argument, nullptr, OptStreamToHdr},
{"stream-lossless", no_argument, nullptr, OptStreamLossless},
{"stream-to-host", required_argument, nullptr, OptStreamToHost},
#endif
{"stream-buf-caps", no_argument, nullptr, OptStreamBufCaps},
{"stream-show-delta-now", no_argument, nullptr, OptStreamShowDeltaNow},
{"stream-mmap", optional_argument, nullptr, OptStreamMmap},
{"stream-user", optional_argument, nullptr, OptStreamUser},
{"stream-dmabuf", no_argument, nullptr, OptStreamDmaBuf},
{"stream-from", required_argument, nullptr, OptStreamFrom},
{"stream-from-hdr", required_argument, nullptr, OptStreamFromHdr},
{"stream-from-host", required_argument, nullptr, OptStreamFromHost},
{"stream-out-pattern", required_argument, nullptr, OptStreamOutPattern},
{"stream-out-square", no_argument, nullptr, OptStreamOutSquare},
{"stream-out-border", no_argument, nullptr, OptStreamOutBorder},
{"stream-out-sav", no_argument, nullptr, OptStreamOutInsertSAV},
{"stream-out-eav", no_argument, nullptr, OptStreamOutInsertEAV},
{"stream-out-pixel-aspect", required_argument, nullptr, OptStreamOutPixelAspect},
{"stream-out-video-aspect", required_argument, nullptr, OptStreamOutVideoAspect},
{"stream-out-alpha", required_argument, nullptr, OptStreamOutAlphaComponent},
{"stream-out-alpha-red-only", no_argument, nullptr, OptStreamOutAlphaRedOnly},
{"stream-out-rgb-lim-range", required_argument, nullptr, OptStreamOutRGBLimitedRange},
{"stream-out-hor-speed", required_argument, nullptr, OptStreamOutHorSpeed},
{"stream-out-vert-speed", required_argument, nullptr, OptStreamOutVertSpeed},
{"stream-out-perc-fill", required_argument, nullptr, OptStreamOutPercFill},
{"stream-out-buf-caps", no_argument, nullptr, OptStreamOutBufCaps},
{"stream-out-mmap", optional_argument, nullptr, OptStreamOutMmap},
{"stream-out-user", optional_argument, nullptr, OptStreamOutUser},
{"stream-out-dmabuf", no_argument, nullptr, OptStreamOutDmaBuf},
{"list-patterns", no_argument, nullptr, OptListPatterns},
{"version", no_argument, nullptr, OptVersion},
{nullptr, 0, nullptr, 0}
};
static void usage_all()
{
common_usage();
tuner_usage();
io_usage();
stds_usage();
vidcap_usage();
vidout_usage();
overlay_usage();
vbi_usage();
sdr_usage();
meta_usage();
subdev_usage();
selection_usage();
misc_usage();
streaming_usage();
edid_usage();
}
static void print_version()
{
#define STR(x) #x
#define STRING(x) STR(x)
printf("v4l2-ctl %s%s\n", PACKAGE_VERSION, STRING(GIT_COMMIT_CNT));
}
int test_ioctl(int fd, unsigned long cmd, void *arg)
{
return options[OptUseWrapper] ? v4l2_ioctl(fd, cmd, arg) : ioctl(fd, cmd, arg);
}
int doioctl_name(int fd, unsigned long int request, void *parm, const char *name)
{
int retval = test_ioctl(fd, request, parm);
if (retval < 0)
app_result = -1;
if (options[OptSilent]) return retval;
if (retval < 0)
printf("%s: failed: %s\n", name, strerror(errno));
else if (verbose)
printf("%s: ok\n", name);
return retval;
}
/*
* Any pixelformat that is not a YUV format is assumed to be
* RGB or HSV.
*/
static bool is_rgb_or_hsv(__u32 pixelformat)
{
switch (pixelformat) {
case V4L2_PIX_FMT_UV8:
case V4L2_PIX_FMT_YVU410:
case V4L2_PIX_FMT_YVU420:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_YYUV:
case V4L2_PIX_FMT_YVYU:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_VYUY:
case V4L2_PIX_FMT_YUV422P:
case V4L2_PIX_FMT_YUV411P:
case V4L2_PIX_FMT_Y41P:
case V4L2_PIX_FMT_YUV444:
case V4L2_PIX_FMT_YUV555:
case V4L2_PIX_FMT_YUV565:
case V4L2_PIX_FMT_YUV32:
case V4L2_PIX_FMT_AYUV32:
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
case V4L2_PIX_FMT_YUV410:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_HI240:
case V4L2_PIX_FMT_NV12_16L16:
case V4L2_PIX_FMT_M420:
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
case V4L2_PIX_FMT_NV61:
case V4L2_PIX_FMT_NV24:
case V4L2_PIX_FMT_NV42:
case V4L2_PIX_FMT_NV12M:
case V4L2_PIX_FMT_NV21M:
case V4L2_PIX_FMT_NV16M:
case V4L2_PIX_FMT_NV61M:
case V4L2_PIX_FMT_NV12MT:
case V4L2_PIX_FMT_NV12MT_16X16:
case V4L2_PIX_FMT_YUV420M:
case V4L2_PIX_FMT_YVU420M:
case V4L2_PIX_FMT_YUV422M:
case V4L2_PIX_FMT_YVU422M:
case V4L2_PIX_FMT_YUV444M:
case V4L2_PIX_FMT_YVU444M:
case V4L2_PIX_FMT_SN9C20X_I420:
case V4L2_PIX_FMT_SPCA501:
case V4L2_PIX_FMT_SPCA505:
case V4L2_PIX_FMT_SPCA508:
case V4L2_PIX_FMT_CIT_YYVYUY:
case V4L2_PIX_FMT_KONICA420:
return false;
default:
return true;
}
}
static std::string printfmtname(int fd, __u32 type, __u32 pixfmt)
{
struct v4l2_fmtdesc fmt = {};
std::string s(" (");
fmt.index = 0;
fmt.type = type;
while (test_ioctl(fd, VIDIOC_ENUM_FMT, &fmt) >= 0) {
if (fmt.pixelformat == pixfmt)
return s + reinterpret_cast<const char *>(fmt.description) + ")";
fmt.index++;
}
return "";
}
void printfmt(int fd, const struct v4l2_format &vfmt)
{
__u32 colsp = vfmt.fmt.pix.colorspace;
__u32 ycbcr_enc = vfmt.fmt.pix.ycbcr_enc;
printf("Format %s:\n", buftype2s(vfmt.type).c_str());
switch (vfmt.type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix.width, vfmt.fmt.pix.height);
printf("\tPixel Format : '%s'%s\n", fcc2s(vfmt.fmt.pix.pixelformat).c_str(),
printfmtname(fd, vfmt.type, vfmt.fmt.pix.pixelformat).c_str());
printf("\tField : %s\n", field2s(vfmt.fmt.pix.field).c_str());
printf("\tBytes per Line : %u\n", vfmt.fmt.pix.bytesperline);
printf("\tSize Image : %u\n", vfmt.fmt.pix.sizeimage);
printf("\tColorspace : %s\n", colorspace2s(colsp).c_str());
printf("\tTransfer Function : %s", xfer_func2s(vfmt.fmt.pix.xfer_func).c_str());
if (vfmt.fmt.pix.xfer_func == V4L2_XFER_FUNC_DEFAULT)
printf(" (maps to %s)",
xfer_func2s(V4L2_MAP_XFER_FUNC_DEFAULT(colsp)).c_str());
printf("\n");
printf("\tYCbCr/HSV Encoding: %s", ycbcr_enc2s(ycbcr_enc).c_str());
if (ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) {
ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(colsp);
printf(" (maps to %s)", ycbcr_enc2s(ycbcr_enc).c_str());
}
printf("\n");
printf("\tQuantization : %s", quantization2s(vfmt.fmt.pix.quantization).c_str());
if (vfmt.fmt.pix.quantization == V4L2_QUANTIZATION_DEFAULT)
printf(" (maps to %s)",
quantization2s(V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb_or_hsv(vfmt.fmt.pix.pixelformat),
colsp, ycbcr_enc)).c_str());
printf("\n");
if (vfmt.fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC)
printf("\tFlags : %s\n", pixflags2s(vfmt.fmt.pix.flags).c_str());
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
printf("\tWidth/Height : %u/%u\n", vfmt.fmt.pix_mp.width, vfmt.fmt.pix_mp.height);
printf("\tPixel Format : '%s'%s\n", fcc2s(vfmt.fmt.pix_mp.pixelformat).c_str(),
printfmtname(fd, vfmt.type, vfmt.fmt.pix_mp.pixelformat).c_str());
printf("\tField : %s\n", field2s(vfmt.fmt.pix_mp.field).c_str());
printf("\tNumber of planes : %u\n", vfmt.fmt.pix_mp.num_planes);
printf("\tFlags : %s\n", pixflags2s(vfmt.fmt.pix_mp.flags).c_str());
printf("\tColorspace : %s\n", colorspace2s(vfmt.fmt.pix_mp.colorspace).c_str());
printf("\tTransfer Function : %s\n", xfer_func2s(vfmt.fmt.pix_mp.xfer_func).c_str());
printf("\tYCbCr/HSV Encoding: %s\n", ycbcr_enc2s(vfmt.fmt.pix_mp.ycbcr_enc).c_str());
printf("\tQuantization : %s\n", quantization2s(vfmt.fmt.pix_mp.quantization).c_str());
for (int i = 0; i < vfmt.fmt.pix_mp.num_planes && i < VIDEO_MAX_PLANES; i++) {
printf("\tPlane %d :\n", i);
printf("\t Bytes per Line : %u\n", vfmt.fmt.pix_mp.plane_fmt[i].bytesperline);
printf("\t Size Image : %u\n", vfmt.fmt.pix_mp.plane_fmt[i].sizeimage);
}
break;
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
printf("\tLeft/Top : %d/%d\n",
vfmt.fmt.win.w.left, vfmt.fmt.win.w.top);
printf("\tWidth/Height: %d/%d\n",
vfmt.fmt.win.w.width, vfmt.fmt.win.w.height);
printf("\tField : %s\n", field2s(vfmt.fmt.win.field).c_str());
printf("\tChroma Key : 0x%08x\n", vfmt.fmt.win.chromakey);
printf("\tGlobal Alpha: 0x%02x\n", vfmt.fmt.win.global_alpha);
printf("\tClip Count : %u\n", vfmt.fmt.win.clipcount);
if (vfmt.fmt.win.clips)
for (unsigned i = 0; i < vfmt.fmt.win.clipcount; i++) {
struct v4l2_rect &r = vfmt.fmt.win.clips[i].c;
printf("\t\tClip %2d: %ux%u@%ux%u\n", i,
r.width, r.height, r.left, r.top);
}
printf("\tClip Bitmap : %s", vfmt.fmt.win.bitmap ? "Yes, " : "No\n");
if (vfmt.fmt.win.bitmap) {
auto bitmap = static_cast<unsigned char *>(vfmt.fmt.win.bitmap);
unsigned stride = (vfmt.fmt.win.w.width + 7) / 8;
unsigned cnt = 0;
for (unsigned y = 0; y < vfmt.fmt.win.w.height; y++)
for (unsigned x = 0; x < vfmt.fmt.win.w.width; x++)
if (bitmap[y * stride + x / 8] & (1 << (x & 7)))
cnt++;
printf("%u bits of %u are set\n", cnt,
vfmt.fmt.win.w.width * vfmt.fmt.win.w.height);
}
break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
case V4L2_BUF_TYPE_VBI_OUTPUT:
printf("\tSampling Rate : %u Hz\n", vfmt.fmt.vbi.sampling_rate);
printf("\tOffset : %u samples (%g secs after leading edge)\n",
vfmt.fmt.vbi.offset,
static_cast<double>(vfmt.fmt.vbi.offset) / static_cast<double>(vfmt.fmt.vbi.sampling_rate));
printf("\tSamples per Line: %u\n", vfmt.fmt.vbi.samples_per_line);
printf("\tSample Format : '%s'\n", fcc2s(vfmt.fmt.vbi.sample_format).c_str());
printf("\tStart 1st Field : %u\n", vfmt.fmt.vbi.start[0]);
printf("\tCount 1st Field : %u\n", vfmt.fmt.vbi.count[0]);
printf("\tStart 2nd Field : %u\n", vfmt.fmt.vbi.start[1]);
printf("\tCount 2nd Field : %u\n", vfmt.fmt.vbi.count[1]);
if (vfmt.fmt.vbi.flags)
printf("\tFlags : %s\n", vbiflags2s(vfmt.fmt.vbi.flags).c_str());
break;
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
printf("\tService Set : %s\n",
service2s(vfmt.fmt.sliced.service_set).c_str());
for (int i = 0; i < 24; i++) {
printf("\tService Line %2d: %8s / %-8s\n", i,
service2s(vfmt.fmt.sliced.service_lines[0][i]).c_str(),
service2s(vfmt.fmt.sliced.service_lines[1][i]).c_str());
}
printf("\tI/O Size : %u\n", vfmt.fmt.sliced.io_size);
break;
case V4L2_BUF_TYPE_SDR_CAPTURE:
case V4L2_BUF_TYPE_SDR_OUTPUT:
printf("\tSample Format : '%s'%s\n", fcc2s(vfmt.fmt.sdr.pixelformat).c_str(),
printfmtname(fd, vfmt.type, vfmt.fmt.sdr.pixelformat).c_str());
printf("\tBuffer Size : %u\n", vfmt.fmt.sdr.buffersize);
break;
case V4L2_BUF_TYPE_META_CAPTURE:
case V4L2_BUF_TYPE_META_OUTPUT:
printf("\tSample Format : '%s'%s\n", fcc2s(vfmt.fmt.meta.dataformat).c_str(),
printfmtname(fd, vfmt.type, vfmt.fmt.meta.dataformat).c_str());
printf("\tBuffer Size : %u\n", vfmt.fmt.meta.buffersize);
break;
}
}
static std::string frmtype2s(unsigned type)
{
static constexpr const char *types[] = {
"Unknown",
"Discrete",
"Continuous",
"Stepwise"
};
if (type > 3)
type = 0;
return types[type];
}
static std::string fract2sec(const struct v4l2_fract &f)
{
char buf[100];
sprintf(buf, "%.3f", (1.0 * f.numerator) / f.denominator);
return buf;
}
static std::string fract2fps(const struct v4l2_fract &f)
{
char buf[100];
sprintf(buf, "%.3f", (1.0 * f.denominator) / f.numerator);
return buf;
}
void print_frmsize(const struct v4l2_frmsizeenum &frmsize, const char *prefix)
{
printf("%s\tSize: %s ", prefix, frmtype2s(frmsize.type).c_str());
if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
printf("%dx%d", frmsize.discrete.width, frmsize.discrete.height);
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS) {
printf("%dx%d - %dx%d",
frmsize.stepwise.min_width,
frmsize.stepwise.min_height,
frmsize.stepwise.max_width,
frmsize.stepwise.max_height);
} else if (frmsize.type == V4L2_FRMSIZE_TYPE_STEPWISE) {
printf("%dx%d - %dx%d with step %d/%d",
frmsize.stepwise.min_width,
frmsize.stepwise.min_height,
frmsize.stepwise.max_width,
frmsize.stepwise.max_height,
frmsize.stepwise.step_width,
frmsize.stepwise.step_height);
}
printf("\n");
}
void print_frmival(const struct v4l2_frmivalenum &frmival, const char *prefix)
{
printf("%s\tInterval: %s ", prefix, frmtype2s(frmival.type).c_str());
if (frmival.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
printf("%ss (%s fps)\n", fract2sec(frmival.discrete).c_str(),
fract2fps(frmival.discrete).c_str());
} else if (frmival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS) {
printf("%ss - %ss (%s-%s fps)\n",
fract2sec(frmival.stepwise.min).c_str(),
fract2sec(frmival.stepwise.max).c_str(),
fract2fps(frmival.stepwise.max).c_str(),
fract2fps(frmival.stepwise.min).c_str());
} else if (frmival.type == V4L2_FRMIVAL_TYPE_STEPWISE) {
printf("%ss - %ss with step %ss (%s-%s fps)\n",
fract2sec(frmival.stepwise.min).c_str(),
fract2sec(frmival.stepwise.max).c_str(),
fract2sec(frmival.stepwise.step).c_str(),
fract2fps(frmival.stepwise.max).c_str(),
fract2fps(frmival.stepwise.min).c_str());
}
}
void print_video_formats(cv4l_fd &fd, __u32 type, unsigned int mbus_code)
{
cv4l_disable_trace dt(fd);
struct v4l2_fmtdesc fmt = {};
if (mbus_code && !(capabilities & V4L2_CAP_IO_MC))
mbus_code = 0;
printf("\tType: %s\n\n", buftype2s(type).c_str());
if (fd.enum_fmt(fmt, true, 0, type, mbus_code))
return;
do {
printf("\t[%d]: '%s' (%s", fmt.index, fcc2s(fmt.pixelformat).c_str(),
fmt.description);
if (fmt.flags) {
bool is_hsv = fmt.pixelformat == V4L2_PIX_FMT_HSV24 ||
fmt.pixelformat == V4L2_PIX_FMT_HSV32;
printf(", %s", fmtdesc2s(fmt.flags, is_hsv).c_str());
}
printf(")\n");
} while (!fd.enum_fmt(fmt));
}
void print_video_formats_ext(cv4l_fd &fd, __u32 type, unsigned int mbus_code)
{
cv4l_disable_trace dt(fd);
struct v4l2_fmtdesc fmt = {};
struct v4l2_frmsizeenum frmsize;
struct v4l2_frmivalenum frmival;
if (mbus_code && !(capabilities & V4L2_CAP_IO_MC))
mbus_code = 0;
printf("\tType: %s\n\n", buftype2s(type).c_str());
if (fd.enum_fmt(fmt, true, 0, type, mbus_code))
return;
do {
printf("\t[%d]: '%s' (%s", fmt.index, fcc2s(fmt.pixelformat).c_str(),
fmt.description);
if (fmt.flags) {
bool is_hsv = fmt.pixelformat == V4L2_PIX_FMT_HSV24 ||
fmt.pixelformat == V4L2_PIX_FMT_HSV32;
printf(", %s", fmtdesc2s(fmt.flags, is_hsv).c_str());
}
printf(")\n");
if (fd.enum_framesizes(frmsize, fmt.pixelformat))
continue;
do {
print_frmsize(frmsize, "\t");
if (frmsize.type != V4L2_FRMSIZE_TYPE_DISCRETE)
continue;
if (fd.enum_frameintervals(frmival, fmt.pixelformat,
frmsize.discrete.width,
frmsize.discrete.height))
continue;
do {
print_frmival(frmival, "\t\t");
} while (!fd.enum_frameintervals(frmival));
} while (!fd.enum_framesizes(frmsize));
} while (!fd.enum_fmt(fmt));
}
int parse_subopt(char **subs, const char * const *subopts, char **value)
{
int opt = v4l_getsubopt(subs, const_cast<char * const *>(subopts), value);
if (opt == -1) {
fprintf(stderr, "Invalid suboptions specified\n");
return -1;
}
if (*value == nullptr) {
fprintf(stderr, "No value given to suboption <%s>\n",
subopts[opt]);
return -1;
}
return opt;
}
__u32 parse_field(const char *s)
{
if (!strcmp(s, "any")) return V4L2_FIELD_ANY;
if (!strcmp(s, "none")) return V4L2_FIELD_NONE;
if (!strcmp(s, "top")) return V4L2_FIELD_TOP;
if (!strcmp(s, "bottom")) return V4L2_FIELD_BOTTOM;
if (!strcmp(s, "interlaced")) return V4L2_FIELD_INTERLACED;
if (!strcmp(s, "seq_tb")) return V4L2_FIELD_SEQ_TB;
if (!strcmp(s, "seq_bt")) return V4L2_FIELD_SEQ_BT;
if (!strcmp(s, "alternate")) return V4L2_FIELD_ALTERNATE;
if (!strcmp(s, "interlaced_tb")) return V4L2_FIELD_INTERLACED_TB;
if (!strcmp(s, "interlaced_bt")) return V4L2_FIELD_INTERLACED_BT;
return V4L2_FIELD_ANY;
}
__u32 parse_colorspace(const char *s)
{
if (!strcmp(s, "smpte170m")) return V4L2_COLORSPACE_SMPTE170M;
if (!strcmp(s, "smpte240m")) return V4L2_COLORSPACE_SMPTE240M;
if (!strcmp(s, "rec709")) return V4L2_COLORSPACE_REC709;
if (!strcmp(s, "470m")) return V4L2_COLORSPACE_470_SYSTEM_M;
if (!strcmp(s, "470bg")) return V4L2_COLORSPACE_470_SYSTEM_BG;
if (!strcmp(s, "jpeg")) return V4L2_COLORSPACE_JPEG;
if (!strcmp(s, "srgb")) return V4L2_COLORSPACE_SRGB;
if (!strcmp(s, "oprgb")) return V4L2_COLORSPACE_OPRGB;
if (!strcmp(s, "bt2020")) return V4L2_COLORSPACE_BT2020;
if (!strcmp(s, "dcip3")) return V4L2_COLORSPACE_DCI_P3;
return 0;
}
__u32 parse_xfer_func(const char *s)
{
if (!strcmp(s, "default")) return V4L2_XFER_FUNC_DEFAULT;
if (!strcmp(s, "smpte240m")) return V4L2_XFER_FUNC_SMPTE240M;
if (!strcmp(s, "rec709")) return V4L2_XFER_FUNC_709;
if (!strcmp(s, "srgb")) return V4L2_XFER_FUNC_SRGB;
if (!strcmp(s, "oprgb")) return V4L2_XFER_FUNC_OPRGB;
if (!strcmp(s, "dcip3")) return V4L2_XFER_FUNC_DCI_P3;
if (!strcmp(s, "smpte2084")) return V4L2_XFER_FUNC_SMPTE2084;
if (!strcmp(s, "none")) return V4L2_XFER_FUNC_NONE;
return 0;
}
__u32 parse_ycbcr(const char *s)
{
if (!strcmp(s, "default")) return V4L2_YCBCR_ENC_DEFAULT;
if (!strcmp(s, "601")) return V4L2_YCBCR_ENC_601;
if (!strcmp(s, "709")) return V4L2_YCBCR_ENC_709;
if (!strcmp(s, "xv601")) return V4L2_YCBCR_ENC_XV601;
if (!strcmp(s, "xv709")) return V4L2_YCBCR_ENC_XV709;
if (!strcmp(s, "bt2020")) return V4L2_YCBCR_ENC_BT2020;
if (!strcmp(s, "bt2020c")) return V4L2_YCBCR_ENC_BT2020_CONST_LUM;
if (!strcmp(s, "smpte240m")) return V4L2_YCBCR_ENC_SMPTE240M;
return V4L2_YCBCR_ENC_DEFAULT;
}
__u32 parse_hsv(const char *s)
{
if (!strcmp(s, "default")) return V4L2_YCBCR_ENC_DEFAULT;
if (!strcmp(s, "180")) return V4L2_HSV_ENC_180;
if (!strcmp(s, "256")) return V4L2_HSV_ENC_256;
return V4L2_YCBCR_ENC_DEFAULT;
}
__u32 parse_quantization(const char *s)
{
if (!strcmp(s, "default")) return V4L2_QUANTIZATION_DEFAULT;
if (!strcmp(s, "full-range")) return V4L2_QUANTIZATION_FULL_RANGE;
if (!strcmp(s, "lim-range")) return V4L2_QUANTIZATION_LIM_RANGE;
return V4L2_QUANTIZATION_DEFAULT;
}
int parse_fmt(char *optarg, __u32 &width, __u32 &height, __u32 &pixelformat,
__u32 &field, __u32 &colorspace, __u32 &xfer_func, __u32 &ycbcr,
__u32 &quantization, __u32 &flags, __u32 *bytesperline,
__u32 *sizeimage)
{
char *value, *subs;
int fmts = 0;
unsigned bpl_index = 0;
unsigned sizeimage_index = 0;
bool be_pixfmt;
field = V4L2_FIELD_ANY;
flags = 0;
subs = optarg;
while (*subs != '\0') {
static constexpr const char *subopts[] = {
"width",
"height",
"pixelformat",
"field",
"colorspace",
"ycbcr",
"hsv",
"bytesperline",
"premul-alpha",
"quantization",
"xfer",
"sizeimage",
nullptr
};
switch (parse_subopt(&subs, subopts, &value)) {
case 0:
width = strtoul(value, nullptr, 0);
fmts |= FmtWidth;
break;
case 1:
height = strtoul(value, nullptr, 0);
fmts |= FmtHeight;
break;
case 2:
be_pixfmt = strlen(value) == 7 && !memcmp(value + 4, "-BE", 3);
if (be_pixfmt || strlen(value) == 4) {
pixelformat =
v4l2_fourcc(value[0], value[1],
value[2], value[3]);
if (be_pixfmt)
pixelformat |= 1U << 31;
} else if (isdigit(value[0])) {
pixelformat = strtol(value, nullptr, 0);
} else {
fprintf(stderr, "The pixelformat '%s' is invalid\n", value);
std::exit(EXIT_FAILURE);
}
fmts |= FmtPixelFormat;
break;
case 3:
field = parse_field(value);
fmts |= FmtField;
break;
case 4:
colorspace = parse_colorspace(value);
if (colorspace)
fmts |= FmtColorspace;
else
fprintf(stderr, "unknown colorspace %s\n", value);
break;
case 5:
ycbcr = parse_ycbcr(value);
fmts |= FmtYCbCr;
break;
case 6:
ycbcr = parse_hsv(value);
fmts |= FmtYCbCr;
break;
case 7:
bytesperline[bpl_index] = strtoul(value, nullptr, 0);
if (bytesperline[bpl_index] > 0xffff) {
fprintf(stderr, "bytesperline can't be more than 65535\n");
bytesperline[bpl_index] = 0;
}
bpl_index++;
fmts |= FmtBytesPerLine;
break;
case 8:
flags |= V4L2_PIX_FMT_FLAG_PREMUL_ALPHA;
fmts |= FmtFlags;
break;
case 9:
quantization = parse_quantization(value);
fmts |= FmtQuantization;
break;
case 10:
xfer_func = parse_xfer_func(value);
fmts |= FmtXferFunc;
break;
case 11:
sizeimage[sizeimage_index] = strtoul(value, nullptr, 0);
sizeimage_index++;
fmts |= FmtSizeImage;
break;
default:
return 0;
}
}
return fmts;
}
int parse_selection_flags(const char *s)
{
if (!strcmp(s, "le")) return V4L2_SEL_FLAG_LE;
if (!strcmp(s, "ge")) return V4L2_SEL_FLAG_GE;
if (!strcmp(s, "keep-config")) return V4L2_SEL_FLAG_KEEP_CONFIG;
return 0;
}
void print_selection(const struct v4l2_selection &sel)
{
printf("Selection %s: %s, Left %d, Top %d, Width %d, Height %d, Flags: %s\n",
buftype2s(sel.type).c_str(), seltarget2s(sel.target).c_str(),
sel.r.left, sel.r.top, sel.r.width, sel.r.height,
selflags2s(sel.flags).c_str());
}
int parse_selection_target(const char *s, unsigned int &target)
{
if (!strcmp(s, "crop")) target = V4L2_SEL_TGT_CROP_ACTIVE;
else if (!strcmp(s, "crop_default")) target = V4L2_SEL_TGT_CROP_DEFAULT;
else if (!strcmp(s, "crop_bounds")) target = V4L2_SEL_TGT_CROP_BOUNDS;
else if (!strcmp(s, "compose")) target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
else if (!strcmp(s, "compose_default")) target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
else if (!strcmp(s, "compose_bounds")) target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
else if (!strcmp(s, "compose_padded")) target = V4L2_SEL_TGT_COMPOSE_PADDED;
else if (!strcmp(s, "native_size")) target = V4L2_SEL_TGT_NATIVE_SIZE;
else return -EINVAL;
return 0;
}
static void print_event(const struct v4l2_event *ev)
{
printf("%lld.%06ld: event %u, pending %u: ",
static_cast<__u64>(ev->timestamp.tv_sec), ev->timestamp.tv_nsec / 1000,
ev->sequence, ev->pending);
switch (ev->type) {
case V4L2_EVENT_VSYNC:
printf("vsync %s\n", field2s(ev->u.vsync.field).c_str());
break;
case V4L2_EVENT_EOS:
printf("eos\n");
break;
case V4L2_EVENT_CTRL:
common_control_event(ev);
break;
case V4L2_EVENT_FRAME_SYNC:
printf("frame_sync %d\n", ev->u.frame_sync.frame_sequence);
break;
case V4L2_EVENT_SOURCE_CHANGE:
printf("source_change: pad/input=%d changes: %x\n", ev->id, ev->u.src_change.changes);
break;
case V4L2_EVENT_MOTION_DET:
if (ev->u.motion_det.flags & V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ)
printf("motion_det frame %d, regions 0x%x\n",
ev->u.motion_det.frame_sequence,
ev->u.motion_det.region_mask);
else
printf("motion_det regions 0x%x\n", ev->u.motion_det.region_mask);
break;
default:
if (ev->type >= V4L2_EVENT_PRIVATE_START)
printf("unknown private event (%08x)\n", ev->type);
else
printf("unknown event (%08x)\n", ev->type);
break;
}
}
static __u32 parse_event(const char *e, const char **name)
{
__u32 event = 0;
*name = "0";
if (isdigit(e[0])) {
event = strtoul(e, nullptr, 0);
if (event == V4L2_EVENT_CTRL) {
fprintf(stderr, "Missing control name for ctrl event, use ctrl=<name>\n");
misc_usage();
std::exit(EXIT_FAILURE);
}
} else if (!strcmp(e, "eos")) {
event = V4L2_EVENT_EOS;
} else if (!strcmp(e, "vsync")) {
event = V4L2_EVENT_VSYNC;
} else if (!strcmp(e, "frame_sync")) {
event = V4L2_EVENT_FRAME_SYNC;
} else if (!strcmp(e, "motion_det")) {
event = V4L2_EVENT_MOTION_DET;
} else if (!strncmp(e, "ctrl=", 5)) {
event = V4L2_EVENT_CTRL;
*name = e + 5;
} else if (!strncmp(e, "source_change=", 14)) {
event = V4L2_EVENT_SOURCE_CHANGE;
*name = e + 14;
} else if (!strcmp(e, "source_change")) {
event = V4L2_EVENT_SOURCE_CHANGE;
}
if (event == 0) {
fprintf(stderr, "Unknown event\n");
misc_usage();
std::exit(EXIT_FAILURE);
}
return event;
}
bool valid_pixel_format(int fd, __u32 pixelformat, bool output, bool mplane)
{
struct v4l2_fmtdesc fmt = {};
if (output)
fmt.type = mplane ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT;
else
fmt.type = mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_CAPTURE;
while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmt)) {
if (fmt.pixelformat == pixelformat)
return true;
fmt.index++;
}
return false;
}
__u32 find_pixel_format(int fd, unsigned index, bool output, bool mplane)
{
struct v4l2_fmtdesc fmt = {};
fmt.index = index;
if (output)
fmt.type = mplane ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT;
else
fmt.type = mplane ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(fd, VIDIOC_ENUM_FMT, &fmt))
return 0;
return fmt.pixelformat;
}
static int open_media_bus_info(const std::string &bus_info)
{
DIR *dp;
struct dirent *ep;
dp = opendir("/dev");
if (dp == nullptr)
return -1;
while ((ep = readdir(dp))) {
const char *name = ep->d_name;
if (!memcmp(name, "media", 5) && isdigit(name[5])) {
struct media_device_info mdi;
std::string devname = std::string("/dev/") + name;
int fd = open(devname.c_str(), O_RDWR);
if (fd < 0)
continue;
if (!ioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi) &&
bus_info == mdi.bus_info) {
closedir(dp);
return fd;
}
close(fd);
}
}
closedir(dp);
return -1;
}
static const char *make_devname(const char *device, const char *devname,
const std::string &media_bus_info)
{
if (device[0] >= '0' && device[0] <= '9' && strlen(device) <= 3) {
static char newdev[32];
sprintf(newdev, "/dev/%s%s", devname, device);
return newdev;
}
if (media_bus_info.empty())
return device;
int media_fd = open_media_bus_info(media_bus_info);
if (media_fd < 0)
return device;
media_v2_topology topology;
memset(&topology, 0, sizeof(topology));
if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology)) {
close(media_fd);
return device;
}
auto ents = new media_v2_entity[topology.num_entities];
topology.ptr_entities = (uintptr_t)ents;
auto links = new media_v2_link[topology.num_links];
topology.ptr_links = (uintptr_t)links;
auto ifaces = new media_v2_interface[topology.num_interfaces];
topology.ptr_interfaces = (uintptr_t)ifaces;
unsigned i, ent_id, iface_id = 0;
if (ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology))
goto err;
if (device[0] == '0' && device[1] == 'x')
iface_id = strtoul(device, nullptr, 16);
if (!iface_id) {
for (i = 0; i < topology.num_entities; i++)
if (!strcmp(ents[i].name, device))
break;
if (i >= topology.num_entities)
goto err;
ent_id = ents[i].id;
for (i = 0; i < topology.num_links; i++)
if (links[i].sink_id == ent_id &&
(links[i].flags & MEDIA_LNK_FL_LINK_TYPE) ==
MEDIA_LNK_FL_INTERFACE_LINK)
break;
if (i >= topology.num_links)
goto err;
iface_id = links[i].source_id;
}
for (i = 0; i < topology.num_interfaces; i++)
if (ifaces[i].id == iface_id)
break;
if (i >= topology.num_interfaces)
goto err;
static char newdev[32];
sprintf(newdev, "/dev/char/%d:%d",
ifaces[i].devnode.major, ifaces[i].devnode.minor);
device = newdev;
err:
delete [] ents;
delete [] links;
delete [] ifaces;
close(media_fd);
return device;
}
int main(int argc, char **argv)
{
int i;
cv4l_fd c_fd;
cv4l_fd c_out_fd;
cv4l_fd c_exp_fd;
int fd = -1;
int out_fd = -1;
int exp_fd = -1;
int media_fd = -1;
bool is_subdev = false;
std::string media_bus_info;
/* command args */
int ch;
const char *device = "/dev/video0"; /* -d device */
const char *out_device = nullptr;
const char *export_device = nullptr;
struct v4l2_capability vcap = {};
struct v4l2_subdev_capability subdevcap = {};
__u32 wait_for_event = 0; /* wait for this event */
const char *wait_event_id = nullptr;
__u32 poll_for_event = 0; /* poll for this event */
const char *poll_event_id = nullptr;
__u32 epoll_for_event = 0; /* epoll for this event */
const char *epoll_event_id = nullptr;
unsigned secs = 0;
char short_options[26 * 2 * 3 + 1];
int idx = 0;
if (argc == 1) {
common_usage();
return 0;
}
for (i = 0; long_options[i].name; i++) {
if (!isalpha(long_options[i].val))
continue;
short_options[idx++] = long_options[i].val;
if (long_options[i].has_arg == required_argument) {
short_options[idx++] = ':';
} else if (long_options[i].has_arg == optional_argument) {
short_options[idx++] = ':';
short_options[idx++] = ':';
}
}
while (true) {
int option_index = 0;
short_options[idx] = 0;
ch = getopt_long(argc, argv, short_options,
long_options, &option_index);
if (ch == -1)
break;
options[ch] = 1;
if (!option_index) {
for (i = 0; long_options[i].val; i++) {
if (long_options[i].val == ch) {
option_index = i;
break;
}
}
}
if (long_options[option_index].has_arg == optional_argument &&
!optarg && argv[optind] && argv[optind][0] != '-')
optarg = argv[optind++];
switch (ch) {
case OptHelp:
common_usage();
return 0;
case OptHelpTuner:
tuner_usage();
return 0;
case OptHelpIO:
io_usage();
return 0;
case OptHelpStds:
stds_usage();
return 0;
case OptHelpVidCap:
vidcap_usage();
return 0;
case OptHelpVidOut:
vidout_usage();
return 0;
case OptHelpOverlay:
overlay_usage();
return 0;
case OptHelpVbi:
vbi_usage();
return 0;
case OptHelpSdr:
sdr_usage();
return 0;
case OptHelpMeta:
meta_usage();
return 0;
case OptHelpSubDev:
subdev_usage();
return 0;
case OptHelpSelection:
selection_usage();
return 0;
case OptHelpMisc:
misc_usage();
return 0;
case OptHelpStreaming:
streaming_usage();
return 0;
case OptHelpEdid:
edid_usage();
return 0;
case OptHelpAll:
usage_all();
return 0;
case OptSetDevice:
device = make_devname(optarg, "video", media_bus_info);
break;
case OptSetOutDevice:
out_device = make_devname(optarg, "video", media_bus_info);
break;
case OptSetExportDevice:
export_device = make_devname(optarg, "video", media_bus_info);
break;
case OptMediaBusInfo:
media_bus_info = optarg;
break;
case OptWaitForEvent:
wait_for_event = parse_event(optarg, &wait_event_id);
if (wait_for_event == 0)
return 1;
break;
case OptPollForEvent:
poll_for_event = parse_event(optarg, &poll_event_id);
if (poll_for_event == 0)
return 1;
break;
case OptEPollForEvent:
epoll_for_event = parse_event(optarg, &epoll_event_id);
if (epoll_for_event == 0)
return 1;
break;
case OptSleep:
secs = strtoul(optarg, nullptr, 0);
break;
case OptVersion:
print_version();
return 0;
case ':':
fprintf(stderr, "Option '%s' requires a value\n",
argv[optind]);
common_usage();
return 1;
case '?':
if (argv[optind])
fprintf(stderr, "Unknown argument '%s'\n", argv[optind]);
common_usage();
return 1;
default:
common_cmd(media_bus_info, ch, optarg);
tuner_cmd(ch, optarg);
io_cmd(ch, optarg);
stds_cmd(ch, optarg);
vidcap_cmd(ch, optarg);
vidout_cmd(ch, optarg);
overlay_cmd(ch, optarg);
vbi_cmd(ch, optarg);
sdr_cmd(ch, optarg);
meta_cmd(ch, optarg);
subdev_cmd(ch, optarg);
selection_cmd(ch, optarg);
misc_cmd(ch, optarg);
streaming_cmd(ch, optarg);
edid_cmd(ch, optarg);
break;
}
}
if (optind < argc) {
printf("unknown arguments: ");
while (optind < argc)
printf("%s ", argv[optind++]);
printf("\n");
common_usage();
return 1;
}
media_type type = mi_media_detect_type(device);
if (type == MEDIA_TYPE_CANT_STAT) {
fprintf(stderr, "Cannot open device %s, exiting.\n",
device);
std::exit(EXIT_FAILURE);
}
switch (type) {
// For now we can only handle V4L2 devices
case MEDIA_TYPE_VIDEO:
case MEDIA_TYPE_VBI:
case MEDIA_TYPE_RADIO:
case MEDIA_TYPE_SDR:
case MEDIA_TYPE_TOUCH:
case MEDIA_TYPE_SUBDEV:
break;
default:
type = MEDIA_TYPE_UNKNOWN;
break;
}
if (type == MEDIA_TYPE_UNKNOWN) {
fprintf(stderr, "Unable to detect what device %s is, exiting.\n",
device);
std::exit(EXIT_FAILURE);
}
is_subdev = type == MEDIA_TYPE_SUBDEV;
if (is_subdev)
options[OptUseWrapper] = 0;
c_fd.s_direct(!options[OptUseWrapper]);
c_out_fd.s_direct(!options[OptUseWrapper]);
c_exp_fd.s_direct(!options[OptUseWrapper]);
if (is_subdev)
fd = c_fd.subdev_open(device);
else
fd = c_fd.open(device);
if (fd < 0) {
fprintf(stderr, "Failed to open %s: %s\n", device,
strerror(errno));
std::exit(EXIT_FAILURE);
}
verbose = options[OptVerbose];
c_fd.s_trace(options[OptSilent] ? 0 : (verbose ? 2 : 1));
if (!is_subdev && doioctl(fd, VIDIOC_QUERYCAP, &vcap)) {
fprintf(stderr, "%s: not a v4l2 node\n", device);
std::exit(EXIT_FAILURE);
} else if (is_subdev) {
// This ioctl was introduced in kernel 5.10, so don't
// exit if this ioctl returns an error.
doioctl(fd, VIDIOC_SUBDEV_QUERYCAP, &subdevcap);
}
if (!is_subdev) {
capabilities = vcap.capabilities;
if (capabilities & V4L2_CAP_DEVICE_CAPS)
capabilities = vcap.device_caps;
}
media_fd = mi_get_media_fd(fd, is_subdev ? 0 : (const char *)vcap.bus_info);
priv_magic = (capabilities & V4L2_CAP_EXT_PIX_FORMAT) ?
V4L2_PIX_FMT_PRIV_MAGIC : 0;
is_multiplanar = capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE |
V4L2_CAP_VIDEO_M2M_MPLANE |
V4L2_CAP_VIDEO_OUTPUT_MPLANE);
vidcap_buftype = is_multiplanar ? V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE :
V4L2_BUF_TYPE_VIDEO_CAPTURE;
vidout_buftype = is_multiplanar ? V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE :
V4L2_BUF_TYPE_VIDEO_OUTPUT;
if (out_device) {
out_fd = c_out_fd.open(out_device);
if (out_fd < 0) {
fprintf(stderr, "Failed to open %s: %s\n", out_device,
strerror(errno));
std::exit(EXIT_FAILURE);
}
c_out_fd.s_trace(options[OptSilent] ? 0 : (verbose ? 2 : 1));
if (doioctl(out_fd, VIDIOC_QUERYCAP, &vcap)) {
fprintf(stderr, "%s: not a v4l2 node\n", out_device);
std::exit(EXIT_FAILURE);
}
out_capabilities = vcap.capabilities;
if (out_capabilities & V4L2_CAP_DEVICE_CAPS)
out_capabilities = vcap.device_caps;
out_priv_magic = (out_capabilities & V4L2_CAP_EXT_PIX_FORMAT) ?
V4L2_PIX_FMT_PRIV_MAGIC : 0;
}
if (export_device) {
exp_fd = c_exp_fd.open(export_device);
if (exp_fd < 0) {
fprintf(stderr, "Failed to open %s: %s\n", export_device,
strerror(errno));
std::exit(EXIT_FAILURE);
}
c_exp_fd.s_trace(options[OptSilent] ? 0 : (verbose ? 2 : 1));
if (doioctl(exp_fd, VIDIOC_QUERYCAP, &vcap)) {
fprintf(stderr, "%s: not a v4l2 node\n", export_device);
std::exit(EXIT_FAILURE);
}
}
common_process_controls(c_fd);
if (wait_for_event == V4L2_EVENT_CTRL && wait_event_id)
if (!common_find_ctrl_id(wait_event_id)) {
fprintf(stderr, "unknown control '%s'\n", wait_event_id);
std::exit(EXIT_FAILURE);
}
if (poll_for_event == V4L2_EVENT_CTRL && poll_event_id)
if (!common_find_ctrl_id(poll_event_id)) {
fprintf(stderr, "unknown control '%s'\n", poll_event_id);
std::exit(EXIT_FAILURE);
}
if (epoll_for_event == V4L2_EVENT_CTRL && epoll_event_id)
if (!common_find_ctrl_id(epoll_event_id)) {
fprintf(stderr, "unknown control '%s'\n", epoll_event_id);
std::exit(EXIT_FAILURE);
}
if (options[OptAll]) {
options[OptGetVideoFormat] = 1;
options[OptGetVideoOutFormat] = 1;
options[OptGetDriverInfo] = 1;
options[OptGetInput] = 1;
options[OptGetOutput] = 1;
options[OptGetAudioInput] = 1;
options[OptGetAudioOutput] = 1;
options[OptGetStandard] = 1;
options[OptGetParm] = 1;
options[OptGetOutputParm] = 1;
options[OptGetFreq] = 1;
options[OptGetTuner] = 1;
options[OptGetModulator] = 1;
options[OptGetOverlayFormat] = 1;
options[OptGetVbiFormat] = 1;
options[OptGetVbiOutFormat] = 1;
options[OptGetSlicedVbiFormat] = 1;
options[OptGetSlicedVbiOutFormat] = 1;
options[OptGetSdrFormat] = 1;
options[OptGetSdrOutFormat] = 1;
options[OptGetMetaFormat] = 1;
options[OptGetMetaOutFormat] = 1;
options[OptGetFBuf] = 1;
options[OptGetCropCap] = 1;
options[OptGetOutputCropCap] = 1;
options[OptGetJpegComp] = 1;
options[OptGetDvTimings] = 1;
options[OptGetDvTimingsCap] = 1;
options[OptGetPriority] = 1;
options[OptGetSelection] = 1;
options[OptGetOutputSelection] = 1;
options[OptListCtrlsMenus] = 1;
options[OptSilent] = 1;
}
/* Information Opts */
if (options[OptGetDriverInfo]) {
printf("Driver Info%s:\n",
options[OptUseWrapper] ? " (using libv4l2)" : "");
if (is_subdev)
v4l2_info_subdev_capability(subdevcap);
else
v4l2_info_capability(vcap);
}
if (options[OptGetDriverInfo] && media_fd >= 0)
mi_media_info_for_fd(media_fd, fd);
/* Set options */
common_set(c_fd);
tuner_set(c_fd);
io_set(c_fd);
stds_set(c_fd);
vidcap_set(c_fd);
vidout_set(out_device ? c_out_fd : c_fd);
overlay_set(c_fd);
vbi_set(c_fd);
sdr_set(c_fd);
meta_set(c_fd);
subdev_set(c_fd);
selection_set(c_fd);
misc_set(c_fd);
edid_set(c_fd);
/* Get options */
common_get(c_fd);
tuner_get(c_fd);
io_get(c_fd);
stds_get(c_fd);
vidcap_get(c_fd);
vidout_get(out_device ? c_out_fd : c_fd);
overlay_get(c_fd);
vbi_get(c_fd);
sdr_get(c_fd);
meta_get(c_fd);
subdev_get(c_fd);
selection_get(c_fd);
misc_get(c_fd);
edid_get(c_fd);
/* List options */
common_list(c_fd);
io_list(c_fd);
stds_list(c_fd);
vidcap_list(c_fd);
vidout_list(out_device ? c_out_fd : c_fd);
overlay_list(c_fd);
vbi_list(c_fd);
sdr_list(c_fd);
meta_list(c_fd);
subdev_list(c_fd);
streaming_list(c_fd, c_out_fd);
/* Special case: handled last */
streaming_set(c_fd, c_out_fd, c_exp_fd);
if (options[OptWaitForEvent]) {
struct v4l2_event_subscription sub;
struct v4l2_event ev;
memset(&sub, 0, sizeof(sub));
sub.type = wait_for_event;
if (wait_for_event == V4L2_EVENT_CTRL)
sub.id = common_find_ctrl_id(wait_event_id);
else if (wait_for_event == V4L2_EVENT_SOURCE_CHANGE)
sub.id = strtoul(wait_event_id, nullptr, 0);
if (!doioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub))
if (!doioctl(fd, VIDIOC_DQEVENT, &ev))
print_event(&ev);
}
if (options[OptPollForEvent]) {
struct v4l2_event_subscription sub;
struct v4l2_event ev;
memset(&sub, 0, sizeof(sub));
sub.flags = V4L2_EVENT_SUB_FL_SEND_INITIAL;
sub.type = poll_for_event;
if (poll_for_event == V4L2_EVENT_CTRL)
sub.id = common_find_ctrl_id(poll_event_id);
else if (poll_for_event == V4L2_EVENT_SOURCE_CHANGE)
sub.id = strtoul(poll_event_id, nullptr, 0);
if (!doioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub)) {
fd_set fds;
__u32 seq = 0;
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
while (true) {
int res;
FD_ZERO(&fds);
FD_SET(fd, &fds);
res = select(fd + 1, nullptr, nullptr, &fds, nullptr);
if (res <= 0)
break;
if (doioctl(fd, VIDIOC_DQEVENT, &ev))
break;
print_event(&ev);
if (ev.sequence > seq)
printf("\tMissed %d events\n",
ev.sequence - seq);
seq = ev.sequence + 1;
}
}
}
if (options[OptEPollForEvent]) {
struct epoll_event epoll_ev;
int epollfd = -1;
struct v4l2_event_subscription sub;
struct v4l2_event ev;
epollfd = epoll_create1(0);
epoll_ev.events = EPOLLPRI;
epoll_ev.data.fd = fd;
memset(&sub, 0, sizeof(sub));
sub.flags = V4L2_EVENT_SUB_FL_SEND_INITIAL;
sub.type = epoll_for_event;
if (epoll_for_event == V4L2_EVENT_CTRL)
sub.id = common_find_ctrl_id(epoll_event_id);
else if (epoll_for_event == V4L2_EVENT_SOURCE_CHANGE)
sub.id = strtoul(epoll_event_id, nullptr, 0);
if (!doioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub)) {
__u32 seq = 0;
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epoll_ev);
while (true) {
int res;
res = epoll_wait(epollfd, &epoll_ev, 1, -1);
if (res <= 0)
break;
if (doioctl(fd, VIDIOC_DQEVENT, &ev))
break;
print_event(&ev);
if (ev.sequence > seq)
printf("\tMissed %d events\n",
ev.sequence - seq);
seq = ev.sequence + 1;
}
}
close(epollfd);
}
if (options[OptSleep]) {
sleep(secs);
printf("Test VIDIOC_QUERYCAP:\n");
if (c_fd.querycap(vcap, true) == 0)
printf("\tDriver name : %s\n", vcap.driver);
else
perror("VIDIOC_QUERYCAP");
}
c_fd.close();
if (out_device)
c_out_fd.close();
if (export_device)
c_exp_fd.close();
if (media_fd >= 0)
close(media_fd);
// --all sets --silent to avoid ioctl errors to be shown when an ioctl
// is not implemented by the driver. Which is fine, but we shouldn't
// return an application error in that specific case.
std::exit(options[OptAll] ? 0 : app_result);
}