| /* |
| 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); |
| } |