From 2a28d80fa9b9080f3a4ce62a561ac99b19e626e4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 11 Mar 2014 00:53:30 +0100 Subject: [PATCH] v4l2-ctl: add support for custom bytesperline values. The V4L2 API allows application to select a bytesperline value that's larger than what the driver gives you, effectively padding each line. Add support to v4l2-ctl to set custom bytesperline values when trying/setting new formats. This also fixes a bug where setting multiplanar formats failed: in the *_cmd function the is_multiplanar global variable isn't set yet because the device hasn't been opened. So it always assumed a single planar format. The fixed code is actually cleaner than what I had before. Signed-off-by: Hans Verkuil --- utils/v4l2-ctl/v4l2-ctl-vidcap.cpp | 76 ++++++++++++++++++------------------ utils/v4l2-ctl/v4l2-ctl-vidout.cpp | 79 +++++++++++++++++++------------------- utils/v4l2-ctl/v4l2-ctl.cpp | 17 ++++++-- utils/v4l2-ctl/v4l2-ctl.h | 3 +- 4 files changed, 94 insertions(+), 81 deletions(-) diff --git a/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp b/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp index c979b22..01b386c 100644 --- a/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp @@ -21,10 +21,11 @@ #include "v4l2-ctl.h" -static struct v4l2_format vfmt_cap; /* set_format/get_format for video */ 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; +static __u32 bytesperline[VIDEO_MAX_PLANES]; void vidcap_usage(void) { @@ -44,10 +45,11 @@ void vidcap_usage(void) " -V, --get-fmt-video\n" " query the video capture format [VIDIOC_G_FMT]\n" " -v, --set-fmt-video\n" - " --try-fmt-video=width=,height=,pixelformat=,field=\n" + " --try-fmt-video=width=,height=,pixelformat=,field=,bytesperline=\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 option can be used multiple times, once for each plane.\n" " can be one of:\n" " any, none, top, bottom, interlaced, seq_tb, seq_bt,\n" " alternate, interlaced_tb, interlaced_bt\n" @@ -185,30 +187,17 @@ static void print_video_fields(int fd) void vidcap_cmd(int ch, char *optarg) { - __u32 width, height, pixfmt, field, colorspace; + __u32 colorspace; char *value, *subs; switch (ch) { case OptSetVideoFormat: case OptTryVideoFormat: - set_fmts = parse_fmt(optarg, width, height, pixfmt, field, colorspace); + set_fmts = parse_fmt(optarg, width, height, pixfmt, field, colorspace, bytesperline); if (!set_fmts || (set_fmts & FmtColorspace)) { vidcap_usage(); exit(1); } - if (is_multiplanar) { - vfmt_cap.fmt.pix_mp.width = width; - vfmt_cap.fmt.pix_mp.height = height; - vfmt_cap.fmt.pix_mp.pixelformat = pixfmt; - vfmt_cap.fmt.pix_mp.field = field; - vfmt_cap.fmt.pix_mp.colorspace = colorspace; - } else { - vfmt_cap.fmt.pix.width = width; - vfmt_cap.fmt.pix.height = height; - vfmt_cap.fmt.pix.pixelformat = pixfmt; - vfmt_cap.fmt.pix.field = field; - vfmt_cap.fmt.pix.colorspace = colorspace; - } break; case OptListFrameSizes: if (strlen(optarg) == 4) @@ -259,14 +248,15 @@ void vidcap_set(int fd) struct v4l2_format vfmt; vfmt.type = vidcap_buftype; + if (doioctl(fd, VIDIOC_G_FMT, &vfmt) == 0) { if (is_multiplanar) { if (set_fmts & FmtWidth) - vfmt.fmt.pix_mp.width = vfmt_cap.fmt.pix_mp.width; + vfmt.fmt.pix_mp.width = width; if (set_fmts & FmtHeight) - vfmt.fmt.pix_mp.height = vfmt_cap.fmt.pix_mp.height; + vfmt.fmt.pix_mp.height = height; if (set_fmts & FmtPixelFormat) { - vfmt.fmt.pix_mp.pixelformat = vfmt_cap.fmt.pix_mp.pixelformat; + vfmt.fmt.pix_mp.pixelformat = pixfmt; if (vfmt.fmt.pix_mp.pixelformat < 256) { vfmt.fmt.pix_mp.pixelformat = find_pixel_format(fd, vfmt.fmt.pix_mp.pixelformat, @@ -274,19 +264,25 @@ void vidcap_set(int fd) } } if (set_fmts & FmtField) - vfmt.fmt.pix_mp.field = vfmt_cap.fmt.pix_mp.field; - /* 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; + vfmt.fmt.pix_mp.field = field; + 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; + } } else { if (set_fmts & FmtWidth) - vfmt.fmt.pix.width = vfmt_cap.fmt.pix.width; + vfmt.fmt.pix.width = width; if (set_fmts & FmtHeight) - vfmt.fmt.pix.height = vfmt_cap.fmt.pix.height; + vfmt.fmt.pix.height = height; if (set_fmts & FmtPixelFormat) { - vfmt.fmt.pix.pixelformat = vfmt_cap.fmt.pix.pixelformat; + vfmt.fmt.pix.pixelformat = pixfmt; if (vfmt.fmt.pix.pixelformat < 256) { vfmt.fmt.pix.pixelformat = find_pixel_format(fd, vfmt.fmt.pix.pixelformat, @@ -294,11 +290,15 @@ void vidcap_set(int fd) } } if (set_fmts & FmtField) - vfmt.fmt.pix.field = vfmt_cap.fmt.pix.field; - /* 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; + vfmt.fmt.pix.field = field; + 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 (options[OptSetVideoFormat]) @@ -314,9 +314,11 @@ void vidcap_set(int fd) void vidcap_get(int fd) { if (options[OptGetVideoFormat]) { - vfmt_cap.type = vidcap_buftype; - if (doioctl(fd, VIDIOC_G_FMT, &vfmt_cap) == 0) - printfmt(vfmt_cap); + struct v4l2_format vfmt; + + vfmt.type = vidcap_buftype; + if (doioctl(fd, VIDIOC_G_FMT, &vfmt) == 0) + printfmt(vfmt); } } diff --git a/utils/v4l2-ctl/v4l2-ctl-vidout.cpp b/utils/v4l2-ctl/v4l2-ctl-vidout.cpp index 1d99f4a..3bf1254 100644 --- a/utils/v4l2-ctl/v4l2-ctl-vidout.cpp +++ b/utils/v4l2-ctl/v4l2-ctl-vidout.cpp @@ -21,8 +21,9 @@ #include "v4l2-ctl.h" -static struct v4l2_format vfmt_out; /* set_format/get_format for video */ static unsigned set_fmts_out; +static __u32 width, height, pixfmt, field, colorspace; +static __u32 bytesperline[VIDEO_MAX_PLANES]; void vidout_usage(void) { @@ -32,10 +33,11 @@ void vidout_usage(void) " --get-fmt-video-out\n" " query the video output format [VIDIOC_G_FMT]\n" " --set-fmt-video-out\n" - " --try-fmt-video-out=width=,height=,pixelformat=,field=,colorspace=\n" + " --try-fmt-video-out=width=,height=,pixelformat=,field=,colorspace=,bytesperline=\n" " set/try the video output format [VIDIOC_S/TRY_FMT]\n" " pixelformat is either the format index as reported by\n" " --list-formats-out, or the fourcc value as a string.\n" + " The bytesperline option can be used multiple times, once for each plane.\n" " can be one of:\n" " any, none, top, bottom, interlaced, seq_tb, seq_bt,\n" " alternate, interlaced_tb, interlaced_bt\n" @@ -75,29 +77,14 @@ static void print_video_out_fields(int fd) void vidout_cmd(int ch, char *optarg) { - __u32 width, height, pixfmt, field, colorspace; - switch (ch) { case OptSetVideoOutFormat: case OptTryVideoOutFormat: - set_fmts_out = parse_fmt(optarg, width, height, pixfmt, field, colorspace); + set_fmts_out = parse_fmt(optarg, width, height, pixfmt, field, colorspace, bytesperline); if (!set_fmts_out) { vidcap_usage(); exit(1); } - if (is_multiplanar) { - vfmt_out.fmt.pix_mp.width = width; - vfmt_out.fmt.pix_mp.height = height; - vfmt_out.fmt.pix_mp.pixelformat = pixfmt; - vfmt_out.fmt.pix_mp.field = field; - vfmt_out.fmt.pix_mp.colorspace = colorspace; - } else { - vfmt_out.fmt.pix.width = width; - vfmt_out.fmt.pix.height = height; - vfmt_out.fmt.pix.pixelformat = pixfmt; - vfmt_out.fmt.pix.field = field; - vfmt_out.fmt.pix.colorspace = colorspace; - } break; } } @@ -113,11 +100,11 @@ void vidout_set(int fd) if (doioctl(fd, VIDIOC_G_FMT, &vfmt) == 0) { if (is_multiplanar) { if (set_fmts_out & FmtWidth) - vfmt.fmt.pix_mp.width = vfmt_out.fmt.pix_mp.width; + vfmt.fmt.pix_mp.width = width; if (set_fmts_out & FmtHeight) - vfmt.fmt.pix_mp.height = vfmt_out.fmt.pix_mp.height; + vfmt.fmt.pix_mp.height = height; if (set_fmts_out & FmtPixelFormat) { - vfmt.fmt.pix_mp.pixelformat = vfmt_out.fmt.pix_mp.pixelformat; + vfmt.fmt.pix_mp.pixelformat = pixfmt; if (vfmt.fmt.pix_mp.pixelformat < 256) { vfmt.fmt.pix_mp.pixelformat = find_pixel_format(fd, vfmt.fmt.pix_mp.pixelformat, @@ -125,21 +112,27 @@ void vidout_set(int fd) } } if (set_fmts_out & FmtField) - vfmt.fmt.pix_mp.field = vfmt_out.fmt.pix_mp.field; + vfmt.fmt.pix_mp.field = field; if (set_fmts_out & FmtColorspace) - vfmt.fmt.pix_mp.colorspace = vfmt_out.fmt.pix_mp.colorspace; - /* 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; + vfmt.fmt.pix_mp.colorspace = colorspace; + if (set_fmts_out & 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; + } } else { if (set_fmts_out & FmtWidth) - vfmt.fmt.pix.width = vfmt_out.fmt.pix.width; + vfmt.fmt.pix.width = width; if (set_fmts_out & FmtHeight) - vfmt.fmt.pix.height = vfmt_out.fmt.pix.height; + vfmt.fmt.pix.height = height; if (set_fmts_out & FmtPixelFormat) { - vfmt.fmt.pix.pixelformat = vfmt_out.fmt.pix.pixelformat; + vfmt.fmt.pix.pixelformat = pixfmt; if (vfmt.fmt.pix.pixelformat < 256) { vfmt.fmt.pix.pixelformat = find_pixel_format(fd, vfmt.fmt.pix.pixelformat, @@ -147,13 +140,17 @@ void vidout_set(int fd) } } if (set_fmts_out & FmtField) - vfmt.fmt.pix.field = vfmt_out.fmt.pix.field; + vfmt.fmt.pix.field = field; if (set_fmts_out & FmtColorspace) - vfmt.fmt.pix.colorspace = vfmt_out.fmt.pix.colorspace; - /* 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; + vfmt.fmt.pix.colorspace = colorspace; + if (set_fmts_out & 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 (options[OptSetVideoOutFormat]) @@ -169,9 +166,11 @@ void vidout_set(int fd) void vidout_get(int fd) { if (options[OptGetVideoOutFormat]) { - vfmt_out.type = vidout_buftype; - if (doioctl(fd, VIDIOC_G_FMT, &vfmt_out) == 0) - printfmt(vfmt_out); + struct v4l2_format vfmt; + + vfmt.type = vidout_buftype; + if (doioctl(fd, VIDIOC_G_FMT, &vfmt) == 0) + printfmt(vfmt); } } diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp index 13944e2..6baae8e 100644 --- a/utils/v4l2-ctl/v4l2-ctl.cpp +++ b/utils/v4l2-ctl/v4l2-ctl.cpp @@ -696,10 +696,11 @@ static __u32 parse_colorspace(const char *s) } int parse_fmt(char *optarg, __u32 &width, __u32 &height, __u32 &pixelformat, - __u32 &field, __u32 &colorspace) + __u32 &field, __u32 &colorspace, __u32 *bytesperline) { char *value, *subs; int fmts = 0; + unsigned bpl_index = 0; field = V4L2_FIELD_ANY; subs = optarg; @@ -710,16 +711,17 @@ int parse_fmt(char *optarg, __u32 &width, __u32 &height, __u32 &pixelformat, "pixelformat", "field", "colorspace", + "bytesperline", NULL }; switch (parse_subopt(&subs, subopts, &value)) { case 0: - width = strtol(value, 0L, 0); + width = strtoul(value, 0L, 0); fmts |= FmtWidth; break; case 1: - height = strtol(value, 0L, 0); + height = strtoul(value, 0L, 0); fmts |= FmtHeight; break; case 2: @@ -742,6 +744,15 @@ int parse_fmt(char *optarg, __u32 &width, __u32 &height, __u32 &pixelformat, else fprintf(stderr, "unknown colorspace %s\n", value); break; + case 5: + bytesperline[bpl_index] = strtoul(value, 0L, 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; default: return 0; } diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h index f9ef10d..1a21335 100644 --- a/utils/v4l2-ctl/v4l2-ctl.h +++ b/utils/v4l2-ctl/v4l2-ctl.h @@ -187,6 +187,7 @@ typedef struct { #define FmtTop (1L<<6) #define FmtField (1L<<7) #define FmtColorspace (1L<<8) +#define FmtBytesPerLine (1L<<9) // v4l2-ctl.cpp int doioctl_name(int fd, unsigned long int request, void *parm, const char *name); @@ -203,7 +204,7 @@ std::string field2s(int val); void print_v4lstd(v4l2_std_id std); __u32 parse_field(const char *s); int parse_fmt(char *optarg, __u32 &width, __u32 &height, __u32 &pixelformat, - __u32 &field, __u32 &colorspace); + __u32 &field, __u32 &colorspace, __u32 *bytesperline); __u32 find_pixel_format(int fd, unsigned index, bool output, bool mplane); void printfmt(const struct v4l2_format &vfmt); void print_video_formats(int fd, __u32 type); -- 2.7.4