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