[FFmpeg-devel] [PATCH] Add and use cli options for v4l2 encoder=h264_v4l2m2m
hydra3333 at gmail.com
hydra3333 at gmail.com
Sun Mar 13 17:34:06 EET 2022
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 at 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 at 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
More information about the ffmpeg-devel
mailing list