diff mbox series

[FFmpeg-devel] avcodec/v4l2_m2m_enc: Support changing qmin/qmax

Message ID 20200119081146.11669-1-andriy.gelman@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel] avcodec/v4l2_m2m_enc: Support changing qmin/qmax
Related show

Checks

Context Check Description
andriy/ffmpeg-patchwork pending
andriy/ffmpeg-patchwork success Applied patch
andriy/ffmpeg-patchwork success Configure finished
andriy/ffmpeg-patchwork success Make finished
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Andriy Gelman Jan. 19, 2020, 8:11 a.m. UTC
From: Andriy Gelman <andriy.gelman@gmail.com>

Hard coded parameters for qmin/qmax are currently used to initialize
v4l2_m2m device. This commit uses values from avctx->{qmin,qmax} if they
are set.

Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
---
 libavcodec/v4l2_m2m_enc.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

Comments

Gyan Doshi Jan. 19, 2020, 11:14 a.m. UTC | #1
On 19-01-2020 01:41 pm, Andriy Gelman wrote:
> From: Andriy Gelman <andriy.gelman@gmail.com>
>
> Hard coded parameters for qmin/qmax are currently used to initialize
> v4l2_m2m device. This commit uses values from avctx->{qmin,qmax} if they
> are set.
>
> Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
> ---
>   libavcodec/v4l2_m2m_enc.c | 15 +++++++++++++--
>   1 file changed, 13 insertions(+), 2 deletions(-)
>
> diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
> index 8059e3bb48f..b61d48316ee 100644
> --- a/libavcodec/v4l2_m2m_enc.c
> +++ b/libavcodec/v4l2_m2m_enc.c
> @@ -31,6 +31,7 @@
>   #include "v4l2_context.h"
>   #include "v4l2_m2m.h"
>   #include "v4l2_fmt.h"
> +#include "internal.h"
>   
>   #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
>   #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
> @@ -232,8 +233,11 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s)
>           return 0;
>       }
>   
> -    if (qmin != avctx->qmin || qmax != avctx->qmax)
> -        av_log(avctx, AV_LOG_WARNING, "Encoder adjusted: qmin (%d), qmax (%d)\n", qmin, qmax);
> +    if (avctx->qmin >= 0)
> +        qmin = avctx->qmin;
> +
> +    if (avctx->qmax >= 0)
> +        qmax = avctx->qmax;

Each encoder has its own range and thus scale. You should clip as per 
the range and document the ranges per codec.
One possible complication is that avctx qmin can go upto 69 but VP8/9's 
range extends beyond that. If qmin > 69 is
legal for VP8/9, then the user won't be able to set it. We can either 
extend the range in avctx or add a private option here.

>   
>       v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale");
>       v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale");
> @@ -349,6 +353,12 @@ static const AVOption options[] = {
>       { NULL },
>   };
>   
> +static const AVCodecDefault v4l2_m2m_defaults[] = {
> +    { "qmin", "-1" },
> +    { "qmax", "-1" },
> +    { NULL },
> +};
> +
>   #define M2MENC_CLASS(NAME) \
>       static const AVClass v4l2_m2m_ ## NAME ## _enc_class = { \
>           .class_name = #NAME "_v4l2m2m_encoder", \
> @@ -370,6 +380,7 @@ static const AVOption options[] = {
>           .send_frame     = v4l2_send_frame, \
>           .receive_packet = v4l2_receive_packet, \
>           .close          = v4l2_encode_close, \
> +        .defaults       = v4l2_m2m_defaults, \
>           .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \
>           .wrapper_name   = "v4l2m2m", \
>       };

On a related note, I see that an  encoder is defined for hevc and not 
for VP9. However, prepare_encoder has a case for vp9 and not for hevc. 
What am I missing here?

Gyan
Andriy Gelman Jan. 19, 2020, 3:41 p.m. UTC | #2
On Sun, 19. Jan 16:44, Gyan wrote:
> 
> 
> On 19-01-2020 01:41 pm, Andriy Gelman wrote:
> > From: Andriy Gelman <andriy.gelman@gmail.com>
> > 
> > Hard coded parameters for qmin/qmax are currently used to initialize
> > v4l2_m2m device. This commit uses values from avctx->{qmin,qmax} if they
> > are set.
> > 
> > Signed-off-by: Andriy Gelman <andriy.gelman@gmail.com>
> > ---
> >   libavcodec/v4l2_m2m_enc.c | 15 +++++++++++++--
> >   1 file changed, 13 insertions(+), 2 deletions(-)
> > 
> > diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
> > index 8059e3bb48f..b61d48316ee 100644
> > --- a/libavcodec/v4l2_m2m_enc.c
> > +++ b/libavcodec/v4l2_m2m_enc.c
> > @@ -31,6 +31,7 @@
> >   #include "v4l2_context.h"
> >   #include "v4l2_m2m.h"
> >   #include "v4l2_fmt.h"
> > +#include "internal.h"
> >   #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
> >   #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
> > @@ -232,8 +233,11 @@ static int v4l2_prepare_encoder(V4L2m2mContext *s)
> >           return 0;
> >       }
> > -    if (qmin != avctx->qmin || qmax != avctx->qmax)
> > -        av_log(avctx, AV_LOG_WARNING, "Encoder adjusted: qmin (%d), qmax (%d)\n", qmin, qmax);
> > +    if (avctx->qmin >= 0)
> > +        qmin = avctx->qmin;
> > +
> > +    if (avctx->qmax >= 0)
> > +        qmax = avctx->qmax;

