blob: cca2d4b42b53d6c1cd1f109803fb2e253a7e40d2 [file] [log] [blame]
#include <cctype>
#include <endian.h>
#include "v4l2-ctl.h"
static struct v4l2_frmsizeenum frmsize; /* list frame sizes */
static struct v4l2_frmivalenum frmival; /* list frame intervals */
static unsigned set_fmts;
static __u32 width, height, pixfmt, field, flags;
static __u32 ycbcr, quantization, xfer_func, colorspace;
static __u32 bytesperline[VIDEO_MAX_PLANES];
static __u32 sizeimage[VIDEO_MAX_PLANES];
static unsigned mbus_code;
void vidcap_usage()
{
printf("\nVideo Capture Formats options:\n"
" --list-formats [<mbus_code>]\n"
" display supported video formats. <mbus_code> is an optional\n"
" media bus code, if the device has capability V4L2_CAP_IO_MC\n"
" then only formats that support this media bus code are listed\n"
" [VIDIOC_ENUM_FMT]\n"
" --list-formats-ext [<mbus_code>]\n"
" display supported video formats including frame sizes and intervals\n"
" <mbus_code> is an optional media bus code, if the device has\n"
" capability V4L2_CAP_IO_MC then only formats that support this\n"
" media bus code are listed [VIDIOC_ENUM_FMT]\n"
" --list-framesizes <f>\n"
" list supported framesizes for pixelformat <f>\n"
" [VIDIOC_ENUM_FRAMESIZES]\n"
" pixelformat is the fourcc value as a string\n"
" --list-frameintervals width=<w>,height=<h>,pixelformat=<f>\n"
" list supported frame intervals for pixelformat <f> and\n"
" the given width and height [VIDIOC_ENUM_FRAMEINTERVALS]\n"
" pixelformat is the fourcc value as a string\n"
" --list-fields list supported fields for the current format\n"
" -V, --get-fmt-video\n"
" query the video capture format [VIDIOC_G_FMT]\n"
" -v, --set-fmt-video\n"
" --try-fmt-video width=<w>,height=<h>,pixelformat=<pf>,field=<f>,colorspace=<c>,\n"
" xfer=<xf>,ycbcr=<y>,hsv=<hsv>,quantization=<q>,\n"
" premul-alpha,bytesperline=<bpl>,sizeimage=<sz>\n"
" set/try the video capture format [VIDIOC_S/TRY_FMT]\n"
" pixelformat is either the format index as reported by\n"
" --list-formats, or the fourcc value as a string.\n"
" The bytesperline and sizeimage options can be used multiple times,\n"
" once for each plane.\n"
" premul-alpha sets V4L2_PIX_FMT_FLAG_PREMUL_ALPHA.\n"
" <f> can be one of the following field layouts:\n"
" any, none, top, bottom, interlaced, seq_tb, seq_bt,\n"
" alternate, interlaced_tb, interlaced_bt\n"
" <c> can be one of the following colorspaces:\n"
" smpte170m, smpte240m, rec709, 470m, 470bg, jpeg, srgb,\n"
" oprgb, bt2020, dcip3\n"
" <xf> can be one of the following transfer functions:\n"
" default, 709, srgb, oprgb, smpte240m, smpte2084, dcip3, none\n"
" <y> can be one of the following Y'CbCr encodings:\n"
" default, 601, 709, xv601, xv709, bt2020, bt2020c, smpte240m\n"
" <hsv> can be one of the following HSV encodings:\n"
" default, 180, 256\n"
" <q> can be one of the following quantization methods:\n"
" default, full-range, lim-range\n"
);
}
static void print_video_fields(int fd)
{
struct v4l2_format fmt;
struct v4l2_format tmp;
memset(&fmt, 0, sizeof(fmt));
fmt.fmt.pix.priv = priv_magic;
fmt.type = vidcap_buftype;
if (test_ioctl(fd, VIDIOC_G_FMT, &fmt) < 0)
return;
printf("Supported Video Fields:\n");
for (__u32 f = V4L2_FIELD_NONE; f <= V4L2_FIELD_INTERLACED_BT; f++) {
bool ok;
tmp = fmt;
if (is_multiplanar)
tmp.fmt.pix_mp.field = f;
else
tmp.fmt.pix.field = f;
if (test_ioctl(fd, VIDIOC_TRY_FMT, &tmp) < 0)
continue;
if (is_multiplanar)
ok = tmp.fmt.pix_mp.field == f;
else
ok = tmp.fmt.pix.field == f;
if (ok)
printf("\t%s\n", field2s(f).c_str());
}
}
void vidcap_cmd(int ch, char *optarg)
{
char *value, *subs;
bool be_pixfmt;
switch (ch) {
case OptSetVideoFormat:
case OptTryVideoFormat:
set_fmts = parse_fmt(optarg, width, height, pixfmt, field, colorspace,
xfer_func, ycbcr, quantization, flags, bytesperline,
sizeimage);
if (!set_fmts) {
vidcap_usage();
std::exit(EXIT_FAILURE);
}
break;
case OptListFormats:
case OptListFormatsExt:
if (optarg)
mbus_code = strtoul(optarg, nullptr, 0);
break;
case OptListFrameSizes:
be_pixfmt = strlen(optarg) == 7 && !memcmp(optarg + 4, "-BE", 3);
if (be_pixfmt || strlen(optarg) == 4) {
frmsize.pixel_format = v4l2_fourcc(optarg[0], optarg[1],
optarg[2], optarg[3]);
if (be_pixfmt)
frmsize.pixel_format |= 1U << 31;
} else if (isdigit(optarg[0])) {
frmsize.pixel_format = strtol(optarg, nullptr, 0);
} else {
fprintf(stderr, "The pixelformat '%s' is invalid\n", optarg);
std::exit(EXIT_FAILURE);
}
break;
case OptListFrameIntervals:
subs = optarg;
while (*subs != '\0') {
static constexpr const char *subopts[] = {
"width",
"height",
"pixelformat",
nullptr
};
switch (parse_subopt(&subs, subopts, &value)) {
case 0:
frmival.width = strtol(value, nullptr, 0);
break;
case 1:
frmival.height = strtol(value, nullptr, 0);
break;
case 2:
be_pixfmt = strlen(value) == 7 && !memcmp(value + 4, "-BE", 3);
if (be_pixfmt || strlen(value) == 4) {
frmival.pixel_format =
v4l2_fourcc(value[0], value[1],
value[2], value[3]);
if (be_pixfmt)
frmival.pixel_format |= 1U << 31;
} else if (isdigit(optarg[0])) {
frmival.pixel_format = strtol(value, nullptr, 0);
} else {
fprintf(stderr, "The pixelformat '%s' is invalid\n", optarg);
std::exit(EXIT_FAILURE);
}
break;
default:
vidcap_usage();
std::exit(EXIT_FAILURE);
}
}
break;
}
}
int vidcap_get_and_update_fmt(cv4l_fd &_fd, struct v4l2_format &vfmt)
{
int fd = _fd.g_fd();
int ret;
memset(&vfmt, 0, sizeof(vfmt));
vfmt.fmt.pix.priv = priv_magic;
vfmt.type = vidcap_buftype;
ret = doioctl(fd, VIDIOC_G_FMT, &vfmt);
if (ret)
return ret;
if (is_multiplanar) {
if (set_fmts & FmtWidth)
vfmt.fmt.pix_mp.width = width;
if (set_fmts & FmtHeight)
vfmt.fmt.pix_mp.height = height;
if (set_fmts & FmtPixelFormat) {
vfmt.fmt.pix_mp.pixelformat = pixfmt;
if (vfmt.fmt.pix_mp.pixelformat < 256) {
vfmt.fmt.pix_mp.pixelformat = pixfmt =
find_pixel_format(fd, vfmt.fmt.pix_mp.pixelformat,
false, true);
}
}
if (set_fmts & FmtField)
vfmt.fmt.pix_mp.field = field;
if (set_fmts & FmtFlags)
vfmt.fmt.pix_mp.flags = flags;
if (set_fmts & FmtBytesPerLine) {
for (unsigned i = 0; i < VIDEO_MAX_PLANES; i++)
vfmt.fmt.pix_mp.plane_fmt[i].bytesperline =
bytesperline[i];
} else {
/*
* G_FMT might return bytesperline values > width,
* reset them to 0 to force the driver to update them
* to the closest value for the new width.
*/
for (unsigned i = 0; i < vfmt.fmt.pix_mp.num_planes; i++)
vfmt.fmt.pix_mp.plane_fmt[i].bytesperline = 0;
}
if (set_fmts & FmtSizeImage) {
for (unsigned i = 0; i < VIDEO_MAX_PLANES; i++)
vfmt.fmt.pix_mp.plane_fmt[i].sizeimage =
sizeimage[i];
}
if (set_fmts & FmtColorspace) {
vfmt.fmt.pix_mp.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix_mp.colorspace = colorspace;
}
if (set_fmts & FmtYCbCr) {
vfmt.fmt.pix_mp.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix_mp.ycbcr_enc = ycbcr;
}
if (set_fmts & FmtQuantization) {
vfmt.fmt.pix_mp.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix_mp.quantization = quantization;
}
if (set_fmts & FmtXferFunc) {
vfmt.fmt.pix_mp.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix_mp.xfer_func = xfer_func;
}
} else {
if (set_fmts & FmtWidth)
vfmt.fmt.pix.width = width;
if (set_fmts & FmtHeight)
vfmt.fmt.pix.height = height;
if (set_fmts & FmtPixelFormat) {
vfmt.fmt.pix.pixelformat = pixfmt;
if (vfmt.fmt.pix.pixelformat < 256) {
vfmt.fmt.pix.pixelformat = pixfmt =
find_pixel_format(fd, vfmt.fmt.pix.pixelformat,
false, false);
}
}
if (set_fmts & FmtField)
vfmt.fmt.pix.field = field;
if (set_fmts & FmtFlags)
vfmt.fmt.pix.flags = flags;
if (set_fmts & FmtBytesPerLine) {
vfmt.fmt.pix.bytesperline = bytesperline[0];
} else {
/*
* G_FMT might return a bytesperline value > width,
* reset this to 0 to force the driver to update it
* to the closest value for the new width.
*/
vfmt.fmt.pix.bytesperline = 0;
}
if (set_fmts & FmtSizeImage)
vfmt.fmt.pix.sizeimage = sizeimage[0];
if (set_fmts & FmtColorspace) {
vfmt.fmt.pix.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix.colorspace = colorspace;
}
if (set_fmts & FmtYCbCr) {
vfmt.fmt.pix.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix.ycbcr_enc = ycbcr;
}
if (set_fmts & FmtQuantization) {
vfmt.fmt.pix.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix.quantization = quantization;
}
if (set_fmts & FmtXferFunc) {
vfmt.fmt.pix.flags |= V4L2_PIX_FMT_FLAG_SET_CSC;
vfmt.fmt.pix.xfer_func = xfer_func;
}
}
if ((set_fmts & FmtPixelFormat) &&
!valid_pixel_format(fd, pixfmt, false, is_multiplanar)) {
if (pixfmt)
fprintf(stderr, "The pixelformat '%s' is invalid\n",
fcc2s(pixfmt).c_str());
else
fprintf(stderr, "The pixelformat index was invalid\n");
return -EINVAL;
}
return 0;
}
void vidcap_set(cv4l_fd &_fd)
{
if (options[OptSetVideoFormat] || options[OptTryVideoFormat]) {
int fd = _fd.g_fd();
int ret;
struct v4l2_format vfmt;
if (vidcap_get_and_update_fmt(_fd, vfmt) == 0) {
if (options[OptSetVideoFormat])
ret = doioctl(fd, VIDIOC_S_FMT, &vfmt);
else
ret = doioctl(fd, VIDIOC_TRY_FMT, &vfmt);
if (ret == 0 && (verbose || options[OptTryVideoFormat]))
printfmt(fd, vfmt);
}
}
}
void vidcap_get(cv4l_fd &fd)
{
if (options[OptGetVideoFormat]) {
struct v4l2_format vfmt;
memset(&vfmt, 0, sizeof(vfmt));
vfmt.fmt.pix.priv = priv_magic;
vfmt.type = vidcap_buftype;
if (doioctl(fd.g_fd(), VIDIOC_G_FMT, &vfmt) == 0)
printfmt(fd.g_fd(), vfmt);
}
}
void vidcap_list(cv4l_fd &fd)
{
if (options[OptListFormats]) {
printf("ioctl: VIDIOC_ENUM_FMT\n");
print_video_formats(fd, vidcap_buftype, mbus_code);
}
if (options[OptListFormatsExt]) {
printf("ioctl: VIDIOC_ENUM_FMT\n");
print_video_formats_ext(fd, vidcap_buftype, mbus_code);
}
if (options[OptListFields]) {
print_video_fields(fd.g_fd());
}
if (options[OptListFrameSizes]) {
frmsize.index = 0;
if (frmsize.pixel_format < 256) {
frmsize.pixel_format =
find_pixel_format(fd.g_fd(), frmsize.pixel_format,
false, is_multiplanar);
if (!frmsize.pixel_format) {
fprintf(stderr, "The pixelformat index was invalid\n");
std::exit(EXIT_FAILURE);
}
}
if (!valid_pixel_format(fd.g_fd(), frmsize.pixel_format, false, is_multiplanar) &&
!valid_pixel_format(fd.g_fd(), frmsize.pixel_format, true, is_multiplanar)) {
fprintf(stderr, "The pixelformat '%s' is invalid\n",
fcc2s(frmsize.pixel_format).c_str());
std::exit(EXIT_FAILURE);
}
printf("ioctl: VIDIOC_ENUM_FRAMESIZES\n");
while (test_ioctl(fd.g_fd(), VIDIOC_ENUM_FRAMESIZES, &frmsize) >= 0) {
print_frmsize(frmsize, "");
frmsize.index++;
}
}
if (options[OptListFrameIntervals]) {
frmival.index = 0;
if (frmival.pixel_format < 256) {
frmival.pixel_format =
find_pixel_format(fd.g_fd(), frmival.pixel_format,
false, is_multiplanar);
if (!frmival.pixel_format) {
fprintf(stderr, "The pixelformat index was invalid\n");
std::exit(EXIT_FAILURE);
}
}
if (!valid_pixel_format(fd.g_fd(), frmival.pixel_format, false, is_multiplanar)) {
fprintf(stderr, "The pixelformat '%s' is invalid\n",
fcc2s(frmival.pixel_format).c_str());
std::exit(EXIT_FAILURE);
}
printf("ioctl: VIDIOC_ENUM_FRAMEINTERVALS\n");
while (test_ioctl(fd.g_fd(), VIDIOC_ENUM_FRAMEINTERVALS, &frmival) >= 0) {
print_frmival(frmival, "");
frmival.index++;
}
}
}
void print_touch_buffer(FILE *f, cv4l_buffer &buf, cv4l_fmt &fmt, cv4l_queue &q)
{
static constexpr char img[16] = {
'.', ',', ':', ';', '!', '|', 'i', 'c',
'n', 'o', 'm', 'I', 'C', 'N', 'O', 'M',
};
auto vbuf = static_cast<__s16 *>(q.g_dataptr(buf.g_index(), 0));
__u32 x, y;
switch (fmt.g_pixelformat()) {
case V4L2_TCH_FMT_DELTA_TD16:
for (y = 0; y < fmt.g_height(); y++) {
fprintf(f, "TD16: ");
for (x = 0; x < fmt.g_width(); x++, vbuf++) {
auto v = static_cast<__s16>(le16toh(*vbuf));
if (!options[OptConcise])
fprintf(f, "% 4d", v);
else if (v > 255)
fprintf(f, "*");
else if (v < -32)
fprintf(f, "-");
else if (v < 0)
fprintf(f, "%c", img[0]);
else
fprintf(f, "%c", img[v / 16]);
}
fprintf(f, "\n");
}
break;
}
}