diff mbox series

[FFmpeg-devel] Add and use cli options for v4l2 encoder=h264_v4l2m2m

Message ID !&!AAAAAAAAAAAYAAAAAAAAAPVpP4J8X/dMswzQa6NNRVPCgAAAEAAAAGjq5t3mT9BFlRBSENs1HQ4BAAAAAA==@gmail.com
State New
Headers show
Series [FFmpeg-devel] Add and use cli options for v4l2 encoder=h264_v4l2m2m | expand

Commit Message

hydra3333@gmail.com March 13, 2022, 3:34 p.m. UTC
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(-)

     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);

Comments

Dennis Mungai March 22, 2022, 5:35 p.m. UTC | #1
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?

>
diff mbox series

Patch

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
-