> 
> Each encoder has its own range and thus scale. You should clip as per the
> range and document the ranges per codec.

I went back and forth about clipping. Ended up not adding it after looking at
examples from libx264/5.c where they except the values as is. 
I'll add clipping in the next version. 

Btw, I have a feeling that these parameters may not be well supported in
v4l2_m2m. At least when I tested on the Raspi4 and Odroid XU4: On the Raspi4,
the ioctl call was not accepted (with any value of qmin/qmax), on the Odroid XU4
the ioctl call worked but didn't have any effect on the video. 
Of course, there may other devices supporting this feature.

> One possible complication is that avctx qmin can go upto 69 but VP8/9's
> range extends beyond that. If qmin > 69 is
> legal for VP8/9, then the user won't be able to set it. We can either extend
> the range in avctx or add a private option here.
> 

I'm leaning towards modifying the global range as this could also help libvpx.
I'll run some tests.

> >       v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale");
> >       v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale");
> > @@ -349,6 +353,12 @@ static const AVOption options[] = {
> >       { NULL },
> >   };
> > +static const AVCodecDefault v4l2_m2m_defaults[] = {
> > +    { "qmin", "-1" },
> > +    { "qmax", "-1" },
> > +    { NULL },
> > +};
> > +
> >   #define M2MENC_CLASS(NAME) \
> >       static const AVClass v4l2_m2m_ ## NAME ## _enc_class = { \
> >           .class_name = #NAME "_v4l2m2m_encoder", \
> > @@ -370,6 +380,7 @@ static const AVOption options[] = {
> >           .send_frame     = v4l2_send_frame, \
> >           .receive_packet = v4l2_receive_packet, \
> >           .close          = v4l2_encode_close, \
> > +        .defaults       = v4l2_m2m_defaults, \
> >           .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \
> >           .wrapper_name   = "v4l2m2m", \
> >       };

> 
> On a related note, I see that an  encoder is defined for hevc and not for
> VP9. However, prepare_encoder has a case for vp9 and not for hevc. What am I
> missing here?

Yeah, good catch again. For vp9, I'll do some research to see if there are
devices supporting it. We should consider adding it. 

For hevc encoder, I tried to fix missing qmin/qmax case (also missing profile
setup) in the past, but couldn't find a "workable" v4l2_m2m device to test.

One option is Nvidia's Jetson Nano, but there were too many
issues when I tried to implement/test with ffmpeg. It required an extra header
with incompatible license, ioctl calls with libv4l2, broken timestamps, and it
had some weird race conditions.. 
FYI, it looks that Nvidia recently added support for interfacing directly to
NVENC [1], so there is no longer a need to use v4l2_m2m interface (although I haven't
tested NVENC on the Nano yet)
[1] https://developer.nvidia.com/embedded/jetpack

Thanks,
diff mbox series

Patch

diff --git a/libavcodec/v4l2_m2m_enc.c b/libavcodec/v4l2_m2m_enc.c
index 8059e3bb48f..b61d48316ee 100644
--- a/libavcodec/v4l2_m2m_enc.c
+++ b/libavcodec/v4l2_m2m_enc.c
@@ -31,6 +31,7 @@ 
 #include "v4l2_context.h"
 #include "v4l2_m2m.h"
 #include "v4l2_fmt.h"
+#include "internal.h"
 
 #define MPEG_CID(x) V4L2_CID_MPEG_VIDEO_##x
 #define MPEG_VIDEO(x) V4L2_MPEG_VIDEO_##x
@@ -232,8 +233,11 @@  static int v4l2_prepare_encoder(V4L2m2mContext *s)
         return 0;
     }
 
-    if (qmin != avctx->qmin || qmax != avctx->qmax)
-        av_log(avctx, AV_LOG_WARNING, "Encoder adjusted: qmin (%d), qmax (%d)\n", qmin, qmax);
+    if (avctx->qmin >= 0)
+        qmin = avctx->qmin;
+
+    if (avctx->qmax >= 0)
+        qmax = avctx->qmax;
 
     v4l2_set_ext_ctrl(s, qmin_cid, qmin, "minimum video quantizer scale");
     v4l2_set_ext_ctrl(s, qmax_cid, qmax, "maximum video quantizer scale");
@@ -349,6 +353,12 @@  static const AVOption options[] = {
     { NULL },
 };
 
+static const AVCodecDefault v4l2_m2m_defaults[] = {
+    { "qmin", "-1" },
+    { "qmax", "-1" },
+    { NULL },
+};
+
 #define M2MENC_CLASS(NAME) \
     static const AVClass v4l2_m2m_ ## NAME ## _enc_class = { \
         .class_name = #NAME "_v4l2m2m_encoder", \
@@ -370,6 +380,7 @@  static const AVOption options[] = {
         .send_frame     = v4l2_send_frame, \
         .receive_packet = v4l2_receive_packet, \
         .close          = v4l2_encode_close, \
+        .defaults       = v4l2_m2m_defaults, \
         .capabilities   = AV_CODEC_CAP_HARDWARE | AV_CODEC_CAP_DELAY, \
         .wrapper_name   = "v4l2m2m", \
     };