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