* Re: [FFmpeg-devel] [PATCH] Add and use cli options for v4l2 encoder=h264_v4l2m2m
2022-03-13 15:34 ` [FFmpeg-devel] [PATCH] Add and use cli options for v4l2 encoder=h264_v4l2m2m hydra3333
@ 2022-03-22 17:35 ` Dennis Mungai
0 siblings, 0 replies; 2+ messages in thread
From: Dennis Mungai @ 2022-03-22 17:35 UTC (permalink / raw)
To: FFmpeg development discussions and patches
On Sun, 13 Mar 2022 at 18:34, <hydra3333@gmail.com> wrote:
> Well, let's try to submit a patch and see how it fares.
>
>
> Add commandline options to v4l2_m2m_enc (h264_v4l2m2m only)
> and use those to configure options for the h264_v4l2m2m encoder.
> Uses AVOption options to filter for valid options per v4l2 spec.
> For h264 it adds spec-compliant:
> -profile <name> (high is max accepted by Raspberry Pi)
> -level <name> (4.2 is max accepted by Raspberry Pi)
> -rc <name> (Bitrate mode, VBR or CBR or CQ)
> -shm <option> (Sequence Header Mode, separate_buffer or joined_1st_frame)
> -rsh <boolean> (Repeat Sequence Header 0(false) 1(true))
> -fsme (Frame Skip Mode for encoder, rejected by Pi OS)
> -b:v <bps> (Bit per second)
> -g <integer> (pseudo GOP size, not an actual one)
> -iframe_period <integer> (the period between two I-frames)
> -qmin <integer> (Minimum quantization parameter for h264)
> -qmax <integer> (Maximum quantization parameter for h264)
>
> Patch does not address pre-existing quirks with h264_v4l2m2m,
> eg on a Raspberry Pi,
> - Bitrate mode VBR, file is reported by mediainfo as CBR
> - Bitrate mode CBR, encoder hangs and appears to
> "lock" /dev/video11 until reboot
> - CFR input yields a VFR file reported by mediainfo (and an
> odd framerate) whereas an equivalent libx264 commandline
> yields expected CFR; tested on a Raspberry Pi4
> - Bitrate mode CBR, profile is limited to less than "high"
> - Bitrate mode VBR, only target bitrate option exposed to set
> - Bitrate mode CQ, is not exposed to set
>
> Notes:
> Patch arises from a desire to use ffmpeg on a Raspberry Pi (4 +).
> Fixes "--profile high" not working (required an integer).
> The Raspberry Pi OS does not expose a GOP size to set, so -g is
> used for backward compatibility with its value overriding
> the "close enough" effect of an "iframe_period" value.
> Hardware like Raspberry Pi 4 rejects some spec-compliant options
> beyond its capacity and/or not implemented by the Raspberry Pi OS.
> The Raspberry Pi OS repository for ffmpeg appears to have Repeat
> Sequence Header hard-coded as True, rather than a cli an option.
> Added more return value checking, AV_LOG_WARNING and a lot
> more AV_LOG_DEBUG code; one-time runtime cost of debug code
> during init phase is negligible.
> Intentionally left in //commented-out debug code.
>
> A working commandline using an interlaced source:
> /usr/local/bin/ffmpeg -hide_banner -nostats -v debug -i
> "~/Desktop/input_file_tiny.mp4" -vsync cfr -sws_flags
> lanczos+accurate_rnd+full_chroma_int+full_chroma_inp -strict experimental
> -filter_complex "[0:v]yadif=0:0:0,format=pix_fmts=yuv420p"
> -c:v h264_v4l2m2m -pix_fmt yuv420p -rc VBR -b:v 4000000 -qmin 10 -qmax 51
> -profile:v high -level 4.2 -shm separate_buffer -rsh 0
> -g:v 25 -movflags +faststart+write_colr -an -y
> "./output_file_tiny_h264_VBR_g25.mp4"
>
> Signed-off-by: hydra3333 <hydra3333@gmail.com>
> ---
> libavcodec/v4l2_m2m.h | 13 +-
> libavcodec/v4l2_m2m_enc.c | 1013 ++++++++++++++++++++++++++++++++-----
> 2 files changed, 894 insertions(+), 132 deletions(-)
>
> diff --git a/libavcodec/v4l2_m2m.h b/libavcodec/v4l2_m2m.h
> index b67b216..f3955b5 100644
> --- a/libavcodec/v4l2_m2m.h
> +++ b/libavcodec/v4l2_m2m.h
> @@ -73,9 +73,20 @@ typedef struct V4L2m2mPriv {
>
> V4L2m2mContext *context;
> AVBufferRef *context_ref;
> -
> int num_output_buffers;
> int num_capture_buffers;
> + /// h264 (mpeg4 part 10: AVC) add these to enable extra privately
> defined options (per V4L_M2M_h264_options) for h264 encoding
> + int h264_profile;
> + int h264_level;
> + int h264_video_bit_rate_mode;
> + int64_t h264_video_bit_rate;
> + int h264_qmin;
> + int h264_qmax;
> + int h264_sequence_header_mode;
> + _Bool h264_repeat_seq_header;
> + int h264_iframe_period; // overridden by h264_gop_size
> + int h264_gop_size; // if specified, overrides
> h264_iframe_period
> + int h264_frame_skip_mode_encoder;
> } V4L2m2mPriv;
>
> /**
> diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
> index 043fe80..9ac7514 100644
> --- a/libavcodec/v4l2_m2m_enc.c
> +++ b/libavcodec/v4l2_m2m_enc.c
> @@ -1,4 +1,4 @@
> -/*
> +/**
> * V4L2 mem2mem encoders
> *
> * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org>
> @@ -38,37 +38,54 @@
> #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
> #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
>
> -static inline void v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int
> num, unsigned int den)
> +static inline int v4l2_set_timeperframe(V4L2m2mContext *s, unsigned int
> num, unsigned int den)
> {
> + /**
> + v4l2_streamparm, V4L2_TYPE_IS_MULTIPLANAR defined in
> linux/videodev2.h
> + */
> struct v4l2_streamparm parm = { 0 };
> -
> + int ret;
> parm.type = V4L2_TYPE_IS_MULTIPLANAR(s->output.type) ?
> V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE : V4L2_BUF_TYPE_VIDEO_OUTPUT;
> parm.parm.output.timeperframe.denominator = den;
> parm.parm.output.timeperframe.numerator = num;
> -
> - if (ioctl(s->fd, VIDIOC_S_PARM, &parm) < 0)
> - av_log(s->avctx, AV_LOG_WARNING, "Failed to set timeperframe");
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to ioctl
> SET timeperframe; "
> + "denominator='%d' numerator='%d'\n", den, num);
> + ret = ioctl(s->fd, VIDIOC_S_PARM, &parm);
> + if (ret < 0) {
> + av_log(s->avctx, AV_LOG_ERROR,
> + "Encoder v4l2_m2m ERROR Failed to ioctl SET timeperframe;
> denominator='%d' numerator='%d' "
> + "ret='%d' errno='%d' error='%s'\n", den, num, ret, errno,
> strerror(errno));
> + return ret;
> + }
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ioctl SET
> timeperframe; "
> + "denominator='%d' numerator='%d'\n", den, num);
> + return 0;
> }
>
> -static inline void v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id,
> signed int value, const char *name, int log_warning)
> +static inline int v4l2_set_ext_ctrl(V4L2m2mContext *s, unsigned int id,
> signed int value, const char *name, int log_warning)
> {
> struct v4l2_ext_controls ctrls = { { 0 } };
> struct v4l2_ext_control ctrl = { 0 };
> -
> + int ret;
> /* set ctrls */
> ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
> ctrls.controls = &ctrl;
> ctrls.count = 1;
> -
> /* set ctrl*/
> ctrl.value = value;
> ctrl.id = id;
>
> - if (ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls) < 0)
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to ioctl
> SET '%s'='%d'\n", name, value);
> + ret = ioctl(s->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
> + if (ret < 0) {
> av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING
> : AV_LOG_DEBUG,
> - "Failed to set %s: %s\n", name, strerror(errno));
> - else
> - av_log(s->avctx, AV_LOG_DEBUG, "Encoder: %s = %d\n", name, value);
> + "Encoder v4l2_m2m WARNING Failed to ioctl SET '%s'='%d' "
> + "ret='%d' errno='%d' warning='%s'\n",
> + name, value, ret, errno, strerror(errno));
> + } else {
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ioctl SET
> '%s'='%d'\n", name, value);
> + }
> + return ret;
> }
>
> static inline int v4l2_get_ext_ctrl(V4L2m2mContext *s, unsigned int id,
> signed int *value, const char *name, int log_warning)
> @@ -76,50 +93,129 @@ static inline int v4l2_get_ext_ctrl(V4L2m2mContext
> *s, unsigned int id, signed i
> struct v4l2_ext_controls ctrls = { { 0 } };
> struct v4l2_ext_control ctrl = { 0 };
> int ret;
> -
> /* set ctrls */
> ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
> ctrls.controls = &ctrl;
> ctrls.count = 1;
> -
> /* set ctrl*/
> ctrl.id = id ;
> -
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to ioctl
> GET '%s'\n", name);
> ret = ioctl(s->fd, VIDIOC_G_EXT_CTRLS, &ctrls);
> if (ret < 0) {
> av_log(s->avctx, log_warning || errno != EINVAL ? AV_LOG_WARNING
> : AV_LOG_DEBUG,
> - "Failed to get %s\n", name);
> + "Encoder v4l2_m2m WARNING Failed to ioctl GET '%s' "
> + "ret='%d' errno='%d' warning='%s'\n", name, ret, errno,
> strerror(errno));
> return ret;
> }
> -
> *value = ctrl.value;
> -
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ioctl GET
> '%s'='%d'\n", name, ctrl.value);
> return 0;
> }
>
> -static inline unsigned int v4l2_h264_profile_from_ff(int p)
> +static inline int ff_h264_profile_from_v4l2_h264_profile(int p)
> {
> static const struct h264_profile {
> - unsigned int ffmpeg_val;
> - unsigned int v4l2_val;
> + int v4l2_val;
> + int ffmpeg_val;
> + /**
> + NOTE: V4L2_MPEG_VIDEO_H264_ profile constants from
> linux/v4l2_controls.h via linux/videodev2.h
> + */
> } profile[] = {
> - { FF_PROFILE_H264_CONSTRAINED_BASELINE,
> MPEG_VIDEO(H264_PROFILE_CONSTRAINED_BASELINE) },
> - { FF_PROFILE_H264_HIGH_444_PREDICTIVE,
> MPEG_VIDEO(H264_PROFILE_HIGH_444_PREDICTIVE) },
> - { FF_PROFILE_H264_HIGH_422_INTRA,
> MPEG_VIDEO(H264_PROFILE_HIGH_422_INTRA) },
> - { FF_PROFILE_H264_HIGH_444_INTRA,
> MPEG_VIDEO(H264_PROFILE_HIGH_444_INTRA) },
> - { FF_PROFILE_H264_HIGH_10_INTRA,
> MPEG_VIDEO(H264_PROFILE_HIGH_10_INTRA) },
> - { FF_PROFILE_H264_HIGH_422, MPEG_VIDEO(H264_PROFILE_HIGH_422) },
> - { FF_PROFILE_H264_BASELINE, MPEG_VIDEO(H264_PROFILE_BASELINE) },
> - { FF_PROFILE_H264_EXTENDED, MPEG_VIDEO(H264_PROFILE_EXTENDED) },
> - { FF_PROFILE_H264_HIGH_10, MPEG_VIDEO(H264_PROFILE_HIGH_10) },
> - { FF_PROFILE_H264_MAIN, MPEG_VIDEO(H264_PROFILE_MAIN) },
> - { FF_PROFILE_H264_HIGH, MPEG_VIDEO(H264_PROFILE_HIGH) },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
> FF_PROFILE_H264_BASELINE },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE,
> FF_PROFILE_H264_CONSTRAINED_BASELINE },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_MAIN,
> FF_PROFILE_H264_MAIN },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH,
> FF_PROFILE_H264_HIGH },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10,
> FF_PROFILE_H264_HIGH_10 },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422,
> FF_PROFILE_H264_HIGH_422 },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE,
> FF_PROFILE_H264_HIGH_444_PREDICTIVE },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA,
> FF_PROFILE_H264_HIGH_10_INTRA },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA,
> FF_PROFILE_H264_HIGH_422_INTRA },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA,
> FF_PROFILE_H264_HIGH_444 }, /* ? presumed match */
> + { V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA,
> FF_PROFILE_H264_CAVLC_444 }, /* ? presumed match */
> + { V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH,
> FF_PROFILE_H264_MULTIVIEW_HIGH },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH,
> FF_PROFILE_H264_STEREO_HIGH },
> + /**
> + { V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE,
> FF_PROFILE_H264_SCALABLE_BASELINE },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH,
> FF_PROFILE_H264_SCALABLE_HIGH },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA,
> FF_PROFILE_H264_SCALABLE_HIGH_INTRA },
> + { V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
> FF_PROFILE_H264_CONSTRAINED_HIGH },
> + */
> };
> int i;
> -
> for (i = 0; i < FF_ARRAY_ELEMS(profile); i++) {
> - if (profile[i].ffmpeg_val == p)
> - return profile[i].v4l2_val;
> + if (profile[i].v4l2_val == p)
> + return profile[i].ffmpeg_val;
> + }
> + return AVERROR(ENOENT);
> +}
> +
> +static inline int ff_h264_level_from_v4l2_h264_level(int p)
> +{
> + static const struct h264_level {
> + int v4l2_val;
> + int ffmpeg_val;
> + /**
> + V4L2_MPEG_VIDEO_H264_ profile constants from linux/v4l2_controls.h
> via linux/videodev2.h
> + NOTE: the table from libavcodec/h264_levels.c (not .h so we
> cannot include anything) :-
> + H.264 table A-1.
> + static const H264LevelDescriptor h264_levels[] = {
> + Name MaxMBPS MaxBR
> MinCR
> + | level_idc | MaxFS | MaxCPB
> | MaxMvsPer2Mb
> + | | cs3f | | MaxDpbMbs | | MaxVmvR
> | |
> + { "1", 10, 0, 1485, 99, 396, 64, 175,
> 64, 2, 0 },
> + { "1b", 11, 1, 1485, 99, 396, 128, 350,
> 64, 2, 0 },
> + { "1b", 9, 0, 1485, 99, 396, 128, 350,
> 64, 2, 0 },
> + { "1.1", 11, 0, 3000, 396, 900, 192, 500,
> 128, 2, 0 },
> + { "1.2", 12, 0, 6000, 396, 2376, 384, 1000,
> 128, 2, 0 },
> + { "1.3", 13, 0, 11880, 396, 2376, 768, 2000,
> 128, 2, 0 },
> + { "2", 20, 0, 11880, 396, 2376, 2000, 2000,
> 128, 2, 0 },
> + { "2.1", 21, 0, 19800, 792, 4752, 4000, 4000,
> 256, 2, 0 },
> + { "2.2", 22, 0, 20250, 1620, 8100, 4000, 4000,
> 256, 2, 0 },
> + { "3", 30, 0, 40500, 1620, 8100, 10000, 10000,
> 256, 2, 32 },
> + { "3.1", 31, 0, 108000, 3600, 18000, 14000, 14000,
> 512, 4, 16 },
> + { "3.2", 32, 0, 216000, 5120, 20480, 20000, 20000,
> 512, 4, 16 },
> + { "4", 40, 0, 245760, 8192, 32768, 20000, 25000,
> 512, 4, 16 },
> + { "4.1", 41, 0, 245760, 8192, 32768, 50000, 62500,
> 512, 2, 16 },
> + { "4.2", 42, 0, 522240, 8704, 34816, 50000, 62500,
> 512, 2, 16 },
> + { "5", 50, 0, 589824, 22080, 110400, 135000, 135000,
> 512, 2, 16 },
> + { "5.1", 51, 0, 983040, 36864, 184320, 240000, 240000,
> 512, 2, 16 },
> + { "5.2", 52, 0, 2073600, 36864, 184320, 240000, 240000,
> 512, 2, 16 },
> + { "6", 60, 0, 4177920, 139264, 696320, 240000, 240000,
> 8192, 2, 16 },
> + { "6.1", 61, 0, 8355840, 139264, 696320, 480000, 480000,
> 8192, 2, 16 },
> + { "6.2", 62, 0, 16711680, 139264, 696320, 800000, 800000,
> 8192, 2, 16 },
> + };
> + NOTE mediainfo uses Android.Media.MediaCodecProfileLevel anmd
> they are different :(
> +
> https://developer.android.com/reference/android/media/MediaCodecInfo.CodecProfileLevel
> + */
> + } level[] = {
> + /**
> + hard-coded libx264 FF numbers since no .h currently has them
> + */
> + { V4L2_MPEG_VIDEO_H264_LEVEL_1_0, 10 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_1B, 11 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_1_1, 11 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_1_2, 12 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_1_3, 13 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_2_0, 20 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_2_1, 21 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_2_2, 22 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_3_0, 30 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_3_1, 31 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_3_2, 32 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_4_0, 40 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_4_1, 41 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_4_2, 42 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_5_0, 50 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_5_1, 51 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_5_2, 52 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_6_0, 60 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_6_1, 61 },
> + { V4L2_MPEG_VIDEO_H264_LEVEL_6_2, 62 },
> + };
> + int i;
> + for (i = 0; i < FF_ARRAY_ELEMS(level); i++) {
> + if (level[i].v4l2_val == p)
> + return level[i].ffmpeg_val;
> }
> return AVERROR(ENOENT);
> }
> @@ -147,140 +243,485 @@ static inline int v4l2_mpeg4_profile_from_ff(int p)
>
> static int v4l2_check_b_frame_support(V4L2m2mContext *s)
> {
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting
> v4l2_check_b_frame_support "
> + "attempting to ioctl GET number of b-frames\n");
> + /**
> +
> https://github.com/raspberrypi/linux/issues/4917#issuecomment-1058120137
> + B frames are not supported, and only 1 reference frame is ever
> used.
> + */
> if (s->avctx->max_b_frames)
> - av_log(s->avctx, AV_LOG_WARNING, "Encoder does not support
> b-frames yet\n");
> -
> - v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames", 0);
> - v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames,
> "number of B-frames", 0);
> - if (s->avctx->max_b_frames == 0)
> + av_log(s->avctx, AV_LOG_WARNING, "Encoder v4l2_m2m WARNING does
> not support b-frames for V4L2 encoding\n");
> + v4l2_set_ext_ctrl(s, MPEG_CID(B_FRAMES), 0, "number of B-frames", 1);
> + v4l2_get_ext_ctrl(s, MPEG_CID(B_FRAMES), &s->avctx->max_b_frames,
> "number of B-frames", 1);
> + if (s->avctx->max_b_frames == 0) {
> return 0;
> -
> - avpriv_report_missing_feature(s->avctx, "DTS/PTS calculation for V4L2
> encoding");
> -
> + }
> + /* ??? was: avpriv_report_missing_feature(s->avctx, "Encoder DTS/PTS
> calculation for V4L2 encoding"); */
> + avpriv_report_missing_feature(s->avctx, "Encoder v4l2_m2m does not
> support b-frames for V4L2 encoding\n");
> return AVERROR_PATCHWELCOME;
> }
>
> -static inline void v4l2_subscribe_eos_event(V4L2m2mContext *s)
> +static int v4l2_subscribe_eos_event(V4L2m2mContext *s)
> {
> struct v4l2_event_subscription sub;
> -
> + int ret;
> + av_log(s->avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> v4l2_subscribe_eos_event attempting to "
> + "ioctl VIDIOC_SUBSCRIBE_EVENT\n");
> memset(&sub, 0, sizeof(sub));
> sub.type = V4L2_EVENT_EOS;
> - if (ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0)
> - av_log(s->avctx, AV_LOG_WARNING,
> - "the v4l2 driver does not support end of stream
> VIDIOC_SUBSCRIBE_EVENT\n");
> + ret = (ioctl(s->fd, VIDIOC_SUBSCRIBE_EVENT, &sub));
> + if (ret < 0) {
> + av_log(s->avctx, AV_LOG_ERROR,
> + "Encoder v4l2_m2m the v4l2 driver does not support end of
> stream VIDIOC_SUBSCRIBE_EVENT\n");
> + return ret;
> + }
> + return 0;
> }
>
> static int v4l2_prepare_encoder(V4L2m2mContext *s)
> {
> AVCodecContext *avctx = s->avctx;
> + V4L2m2mPriv *priv = avctx->priv_data; /* for per-codec private
> options; eg h264_profile etc when h264 */
> int qmin_cid, qmax_cid, qmin, qmax;
> - int ret, val;
> -
> + int ret, val, ival;
> /**
> * requirements
> */
> - v4l2_subscribe_eos_event(s);
> -
> + ret = v4l2_subscribe_eos_event(s);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> v4l2_subscribe_eos_event, "
> + "return code ret=(%d) errno=(%d) error='%s'\n",
> + ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + /**
> +
> https://github.com/raspberrypi/linux/issues/4917#issuecomment-1058120137
> + B frames are not supported, and only 1 reference frame is ever
> used.
> + */
> ret = v4l2_check_b_frame_support(s);
> - if (ret)
> - return ret;
> -
> + if (ret) {
> + av_log(avctx, AV_LOG_WARNING, "Encoder v4l2_m2m WARNING this
> encoder does not support b-frames") ;
> + ///return ret;
> + }
> + av_log(s->avctx, AV_LOG_WARNING, "Encoder v4l2_m2m WARNING some
> hardware encoders do not support "
> + "Frame Level Rate Control for V4L2 encoding; FLRC ignored.\n");
> + /*
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(FRAME_RC_ENABLE), 0,
> + "frame level rate control", 1); /// 1=enable, 0=disable;
> always disable(0)
> + if (ret) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m failed to globally
> disable frame level rate "
> + "control is OK. ret=(%d) errno=(%d) error='%s'\n", ret,
> errno, strerror(errno));
> + ///return AVERROR(EINVAL);
> + }
> + */
> /**
> - * settingss
> + Codec-specific processing, eg h264, mpeg4, hevc(hevc), h263, vp8.
> + Code is repeated but not always identical in each block, to ensure
> flexibility
> + at the expense of dupication and elegance; some people don't like
> that.
> */
> - if (avctx->framerate.num || avctx->framerate.den)
> - v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> -
> - /* set ext ctrls */
> - v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE),
> MPEG_VIDEO(HEADER_MODE_SEPARATE), "header mode", 0);
> - v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE) , avctx->bit_rate, "bit rate",
> 1);
> - v4l2_set_ext_ctrl(s, MPEG_CID(FRAME_RC_ENABLE), 1, "frame level rate
> control", 0);
> - v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,"gop size",
> 1);
> -
> - av_log(avctx, AV_LOG_DEBUG,
> - "Encoder Context: id (%d), profile (%d), frame rate(%d/%d),
> number b-frames (%d), "
> - "gop size (%d), bit rate (%"PRId64"), qmin (%d), qmax (%d)\n",
> - avctx->codec_id, avctx->profile, avctx->framerate.num,
> avctx->framerate.den,
> - avctx->max_b_frames, avctx->gop_size, avctx->bit_rate,
> avctx->qmin, avctx->qmax);
> -
> switch (avctx->codec_id) {
> case AV_CODEC_ID_H264:
> - if (avctx->profile != FF_PROFILE_UNKNOWN) {
> - val = v4l2_h264_profile_from_ff(avctx->profile);
> - if (val < 0)
> - av_log(avctx, AV_LOG_WARNING, "h264 profile not found\n");
> - else
> - v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE), val, "h264
> profile", 1);
> + /**
> + By now, the h264-specific profile and level and mode have been
> automatically
> + translated via Option values into h264 numeric values
> understood by the driver.
> + see h264-specific options
> + Back-fill some context with FFmpeg constant values,
> + to be compatible with previous version of encoder
> + */
> + val = ff_h264_profile_from_v4l2_h264_profile(priv->h264_profile);
> + if (val < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR profile
> not translated to FF-h264: "
> + "profile not found (%d)\n",priv->h264_profile);
> + return AVERROR(EINVAL);
> + } else {
> + avctx->profile = val;
> + }
> + val = ff_h264_level_from_v4l2_h264_level(priv->h264_level);
> + if (val < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR level not
> translated to H.264 table A-1: "
> + "level not found (%d)\n",priv->h264_level);
> + return AVERROR(EINVAL);
> + } else {
> + avctx->level = val;
> + }
> + if (priv->h264_video_bit_rate_mode ==
> V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
> + priv->h264_profile == V4L2_MPEG_VIDEO_H264_PROFILE_HIGH) {
> + av_log(avctx, AV_LOG_WARNING, "Encoder v4l2_m2m WARNING
> h264_video_bit_rate_mode 'CBR' "
> + "and profile 'high' may not be compatible. Try profile
> 'main' if it fails.\n");
> + ///return AVERROR(EINVAL);
> + }
> + if (priv->h264_qmin > priv->h264_qmax) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> (h264_only) priv->h264_qmin:(%d) "
> + "must be <= h264_qmax (%d)\n", priv->h264_qmin,
> priv->h264_qmax);
> + return AVERROR(EINVAL);
> + }
> + if (priv->h264_gop_size > 0) {
> + priv->h264_iframe_period = priv->h264_gop_size;
> + } else { /* fill in missing gop with h264_iframe_period which has
> a valid default */
> + if (priv->h264_iframe_period >=0 ) {
> + priv->h264_gop_size = priv->h264_iframe_period;
> + }
> + }
> + avctx->gop_size = priv->h264_gop_size;
> + avctx->bit_rate = priv->h264_video_bit_rate;
> + avctx->qmin = priv->h264_qmin;
> + avctx->qmax = priv->h264_qmax;
> + /* Show retrieved Option values */
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (global)
> codec_id:(%d)\n", avctx->codec_id);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_profile:(%d)\n", priv->h264_profile);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_level:(%d)\n", priv->h264_level);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (global)
> max_b_frames:(%d)\n", avctx->max_b_frames);
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (global)
> framerate.den:(%d)\n", avctx->framerate.den);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (global)
> framerate.num:(%d)\n", avctx->framerate.num);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (global)
> framerate.num:(%d/%d)\n", avctx->framerate.num,
> + avctx->framerate.den);
> + }
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m (h264_only)
> priv->h264_video_bit_rate_mode:(%d) "
> + "is one of VBR(%d) CBR(%d) CQ(%d)\n",
> + priv->h264_video_bit_rate_mode,
> + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
> V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, V4L2_MPEG_VIDEO_BITRATE_MODE_CQ);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_video_bit_rate:(%"PRId64")\n",
> priv->h264_video_bit_rate);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_qmin:(%d)\n", priv->h264_qmin);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_qmax:(%d)\n", priv->h264_qmax);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_sequence_header_mode:(%d)\n",
> priv->h264_sequence_header_mode);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_repeat_seq_header:(%d)\n",
> priv->h264_repeat_seq_header);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_iframe_period:(%d)\n", priv->h264_iframe_period);
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m (h264_only)
> priv->h264_gop_size:(%d)\n", priv->h264_gop_size);
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to
> set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + ret = v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set timeperframe (%d/%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + avctx->framerate.num, avctx->framerate.den, ret,
> errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + }
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(H264_PROFILE),
> priv->h264_profile, "h264 profile", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> set h264_profile "
> + "(%d) ret=(%d) errno=(%d) error='%s'\n'high' is possibly
> the max h264 profile on this hardware\n",
> + priv->h264_profile, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(H264_LEVEL),
> priv->h264_level, "h264 Level", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> set h264_level (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n'4.2' is possibly the
> max h264 level on this hardware\n",
> + priv->h264_level, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE_MODE),
> priv->h264_video_bit_rate_mode,
> + "h264 Bitrate Mode, VBR(0) or CBR(1) or CQ(2)", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> set h264_video_bit_rate_mode (%d); "
> + "VBR=(%d) CBR=(%d) CQ=(%d) ret=(%d) errno=(%d)
> error='%s'\n",
> + priv->h264_video_bit_rate_mode,
> + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
> V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, V4L2_MPEG_VIDEO_BITRATE_MODE_CQ,
> + ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + /* cast to int64_t to (int) used by v4l2_set_ext_ctrl */
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(BITRATE), (int)
> priv->h264_video_bit_rate, "h264 Video Bitrate", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> set h264_video_bit_rate (%"PRId64") "
> + "ret=(%d) errno=(%d) error='%s'\n",
> priv->h264_video_bit_rate, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + /**
> + V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE
> + Indicates in what conditions the encoder should skip frames.
> + If encoding a frame would cause the encoded stream to be
> larger than
> + a chosen data limit then the frame will be skipped.
> + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED Frame skip mode is
> disabled.
> + */
> + if (priv->h264_frame_skip_mode_encoder !=
> V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED) {
> + av_log(avctx, AV_LOG_WARNING, "Encoder v4l2_m2m WARNING "
> + "frame skip mode for encoder set to NON-disabled (%d)\n",
> + priv->h264_frame_skip_mode_encoder);
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m "
> + "frame skip mode for encoder set to disabled (%d)\n",
> + priv->h264_frame_skip_mode_encoder);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(FRAME_SKIP_MODE),
> priv->h264_frame_skip_mode_encoder,
> + "frame skip mode for encoder", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_WARNING, "Encoder v4l2_m2m WARNING
> failed to set "
> + "frame skip mode for encoder (%d); ret=(%d) errno=(%d)
> error='%s'\n",
> + priv->h264_frame_skip_mode_encoder, ret, errno,
> strerror(errno));
> + ///return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(HEADER_MODE),
> priv->h264_sequence_header_mode,
> + "sequence header mode", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> set h264_sequence_header_mode (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + priv->h264_sequence_header_mode, ret, errno,
> strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(REPEAT_SEQ_HEADER),
> priv->h264_repeat_seq_header,
> + "repeat sequence header", 1); /* RPi repositories build
> with 1(True) */
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR failed to
> set h264_repeat_seq_header (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + priv->h264_repeat_seq_header, ret, errno,
> strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + /**
> + h264_iframe_period instead of gop size per
> +
> https://github.com/raspberrypi/linux/issues/4917#issuecomment-1057977916
> + */
> + if (priv->h264_iframe_period >=0) {
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(H264_I_PERIOD),
> priv->h264_iframe_period,
> + "Period between I-frames", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> failed to set h264_iframe_period(/gop) (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + priv->h264_iframe_period, ret, errno,
> strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + } else {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> h264_iframe_period(/gop) "
> + "defaulted to not set (%d)\n",
> + priv->h264_iframe_period);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(GOP_SIZE), avctx->gop_size,
> "h264 gop size", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m WARNING failed
> to set h264_gop_size (%d) "
> + "is OK (not used by driver anyway) ret=(%d) errno=(%d)
> error='%s'\n",
> + priv->h264_gop_size, ret, errno, strerror(errno));
> + ///return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(H264_MIN_QP),
> priv->h264_qmin, "h264 Qmin", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ERROR failed to
> set h264_qmin (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + priv->h264_qmin, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(H264_MAX_QP),
> priv->h264_qmax, "h264 Qmax", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ERROR failed to
> set h264_qmax (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + priv->h264_qmax, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> }
> - qmin_cid = MPEG_CID(H264_MIN_QP);
> - qmax_cid = MPEG_CID(H264_MAX_QP);
> - qmin = 0;
> - qmax = 51;
> break;
> case AV_CODEC_ID_MPEG4:
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to
> set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + ret = v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set timeperframe (%d/%d) "
> + "val=(%d) errno=(%d) error='%s'\n",
> + avctx->framerate.num, avctx->framerate.den, val,
> errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + }
> + }
> if (avctx->profile != FF_PROFILE_UNKNOWN) {
> - val = v4l2_mpeg4_profile_from_ff(avctx->profile);
> - if (val < 0)
> - av_log(avctx, AV_LOG_WARNING, "mpeg4 profile not
> found\n");
> - else
> - v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), val, "mpeg4
> profile", 1);
> + ival = v4l2_mpeg4_profile_from_ff(avctx->profile);
> + if (ival < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR mpeg4
> profile not found/translated "
> + "from FF to V42L profile; FF=(%d)\n", avctx->profile);
> + return AVERROR(EINVAL);
> + } else {
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_PROFILE), ival,
> "mpeg4 profile", 1);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set mpeg4 V42L profile (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + ival, ret, errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set mpeg4 profile (%d)\n", ival);
> + }
> + }
> + }
> + if (avctx->flags & AV_CODEC_FLAG_QPEL) {
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "mpeg4
> qpel", 1); /* hard-code 1 as ON */
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> failed to set mpeg4_qpel to 1 "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m mpeg4_qpel
> flag not specified, ignored");
> }
> qmin_cid = MPEG_CID(MPEG4_MIN_QP);
> qmax_cid = MPEG_CID(MPEG4_MAX_QP);
> - if (avctx->flags & AV_CODEC_FLAG_QPEL)
> - v4l2_set_ext_ctrl(s, MPEG_CID(MPEG4_QPEL), 1, "qpel", 1);
> qmin = 1;
> qmax = 31;
> break;
> + case AV_CODEC_ID_HEVC:
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to
> set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + ret = v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set timeperframe (%d/%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + avctx->framerate.num, avctx->framerate.den, ret,
> errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + }
> + }
> + /* no qmin/qmax processing for HEVC */
> + break;
> case AV_CODEC_ID_H263:
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to
> set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + ret = v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set timeperframe (%d/%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + avctx->framerate.num, avctx->framerate.den, ret,
> errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + }
> + }
> qmin_cid = MPEG_CID(H263_MIN_QP);
> qmax_cid = MPEG_CID(H263_MAX_QP);
> qmin = 1;
> qmax = 31;
> break;
> case AV_CODEC_ID_VP8:
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to
> set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + ret = v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set timeperframe (%d/%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + avctx->framerate.num, avctx->framerate.den, ret,
> errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + }
> + }
> qmin_cid = MPEG_CID(VPX_MIN_QP);
> qmax_cid = MPEG_CID(VPX_MAX_QP);
> qmin = 0;
> qmax = 127;
> break;
> case AV_CODEC_ID_VP9:
> + if (avctx->framerate.num || avctx->framerate.den) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m attempting to
> set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + ret = v4l2_set_timeperframe(s, avctx->framerate.den,
> avctx->framerate.num);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> Failed to set timeperframe (%d/%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + avctx->framerate.num, avctx->framerate.den, ret,
> errno, strerror(errno));
> + return errno;
> + } else {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> successfully set timeperframe (%d/%d)\n",
> + avctx->framerate.num, avctx->framerate.den);
> + }
> + }
> qmin_cid = MPEG_CID(VPX_MIN_QP);
> qmax_cid = MPEG_CID(VPX_MAX_QP);
> qmin = 0;
> qmax = 255;
> break;
> default:
> - return 0;
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR UNRECOGNISED
> CODEC ID (%d)\n", avctx->codec_id);
> + return AVERROR(EINVAL);
> }
>
> - if (avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin >
> avctx->qmax) {
> - av_log(avctx, AV_LOG_WARNING, "Invalid qmin:%d qmax:%d. qmin
> should not "
> - "exceed qmax\n", avctx->qmin,
> avctx->qmax);
> - } else {
> - qmin = avctx->qmin >= 0 ? avctx->qmin : qmin;
> - qmax = avctx->qmax >= 0 ? avctx->qmax : qmax;
> + if ( /* common qmin/qmax does not apply to H264 (own processing) nor
> HEVC (per legacy code) */
> + avctx->codec_id == AV_CODEC_ID_MPEG4 ||
> + avctx->codec_id == AV_CODEC_ID_H263 ||
> + avctx->codec_id == AV_CODEC_ID_VP8 ||
> + avctx->codec_id == AV_CODEC_ID_VP9
> + ) {
> + if (avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin >
> avctx->qmax) {
> + av_log(avctx, AV_LOG_WARNING, "Invalid qmin:%d qmax:%d. qmin
> should not "
> + "exceed qmax\n", avctx->qmin,
> avctx->qmax);
> + } else {
> + qmin = avctx->qmin >= 0 ? avctx->qmin : qmin;
> + qmax = avctx->qmax >= 0 ? avctx->qmax : qmax;
> + }
> + ret = v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video
> quantizer scale",
> + avctx->qmin >= 0);
> + if (ret) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ERROR failed to
> set qmin (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + qmin, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> + ret = v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video
> quantizer scale",
> + avctx->qmax >= 0);
> + if (ret) {
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m ERROR failed to
> set qmax (%d) "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + qmax, ret, errno, strerror(errno));
> + return AVERROR(EINVAL);
> + }
> }
> -
> - v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale",
> - avctx->qmin >= 0);
> - v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale",
> - avctx->qmax >= 0);
> -
> return 0;
> }
>
> static int v4l2_send_frame(AVCodecContext *avctx, const AVFrame *frame)
> {
> + int ret;
> +
> V4L2m2mContext *s = ((V4L2m2mPriv*)avctx->priv_data)->context;
> V4L2Context *const output = &s->output;
> -
> + static const int Force_a_key_frame_for_the_next_queued_buffer = 0; /*
> not force for the next queued buffer */
> #ifdef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME
> - if (frame && frame->pict_type == AV_PICTURE_TYPE_I)
> - v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME), 0, "force key
> frame", 1);
> + /**
> +
> https://www.kernel.org/doc/html/latest/userspace-api/media/v4l/ext-ctrls-codec.html
> + V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (button)
> + When 1, Force a key frame for the next queued buffer.
> + When 0, do not Force a key frame for the next queued buffer.
> + */
> + if (frame && frame->pict_type == AV_PICTURE_TYPE_I) {
> + ret = v4l2_set_ext_ctrl(s, MPEG_CID(FORCE_KEY_FRAME),
> Force_a_key_frame_for_the_next_queued_buffer,
> + "force key frame (0=no, 1=yes)", 1);
> + if (ret) {
> + /*
> + av_log(avctx, AV_LOG_WARNING, "Encoder v4l2_m2m
> V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME is defined, "
> + "am processing an IFRAME, FAILED SET force a key frame for
> the next queued buffer as (0=off 1=on): %d "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + Force_a_key_frame_for_the_next_queued_buffer, ret, errno,
> strerror(errno));
> + ///return AVERROR(EINVAL);
> + */
> + } else {
> + /*
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME is defined, "
> + "am processing an IFRAME, successful SET force a key frame
> for the next queued buffer as (0=off 1=on): %d\n",
> + Force_a_key_frame_for_the_next_queued_buffer);
> + */
> + }
> + } else {
> + /*
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME is defined, "
> + "am NOT processing an IFRAME, hence did not set
> FORCE_KEY_FRAME for the next queued buffer\n");
> + */
> + }
> +#endif
> +#ifndef V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME
> + /*
> + av_log(avctx, AV_LOG_DEBUG, "Encoder v4l2_m2m
> V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME is not defined "
> + "... so NOT forcing a key frame for the next queued buffer
> even if we wanted to.\n");
> + */
> #endif
> -
> return ff_v4l2_context_enqueue_frame(output, frame);
> }
>
> @@ -291,30 +732,33 @@ static int v4l2_receive_packet(AVCodecContext
> *avctx, AVPacket *avpkt)
> V4L2Context *const output = &s->output;
> AVFrame *frame = s->frame;
> int ret;
> -
> if (s->draining)
> goto dequeue;
>
> if (!frame->buf[0]) {
> ret = ff_encode_get_frame(avctx, frame);
> - if (ret < 0 && ret != AVERROR_EOF)
> + if (ret < 0 && ret != AVERROR_EOF) {
> return ret;
> -
> - if (ret == AVERROR_EOF)
> + }
> + if (ret == AVERROR_EOF) {
> frame = NULL;
> + }
> }
>
> ret = v4l2_send_frame(avctx, frame);
> - if (ret != AVERROR(EAGAIN))
> + if (ret != AVERROR(EAGAIN)) {
> av_frame_unref(frame);
> -
> - if (ret < 0 && ret != AVERROR(EAGAIN))
> + }
> + if (ret < 0 && ret != AVERROR(EAGAIN)) {
> return ret;
> + }
>
> if (!output->streamon) {
> ret = ff_v4l2_context_set_status(output, VIDIOC_STREAMON);
> if (ret) {
> - av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on output
> context\n");
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR
> VIDIOC_STREAMON failed on "
> + "output context ret=(%d) errno=(%d) error='%s'\n",
> + ret, errno, strerror(errno));
> return ret;
> }
> }
> @@ -322,7 +766,10 @@ static int v4l2_receive_packet(AVCodecContext *avctx,
> AVPacket *avpkt)
> if (!capture->streamon) {
> ret = ff_v4l2_context_set_status(capture, VIDIOC_STREAMON);
> if (ret) {
> - av_log(avctx, AV_LOG_ERROR, "VIDIOC_STREAMON failed on
> capture context\n");
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m
> v4l2_receive_packet "
> + "ERROR: VIDIOC_STREAMON failed on capture context "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + ret, errno, strerror(errno));
> return ret;
> }
> }
> @@ -339,11 +786,13 @@ static av_cold int v4l2_encode_init(AVCodecContext
> *avctx)
> enum AVPixelFormat pix_fmt_output;
> uint32_t v4l2_fmt_output;
> int ret;
> -
> ret = ff_v4l2_m2m_create_context(priv, &s);
> - if (ret < 0)
> + if (ret < 0) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m v4l2_encode_init
> ERROR ff_v4l2_m2m_create_context failed "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + ret, errno, strerror(errno));
> return ret;
> -
> + }
> capture = &s->capture;
> output = &s->output;
>
> @@ -362,7 +811,9 @@ static av_cold int v4l2_encode_init(AVCodecContext
> *avctx)
> s->avctx = avctx;
> ret = ff_v4l2_m2m_codec_init(priv);
> if (ret) {
> - av_log(avctx, AV_LOG_ERROR, "can't configure encoder\n");
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m v4l2_encode_init
> ERROR can't configure encoder "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + ret, errno, strerror(errno));
> return ret;
> }
>
> @@ -374,18 +825,64 @@ static av_cold int v4l2_encode_init(AVCodecContext
> *avctx)
> pix_fmt_output = ff_v4l2_format_v4l2_to_avfmt(v4l2_fmt_output,
> AV_CODEC_ID_RAWVIDEO);
> if (pix_fmt_output != avctx->pix_fmt) {
> const AVPixFmtDescriptor *desc =
> av_pix_fmt_desc_get(pix_fmt_output);
> - av_log(avctx, AV_LOG_ERROR, "Encoder requires %s pixel
> format.\n", desc->name);
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m ERROR requires %s
> pixel format. "
> + "ret=(%d) errno=(%d) error='%s'\n",
> + desc->name, ret, errno, strerror(errno));
> return AVERROR(EINVAL);
> }
> -
> return v4l2_prepare_encoder(s);
> }
>
> static av_cold int v4l2_encode_close(AVCodecContext *avctx)
> {
> - return ff_v4l2_m2m_codec_end(avctx->priv_data);
> + int ret;
> + ret = ff_v4l2_m2m_codec_end(avctx->priv_data);
> + if (ret) {
> + av_log(avctx, AV_LOG_ERROR, "Encoder v4l2_m2m v4l2_encode_close
> ERROR "
> + "ff_v4l2_m2m_codec_end failed ret=(%d) errno=(%d)
> error='%s'\n",
> + ret, errno, strerror(errno));
> + return ret;
> + }
> + return 0;
> }
>
> +/*
> + NOTING: /usr/include/linux/v4l2_controls.h
> + ESPECIALLY noting driver limits from: v4l2-ctl --list-ctrls-menu
> -d 11
> + video_bitrate_mode 0x009909ce (menu) : min=0 max=1
> default=0 value=0 flags=update
> + 0: Variable Bitrate
> + 1: Constant Bitrate
> + video_bitrate 0x009909cf (int) : min=25000
> max=25000000 step=25000 default=10000000 value=10000000
> + sequence_header_mode 0x009909d8 (menu) : min=0 max=1
> default=1 value=1
> + 0: Separate Buffer
> + 1: Joined With 1st Frame
> + repeat_sequence_header 0x009909e2 (bool) : default=0 value=0
> + force_key_frame 0x009909e5 (button) : flags=write-only,
> execute-on-write
> + h264_minimum_qp_value 0x00990a61 (int) : min=0 max=51 step=1
> default=20 value=20
> + h264_maximum_qp_value 0x00990a62 (int) : min=0 max=51 step=1
> default=51 value=51
> + h264_i_frame_period 0x00990a66 (int) : min=0
> max=2147483647 step=1 default=60 value=60
> + h264_level 0x00990a67 (menu) : min=0 max=13
> default=11 value=11
> + 0: 1
> + 1: 1b
> + 2: 1.1
> + 3: 1.2
> + 4: 1.3
> + 5: 2
> + 6: 2.1
> + 7: 2.2
> + 8: 3
> + 9: 3.1
> + 10: 3.2
> + 11: 4
> + 12: 4.1
> + 13: 4.2
> + h264_profile 0x00990a6b (menu) : min=0 max=4
> default=4 value=4
> + 0: Baseline
> + 1: Constrained Baseline
> + 2: Main
> + 4: High
> +*/
> +
> #define OFFSET(x) offsetof(V4L2m2mPriv, x)
> #define FLAGS AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
>
> @@ -394,20 +891,270 @@ static av_cold int v4l2_encode_close(AVCodecContext
> *avctx)
> { "num_capture_buffers", "Number of buffers in the capture context", \
> OFFSET(num_capture_buffers), AV_OPT_TYPE_INT, {.i64 = 4 }, 4,
> INT_MAX, FLAGS }
>
> -static const AVOption mpeg4_options[] = {
> +/**
> + The following V4L_M2M_h264_options are parsed and stored in v4l2_m2m.h
> private typedef struct V4L2m2mPriv
> + All of the code in this encoder DEPENDS on the Options below,
> + "overriding" all "global" options having the same name.
> + i.e. the defaults and allowable values and ranges HERE take
> precendence.
> + Each codec has its own Options with the same names and different
> values.
> + */
> +/**
> + h264 - h264 part 10 AVC
> + */
> +static const AVOption V4L_M2M_h264_options[] = {
> + /**
> + V4L_M2M_CAPTURE_OPTS contains num_output_buffers and
> num_capture_buffers
> + */
> + V4L_M2M_CAPTURE_OPTS,
> + /**
> + per
> https://github.com/raspberrypi/linux/issues/4917#issuecomment-1057977916
> + Profile definitions from avcodec.h and v4l2-controls.h and
> videodev2.h
> + */
> + { "profile", "Profile restrictions, eg -profile high",
> + OFFSET(h264_profile), AV_OPT_TYPE_INT,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH},
> + V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
> + V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
> AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "profile" },
> + { "baseline", "baseline",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "c_baseline", "constrained_baseline",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "main", "main",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_MAIN},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high", "high",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high10", "high 10",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high422", "high 422",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high444", "high 444 Predictive",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high10_intra", "high 10 Intra",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10_INTRA},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high422_intra", "high 422 Intra",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422_INTRA},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "high444_intra", "high 444 Predictive Intra",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_INTRA},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "calv444_intra", "calvc 444 Intra",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_CAVLC_444_INTRA},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "scalable_baseline", "scalable baseline",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_BASELINE},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "scalable_high", "scalable high",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "scalable_high_intra", "scalable high intra",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "stereo_high", "stereo high",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "multiview_high", "multiview high",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + { "constrained_high", "constrained high",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "profile" },
> + /**
> + Profile Level definitions from avcodec.h and v4l2-controls.h and
> videodev2.h
> + */
> + { "level", "Profile Level restrictions, eg 4.2",
> + OFFSET(h264_level), AV_OPT_TYPE_INT,
> {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_4_2},
> + V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
> + V4L2_MPEG_VIDEO_H264_LEVEL_6_2, AV_OPT_FLAG_VIDEO_PARAM |
> AV_OPT_FLAG_ENCODING_PARAM, "level" },
> + { "1", "level 1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_1_0},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "1B", "level 1B",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_1B},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "1.1", "level 1.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_1_1},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "1.2", "level 1.2",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_1_2},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "1.3", "level 1.3",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_1_3},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "2", "level 2",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_2_0},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "2.1", "level 2.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_2_1},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "2.2", "level 2.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_2_2},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "3", "level 3",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_3_0},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "3.1", "level 3.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_3_1},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "3.2", "level 3.2",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_3_2},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "4", "level 4",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_4_0},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "4.1", "level 4.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_4_1},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "4.2", "level 4.2",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_4_2},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "5", "level 5",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_5_0},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "5.1", "level 5.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_5_1},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "5.2", "level 5.2",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_5_2},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "6", "level 6",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_6_0},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "6.1", "level 6.1",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_6_1},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + { "6.2", "level 6.2",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_H264_LEVEL_6_2},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "level" },
> + /**
> + video_bitrate_mode is either CBR or VBR or CQ ... here called eg
> -rc CBR or -rc VBR
> + ... default to VBR, since CBR crashes with:
> + ERROR VIDIOC_STREAMON failed on output context ret=(-3)
> errno=(3 'No such process')
> + */
> + { "rc", "Bitrate mode VBR or CBR or CQ",
> + OFFSET(h264_video_bit_rate_mode), AV_OPT_TYPE_INT,
> {.i64=V4L2_MPEG_VIDEO_BITRATE_MODE_VBR},
> + V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
> + V4L2_MPEG_VIDEO_BITRATE_MODE_CQ, AV_OPT_FLAG_VIDEO_PARAM |
> AV_OPT_FLAG_ENCODING_PARAM, "rc" },
> + { "VBR", "for variable bitrate mode",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_BITRATE_MODE_VBR},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "rc"
> },
> + /**
> + CBR: 1. CBR is not supported in high profile on the Raspberry Pi
> SoC.
> + 2. CBR causes ffmpeg encoder to hang on the Raspberry Pi.
> + */
> + { "CBR", "for constant bitrate mode",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_BITRATE_MODE_CBR},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "rc"
> },
> + /**
> + CQ: https://lkml.org/lkml/2020/5/22/1219
> + When V4L2_CID_MPEG_VIDEO_BITRATE_MODE value is
> V4L2_MPEG_VIDEO_BITRATE_MODE_CQ,
> + encoder will produce constant quality output indicated by
> the
> + V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY control value.
> Encoder will choose
> + appropriate quantization parameter and bitrate to produce
> requested frame quality level.
> + Valid range is 1 to 100 where 1 = lowest quality, 100 =
> highest quality.
> + ** V4L2_CID_MPEG_VIDEO_CONSTANT_QUALITY not dealt with here
> ... since
> + mode V4L2_MPEG_VIDEO_BITRATE_MODE_CQ is not allowed on a
> Raspberry Pi 4.
> + sequence_header_mode is peculiar ... here called -shm
> separate_buffer or -shm joined_1st_frame
> + */
> + { "CQ", "for constant quality mode",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_BITRATE_MODE_CQ},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "rc"
> },
> + { "shm", "Sequence_header_mode",
> + OFFSET(h264_sequence_header_mode), AV_OPT_TYPE_INT,
> {.i64=V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME},
> + V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
> + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
> AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "shm" },
> + { "separate_buffer", "separate_buffer",
> + 0, AV_OPT_TYPE_CONST, {.i64=V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "shm"
> },
> + { "joined_1st_frame", "joined_1st_frame",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "shm"
> },
> + /**
> + Repeat Sequence Headers (Raspberry Pi respositories compile with
> this on)
> + ... here called -rsh 0 or -rsh 0
> + */
> + { "rsh", "repeat sequence header 0(false) 1(true)",
> + OFFSET(h264_repeat_seq_header), AV_OPT_TYPE_BOOL, {.i64=1},
> + 0, 1, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
> + /**
> + Frame Skip Mode here called -fsm.
> V4L2_CID_MPEG_VIDEO_FRAME_SKIP_MODE
> + Indicates in what conditions the encoder should skip frames.
> + If encoding a frame would cause the encoded stream to be larger
> than
> + a chosen data limit then the frame will be skipped.
> + */
> + { "fsme", "Frame Skip Mode for encoder",
> + OFFSET(h264_frame_skip_mode_encoder), AV_OPT_TYPE_INT,
> {.i64=V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED},
> + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED,
> + V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT,
> AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM, "fsme" },
> + { "disabled", "fsme disabled",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_DISABLED},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "fsme" },
> + { "level_limit", "fsme enabled and buffer limit is set by
> the chosen level",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_LEVEL_LIMIT},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "fsme" },
> + { "buffer_limit", "fsme enabled and buffer limit is set by
> CPB (H264) buffer size control",
> + 0, AV_OPT_TYPE_CONST,
> {.i64=V4L2_MPEG_VIDEO_FRAME_SKIP_MODE_BUF_LIMIT},
> + 0, 0, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM,
> "fsme" },
> + /**
> + Video_bitrate -b:v for both CBR and VBR
> + */
> + { "b", "-b:v video_bitrate bits per second",
> + OFFSET(h264_video_bit_rate), AV_OPT_TYPE_INT64, {.i64=10000000},
> + 25000, 25000000, AV_OPT_FLAG_VIDEO_PARAM |
> AV_OPT_FLAG_ENCODING_PARAM },
> + { "qmin", "Minimum quantization parameter for h264",
> + OFFSET(h264_qmin), AV_OPT_TYPE_INT, {.i64=1},
> + 1, 51, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
> + { "qmax", "Maximum quantization parameter for h264",
> + OFFSET(h264_qmax), AV_OPT_TYPE_INT, {.i64=51},
> + 1, 51, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM },
> + /**
> + Since GOP setting is not currently used by the driver,
> + Accept it privately and if specified then allow it to over-ride
> iframe_period
> + */
> + { "g", "gop size, overrides iframe_period used by
> the driver",
> + OFFSET(h264_gop_size), AV_OPT_TYPE_INT, {.i64=-1},
> + -1, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> },
> + /**
> + Allow iframe_period to be used directly (over-ridden by a GOP
> setting):
> + "For H264 there is V4L2_CID_MPEG_VIDEO_H264_I_PERIOD, which is
> what is implemented in the encoder"
> + per
> https://github.com/raspberrypi/linux/issues/4917#issuecomment-1057977916
> + */
> + { "iframe_period", "iframe_period used by the driver;
> overridden by -g",
> + OFFSET(h264_iframe_period), AV_OPT_TYPE_INT, {.i64=25},
> + 0, INT_MAX, AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
> },
> + { NULL },
> +};
> +
> +/**
> + mpeg4 options
> + */
> +static const AVOption V4L_M2M_mpeg4_options[] = {
> V4L_M2M_CAPTURE_OPTS,
> FF_MPEG4_PROFILE_OPTS
> { NULL },
> };
>
> -static const AVOption options[] = {
> +/**
> + Something for num_output_buffers and num_capture_buffers
> + */
> +static const AVOption V4L_M2M_options[] = {
> + /// V4L_M2M_CAPTURE_OPTS contains num_output_buffers and
> num_capture_buffers
> V4L_M2M_CAPTURE_OPTS,
> { NULL },
> };
>
> +/**
> + info at
> https://stackoverflow.com/questions/12648988/converting-a-defined-constant-number-to-a-string
> + */
> +#define STRINGIZE_(x) #x
> +#define STRINGIZE(x) STRINGIZE_(x)
> static const AVCodecDefault v4l2_m2m_defaults[] = {
> - { "qmin", "-1" },
> - { "qmax", "-1" },
> { NULL },
> };
>
> @@ -437,8 +1184,12 @@ static const AVCodecDefault v4l2_m2m_defaults[] = {
> .wrapper_name = "v4l2m2m", \
> }
>
> -M2MENC(mpeg4,"MPEG4", mpeg4_options, AV_CODEC_ID_MPEG4);
> -M2MENC(h263, "H.263", options, AV_CODEC_ID_H263);
> -M2MENC(h264, "H.264", options, AV_CODEC_ID_H264);
> -M2MENC(hevc, "HEVC", options, AV_CODEC_ID_HEVC);
> -M2MENC(vp8, "VP8", options, AV_CODEC_ID_VP8);
> +/**
> + also see v4l2_m2m.h typedef struct V4L2m2mPriv
> + */
> +M2MENC(h264, "H.264", V4L_M2M_h264_options, AV_CODEC_ID_H264);
> +M2MENC(mpeg4, "MPEG4", V4L_M2M_mpeg4_options, AV_CODEC_ID_MPEG4);
> +M2MENC(hevc, "HEVC", V4L_M2M_options, AV_CODEC_ID_HEVC);
> +M2MENC(vp8, "VP8", V4L_M2M_options, AV_CODEC_ID_VP8);
> +M2MENC(vp9, "VP9", V4L_M2M_options, AV_CODEC_ID_VP9);
> +M2MENC(h263, "H.263", V4L_M2M_options, AV_CODEC_ID_H263);
> --
> 2.30.2
>
> _______________________________________________
Hello,
Any updates on this patch set?
>
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 2+ messages in thread