| #include "v4l2-ctl.h" |
| |
| struct v4l2_decoder_cmd dec_cmd; /* (try_)decoder_cmd */ |
| static struct v4l2_encoder_cmd enc_cmd; /* (try_)encoder_cmd */ |
| static struct v4l2_jpegcompression jpegcomp; /* jpeg compression */ |
| static struct v4l2_streamparm parm; /* get/set parm */ |
| static double fps = 0; /* set framerate speed, in fps */ |
| static double output_fps = 0; /* set framerate speed, in fps */ |
| |
| void misc_usage() |
| { |
| printf("\nMiscellaneous options:\n" |
| " --wait-for-event <event>\n" |
| " wait for an event [VIDIOC_DQEVENT]\n" |
| " <event> is the event number or one of:\n" |
| " eos, vsync, ctrl=<id>, frame_sync, source_change=<pad>,\n" |
| " motion_det\n" |
| " where <id> is the name of the control\n" |
| " and where <pad> is the index of the pad or input\n" |
| " --poll-for-event <event>\n" |
| " poll for an event [VIDIOC_DQEVENT]\n" |
| " see --wait-for-event for possible events\n" |
| " --epoll-for-event <event>\n" |
| " epoll for an event [VIDIOC_DQEVENT]\n" |
| " see --wait-for-event for possible events\n" |
| " -P, --get-parm display video parameters [VIDIOC_G_PARM]\n" |
| " -p, --set-parm <fps>\n" |
| " set video framerate in <fps> [VIDIOC_S_PARM]\n" |
| " --get-output-parm display output video parameters [VIDIOC_G_PARM]\n" |
| " --set-output-parm <fps>\n" |
| " set output video framerate in <fps> [VIDIOC_S_PARM]\n" |
| " --get-jpeg-comp query the JPEG compression [VIDIOC_G_JPEGCOMP]\n" |
| " --set-jpeg-comp quality=<q>,markers=<markers>,comment=<c>,app<n>=<a>\n" |
| " set the JPEG compression [VIDIOC_S_JPEGCOMP]\n" |
| " <n> is the app segment: 0-9/a-f, <a> is the actual string.\n" |
| " <markers> is a colon separated list of:\n" |
| " dht: Define Huffman Tables\n" |
| " dqt: Define Quantization Tables\n" |
| " dri: Define Restart Interval\n" |
| " --encoder-cmd cmd=<cmd>,flags=<flags>\n" |
| " Send a command to the encoder [VIDIOC_ENCODER_CMD]\n" |
| " cmd=start|stop|pause|resume\n" |
| " flags=stop_at_gop_end\n" |
| " --try-encoder-cmd cmd=<cmd>,flags=<flags>\n" |
| " Try an encoder command [VIDIOC_TRY_ENCODER_CMD]\n" |
| " See --encoder-cmd for the arguments.\n" |
| " --decoder-cmd cmd=<cmd>,flags=<flags>,stop_pts=<pts>,start_speed=<speed>,\n" |
| " start_format=<none|gop>\n" |
| " Send a command to the decoder [VIDIOC_DECODER_CMD]\n" |
| " cmd=start|stop|pause|resume\n" |
| " flags=start_mute_audio|pause_to_black|stop_to_black|\n" |
| " stop_immediately\n" |
| " --try-decoder-cmd cmd=<cmd>,flags=<flags>\n" |
| " Try a decoder command [VIDIOC_TRY_DECODER_CMD]\n" |
| " See --decoder-cmd for the arguments.\n" |
| ); |
| } |
| |
| static std::string markers2s(unsigned markers) |
| { |
| std::string s; |
| |
| if (markers & V4L2_JPEG_MARKER_DHT) |
| s += "\t\tDefine Huffman Tables\n"; |
| if (markers & V4L2_JPEG_MARKER_DQT) |
| s += "\t\tDefine Quantization Tables\n"; |
| if (markers & V4L2_JPEG_MARKER_DRI) |
| s += "\t\tDefine Restart Interval\n"; |
| if (markers & V4L2_JPEG_MARKER_COM) |
| s += "\t\tDefine Comment\n"; |
| if (markers & V4L2_JPEG_MARKER_APP) |
| s += "\t\tDefine APP segment\n"; |
| return s; |
| } |
| |
| static void printjpegcomp(const struct v4l2_jpegcompression &jc) |
| { |
| printf("JPEG compression:\n"); |
| printf("\tQuality: %d\n", jc.quality); |
| if (jc.COM_len) |
| printf("\tComment: '%s'\n", jc.COM_data); |
| if (jc.APP_len) |
| printf("\tAPP%x : '%s'\n", jc.APPn, jc.APP_data); |
| printf("\tMarkers: 0x%08x\n", jc.jpeg_markers); |
| printf("%s", markers2s(jc.jpeg_markers).c_str()); |
| } |
| |
| static void print_enccmd(const struct v4l2_encoder_cmd &cmd) |
| { |
| switch (cmd.cmd) { |
| case V4L2_ENC_CMD_START: |
| printf("\tstart\n"); |
| break; |
| case V4L2_ENC_CMD_STOP: |
| printf("\tstop%s\n", |
| (cmd.flags & V4L2_ENC_CMD_STOP_AT_GOP_END) ? " at gop end" : ""); |
| break; |
| case V4L2_ENC_CMD_PAUSE: |
| printf("\tpause\n"); |
| break; |
| case V4L2_ENC_CMD_RESUME: |
| printf("\tresume\n"); |
| break; |
| } |
| } |
| |
| static void print_deccmd(const struct v4l2_decoder_cmd &cmd) |
| { |
| __s32 speed; |
| |
| switch (cmd.cmd) { |
| case V4L2_DEC_CMD_START: |
| speed = cmd.start.speed; |
| if (speed == 0) |
| speed = 1000; |
| printf("\tstart%s%s, ", |
| cmd.start.format == V4L2_DEC_START_FMT_GOP ? " (GOP aligned)" : "", |
| (speed != 1000 && |
| (cmd.flags & V4L2_DEC_CMD_START_MUTE_AUDIO)) ? " (mute audio)" : ""); |
| if (speed == 1 || speed == -1) |
| printf("single step %s\n", |
| speed == 1 ? "forward" : "backward"); |
| else |
| printf("speed %.3fx\n", speed / 1000.0); |
| break; |
| case V4L2_DEC_CMD_STOP: |
| printf("\tstop%s%s\n", |
| (cmd.flags & V4L2_DEC_CMD_STOP_TO_BLACK) ? " to black" : "", |
| (cmd.flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) ? " immediately" : ""); |
| break; |
| case V4L2_DEC_CMD_PAUSE: |
| printf("\tpause%s\n", |
| (cmd.flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? " to black" : ""); |
| break; |
| case V4L2_DEC_CMD_RESUME: |
| printf("\tresume\n"); |
| break; |
| } |
| } |
| |
| /* Used for both encoder and decoder commands since they are the same |
| at the moment. */ |
| static int parse_cmd(const char *s) |
| { |
| if (!strcmp(s, "start")) return V4L2_ENC_CMD_START; |
| if (!strcmp(s, "stop")) return V4L2_ENC_CMD_STOP; |
| if (!strcmp(s, "pause")) return V4L2_ENC_CMD_PAUSE; |
| if (!strcmp(s, "resume")) return V4L2_ENC_CMD_RESUME; |
| return 0; |
| } |
| |
| static int parse_encflags(const char *s) |
| { |
| if (!strcmp(s, "stop_at_gop_end")) return V4L2_ENC_CMD_STOP_AT_GOP_END; |
| return 0; |
| } |
| |
| static int parse_decflags(const char *s) |
| { |
| if (!strcmp(s, "start_mute_audio")) return V4L2_DEC_CMD_START_MUTE_AUDIO; |
| if (!strcmp(s, "pause_to_black")) return V4L2_DEC_CMD_PAUSE_TO_BLACK; |
| if (!strcmp(s, "stop_to_black")) return V4L2_DEC_CMD_STOP_TO_BLACK; |
| if (!strcmp(s, "stop_immediately")) return V4L2_DEC_CMD_STOP_IMMEDIATELY; |
| return 0; |
| } |
| |
| void misc_cmd(int ch, char *optarg) |
| { |
| char *value, *subs; |
| |
| switch (ch) { |
| case OptSetParm: |
| fps = strtod(optarg, nullptr); |
| break; |
| case OptSetOutputParm: |
| output_fps = strtod(optarg, nullptr); |
| break; |
| case OptSetJpegComp: |
| subs = optarg; |
| while (*subs != '\0') { |
| static constexpr const char *subopts[] = { |
| "app0", "app1", "app2", "app3", |
| "app4", "app5", "app6", "app7", |
| "app8", "app9", "appa", "appb", |
| "appc", "appd", "appe", "appf", |
| "quality", |
| "markers", |
| "comment", |
| nullptr |
| }; |
| size_t len; |
| int opt = parse_subopt(&subs, subopts, &value); |
| |
| switch (opt) { |
| case 16: |
| jpegcomp.quality = strtol(value, nullptr, 0); |
| break; |
| case 17: |
| if (strstr(value, "dht")) |
| jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DHT; |
| if (strstr(value, "dqt")) |
| jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DQT; |
| if (strstr(value, "dri")) |
| jpegcomp.jpeg_markers |= V4L2_JPEG_MARKER_DRI; |
| break; |
| case 18: |
| len = strlen(value); |
| if (len > sizeof(jpegcomp.COM_data) - 1) |
| len = sizeof(jpegcomp.COM_data) - 1; |
| jpegcomp.COM_len = len; |
| memcpy(jpegcomp.COM_data, value, len); |
| jpegcomp.COM_data[len] = '\0'; |
| break; |
| default: |
| if (opt < 0 || opt > 15) { |
| misc_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| len = strlen(value); |
| if (len > sizeof(jpegcomp.APP_data) - 1) |
| len = sizeof(jpegcomp.APP_data) - 1; |
| if (jpegcomp.APP_len) { |
| fprintf(stderr, "Only one APP segment can be set\n"); |
| break; |
| } |
| jpegcomp.APP_len = len; |
| memcpy(jpegcomp.APP_data, value, len); |
| jpegcomp.APP_data[len] = '\0'; |
| jpegcomp.APPn = opt; |
| break; |
| } |
| } |
| break; |
| case OptEncoderCmd: |
| case OptTryEncoderCmd: |
| subs = optarg; |
| while (*subs != '\0') { |
| static constexpr const char *subopts[] = { |
| "cmd", |
| "flags", |
| nullptr |
| }; |
| |
| switch (parse_subopt(&subs, subopts, &value)) { |
| case 0: |
| enc_cmd.cmd = parse_cmd(value); |
| break; |
| case 1: |
| enc_cmd.flags = parse_encflags(value); |
| break; |
| default: |
| misc_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| break; |
| case OptDecoderCmd: |
| case OptTryDecoderCmd: |
| subs = optarg; |
| while (*subs != '\0') { |
| static constexpr const char *subopts[] = { |
| "cmd", |
| "flags", |
| "stop_pts", |
| "start_speed", |
| "start_format", |
| nullptr |
| }; |
| |
| switch (parse_subopt(&subs, subopts, &value)) { |
| case 0: |
| dec_cmd.cmd = parse_cmd(value); |
| break; |
| case 1: |
| dec_cmd.flags = parse_decflags(value); |
| break; |
| case 2: |
| dec_cmd.stop.pts = strtoull(value, nullptr, 0); |
| break; |
| case 3: |
| dec_cmd.start.speed = strtol(value, nullptr, 0); |
| break; |
| case 4: |
| if (!strcmp(value, "gop")) |
| dec_cmd.start.format = V4L2_DEC_START_FMT_GOP; |
| else if (!strcmp(value, "none")) |
| dec_cmd.start.format = V4L2_DEC_START_FMT_NONE; |
| break; |
| default: |
| misc_usage(); |
| std::exit(EXIT_FAILURE); |
| } |
| } |
| break; |
| } |
| } |
| |
| void misc_set(cv4l_fd &_fd) |
| { |
| int fd = _fd.g_fd(); |
| |
| if (options[OptSetParm]) { |
| memset(&parm, 0, sizeof(parm)); |
| parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| parm.parm.capture.timeperframe.numerator = 1000; |
| parm.parm.capture.timeperframe.denominator = |
| static_cast<uint32_t>(fps * parm.parm.capture.timeperframe.numerator); |
| |
| if (doioctl(fd, VIDIOC_S_PARM, &parm) == 0) { |
| struct v4l2_fract *tf = &parm.parm.capture.timeperframe; |
| |
| if (!tf->denominator || !tf->numerator) |
| printf("Invalid frame rate\n"); |
| else |
| printf("Frame rate set to %.3f fps\n", |
| 1.0 * tf->denominator / tf->numerator); |
| } |
| } |
| |
| if (options[OptSetOutputParm]) { |
| memset(&parm, 0, sizeof(parm)); |
| parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
| parm.parm.output.timeperframe.numerator = 1000; |
| parm.parm.output.timeperframe.denominator = |
| static_cast<uint32_t>(output_fps * parm.parm.output.timeperframe.numerator); |
| |
| if (doioctl(fd, VIDIOC_S_PARM, &parm) == 0) { |
| struct v4l2_fract *tf = &parm.parm.output.timeperframe; |
| |
| if (!tf->denominator || !tf->numerator) |
| printf("Invalid frame rate\n"); |
| else |
| printf("Frame rate set to %.3f fps\n", |
| 1.0 * tf->denominator / tf->numerator); |
| } |
| } |
| |
| if (options[OptSetJpegComp]) { |
| doioctl(fd, VIDIOC_S_JPEGCOMP, &jpegcomp); |
| } |
| |
| if (options[OptEncoderCmd]) |
| doioctl(fd, VIDIOC_ENCODER_CMD, &enc_cmd); |
| if (options[OptTryEncoderCmd]) |
| if (doioctl(fd, VIDIOC_TRY_ENCODER_CMD, &enc_cmd) == 0) |
| print_enccmd(enc_cmd); |
| if (options[OptDecoderCmd]) |
| doioctl(fd, VIDIOC_DECODER_CMD, &dec_cmd); |
| if (options[OptTryDecoderCmd]) |
| if (doioctl(fd, VIDIOC_TRY_DECODER_CMD, &dec_cmd) == 0) |
| print_deccmd(dec_cmd); |
| } |
| |
| void misc_get(cv4l_fd &_fd) |
| { |
| int fd = _fd.g_fd(); |
| |
| if (options[OptGetJpegComp]) { |
| struct v4l2_jpegcompression jc; |
| if (doioctl(fd, VIDIOC_G_JPEGCOMP, &jc) == 0) |
| printjpegcomp(jc); |
| } |
| |
| if (options[OptGetParm]) { |
| memset(&parm, 0, sizeof(parm)); |
| parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
| if (doioctl(fd, VIDIOC_G_PARM, &parm) == 0) { |
| const struct v4l2_fract &tf = parm.parm.capture.timeperframe; |
| |
| printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str()); |
| if (parm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) |
| printf("\tCapabilities : timeperframe\n"); |
| if (parm.parm.capture.capturemode & V4L2_MODE_HIGHQUALITY) |
| printf("\tCapture mode : high quality\n"); |
| if (!tf.denominator || !tf.numerator) |
| printf("\tFrames per second: invalid (%d/%d)\n", |
| tf.denominator, tf.numerator); |
| else |
| printf("\tFrames per second: %.3f (%d/%d)\n", |
| (1.0 * tf.denominator) / tf.numerator, |
| tf.denominator, tf.numerator); |
| printf("\tRead buffers : %d\n", parm.parm.capture.readbuffers); |
| } |
| } |
| |
| if (options[OptGetOutputParm]) { |
| memset(&parm, 0, sizeof(parm)); |
| parm.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; |
| if (doioctl(fd, VIDIOC_G_PARM, &parm) == 0) { |
| const struct v4l2_fract &tf = parm.parm.output.timeperframe; |
| |
| printf("Streaming Parameters %s:\n", buftype2s(parm.type).c_str()); |
| if (parm.parm.output.capability & V4L2_CAP_TIMEPERFRAME) |
| printf("\tCapabilities : timeperframe\n"); |
| if (parm.parm.output.outputmode & V4L2_MODE_HIGHQUALITY) |
| printf("\tOutput mode : high quality\n"); |
| if (!tf.denominator || !tf.numerator) |
| printf("\tFrames per second: invalid (%d/%d)\n", |
| tf.denominator, tf.numerator); |
| else |
| printf("\tFrames per second: %.3f (%d/%d)\n", |
| (1.0 * tf.denominator) / tf.numerator, |
| tf.denominator, tf.numerator); |
| printf("\tWrite buffers : %d\n", parm.parm.output.writebuffers); |
| } |
| } |
| } |