[FFmpeg-devel] cinepakenc: When rd_frame() doesn't use MC, mark pkt as keyframe and reset curframe

Submitted by Tomas Härdin on Aug. 15, 2019, 6:55 p.m.

Details

Message ID 8fbbdfbb2990d350c5c87cb1d23adf13eb21288e.camel@acc.umu.se
State New
Headers show

Commit Message

Tomas Härdin Aug. 15, 2019, 6:55 p.m.
tor 2019-08-15 klockan 14:20 -0300 skrev James Almer:
> On 8/15/2019 2:13 PM, Tomas Härdin wrote:
> > tor 2019-08-15 klockan 12:54 -0300 skrev James Almer:
> > > On 8/15/2019 11:34 AM, Tomas Härdin wrote:
> > > > 
> > > > Aside: what is -keyint_min for exactly? It seems to be used as a max in
> > > > msvideo1enc.c, but libx264.c and libxavs.c uses it and gop_size as
> > > > minimum and maximum respectively.. Feels like the relevant encoders
> > > > need to be brought into line (assuming it doesn't break user scripts)
> > > 
> > > Afaik, keyint_min is minimum interval/distance between keyframes. So
> > > yes, if it's being used as max distance then it's wrong, as that's what
> > > gop_size is for.
> > 
> > Make sense. But why is gop_size < keyint_min by default then? 12 vs 25.
> 
> I have no idea.
> 
> > I think the x264 people have complained multiple times about the
> > default gop_size setting even
> 
> The libx264 wrapper changes both to -1 in libavcodec, which means x264's
> own defaults will be used instead. So if they complained then i guess it
> was changed accordingly.

Didn't know encoders could have their own defaults. Updated patch
attached. I added some sanity checks on -keyint_min and -g as well.

I couldn't find rl's contact information, hopefully they are subscribed
to this list.

/Tomas

Comments

James Almer Aug. 16, 2019, 2:35 a.m.
On 8/15/2019 3:55 PM, Tomas Härdin wrote:
> tor 2019-08-15 klockan 14:20 -0300 skrev James Almer:
>> On 8/15/2019 2:13 PM, Tomas Härdin wrote:
>>> tor 2019-08-15 klockan 12:54 -0300 skrev James Almer:
>>>> On 8/15/2019 11:34 AM, Tomas Härdin wrote:
>>>>> Aside: what is -keyint_min for exactly? It seems to be used as a max in
>>>>> msvideo1enc.c, but libx264.c and libxavs.c uses it and gop_size as
>>>>> minimum and maximum respectively.. Feels like the relevant encoders
>>>>> need to be brought into line (assuming it doesn't break user scripts)
>>>> Afaik, keyint_min is minimum interval/distance between keyframes. So
>>>> yes, if it's being used as max distance then it's wrong, as that's what
>>>> gop_size is for.
>>> Make sense. But why is gop_size < keyint_min by default then? 12 vs 25.
>> I have no idea.
>>
>>> I think the x264 people have complained multiple times about the
>>> default gop_size setting even
>> The libx264 wrapper changes both to -1 in libavcodec, which means x264's
>> own defaults will be used instead. So if they complained then i guess it
>> was changed accordingly.
> Didn't know encoders could have their own defaults. Updated patch
> attached. I added some sanity checks on -keyint_min and -g as well.
> 
> I couldn't find rl's contact information, hopefully they are subscribed
> to this list.
> 
> /Tomas
> 
> 
> 0001-cinepakenc-When-rd_frame-doesn-t-use-MC-mark-pkt-as-.patch
> 
> From 49d7ac02d35acbf3e30555eef215ae3a15c06684 Mon Sep 17 00:00:00 2001
> From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= <tjoppen@acc.umu.se>
> Date: Thu, 15 Aug 2019 16:20:23 +0200
> Subject: [PATCH] cinepakenc: When rd_frame() doesn't use MC, mark pkt as
>  keyframe and reset curframe
> 
> This allows the encoder to make smarter choices for future frames,
> while still keeping keyframe distances within [keyint_min, gop_size].
> Defaults and checks prevent nonsensical keyint_min / gop_size combinations.
> ---
>  libavcodec/cinepakenc.c | 54 +++++++++++++++++++++++++++++++++--------
>  1 file changed, 44 insertions(+), 10 deletions(-)
> 
> diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c
> index 93917fafe8..396d3e5273 100644
> --- a/libavcodec/cinepakenc.c
> +++ b/libavcodec/cinepakenc.c
> @@ -112,7 +112,7 @@ typedef struct CinepakEncContext {
>      enum AVPixelFormat pix_fmt;
>      int w, h;
>      int frame_buf_size;
> -    int curframe, keyint;
> +    int curframe, keyint_min, keyint_max;
>      AVLFG randctx;
>      uint64_t lambda;
>      int *codebook_input;
> @@ -168,6 +168,16 @@ static av_cold int cinepak_encode_init(AVCodecContext *avctx)
>          return AVERROR(EINVAL);
>      }
>  
> +    if (avctx->keyint_min <= 0) {
> +        av_log(avctx, AV_LOG_ERROR, "-keyint_min must be >= 1\n");
> +        return AVERROR(EINVAL);
> +    }
> +
> +    if (avctx->keyint_min > avctx->gop_size) {
> +        av_log(avctx, AV_LOG_ERROR, "-keyint_min > -g is not allowed. Did you mean -g?\n");

Don't use option names in error messages, and especially not with CLI
formatting. Use keyint_min and gop_size instead, which are the names of
the AVCodecContext fields.

And the suggestion at the end is IMO not really needed.

> +        return AVERROR(EINVAL);
> +    }
> +
>      if (!(s->last_frame = av_frame_alloc()))
>          return AVERROR(ENOMEM);
>      if (!(s->best_frame = av_frame_alloc()))
> @@ -213,7 +223,8 @@ static av_cold int cinepak_encode_init(AVCodecContext *avctx)
>      s->h              = avctx->height;
>      s->frame_buf_size = frame_buf_size;
>      s->curframe       = 0;
> -    s->keyint         = avctx->keyint_min;
> +    s->keyint_min     = avctx->keyint_min;
> +    s->keyint_max     = avctx->gop_size;
>      s->pix_fmt        = avctx->pix_fmt;
>  
>      // set up AVFrames
> @@ -871,7 +882,7 @@ static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe,
>                      uint8_t *last_data[4], int last_linesize[4],
>                      uint8_t *data[4], int linesize[4],
>                      uint8_t *scratch_data[4], int scratch_linesize[4],
> -                    unsigned char *buf, int64_t *best_score)
> +                    unsigned char *buf, int64_t *best_score, CinepakMode *best_mode)
>  {
>      int64_t score = 0;
>      int best_size = 0;
> @@ -970,6 +981,7 @@ static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe,
>  
>                  if (best_size == 0 || score < *best_score) {
>                      *best_score = score;
> +                    *best_mode = mode;
>                      best_size = encode_mode(s, h,
>                                              scratch_data, scratch_linesize,
>                                              last_data, last_linesize, &info,
> @@ -1000,13 +1012,15 @@ static int write_cvid_header(CinepakEncContext *s, unsigned char *buf,
>  }
>  
>  static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
> -                    int isakeyframe, unsigned char *buf, int buf_size)
> +                    int isakeyframe, unsigned char *buf, int buf_size,
> +                    int *has_mc_out)
>  {
>      int num_strips, strip, i, y, nexty, size, temp_size, best_size;
>      uint8_t *last_data    [4], *data    [4], *scratch_data    [4];
>      int      last_linesize[4],  linesize[4],  scratch_linesize[4];
>      int64_t best_score = 0, score, score_temp;
>      int best_nstrips;
> +    int has_mc;
>  
>      if (s->pix_fmt == AV_PIX_FMT_RGB24) {
>          int x;
> @@ -1067,9 +1081,11 @@ static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
>      for (num_strips = s->min_strips; num_strips <= s->max_strips && num_strips <= s->h / MB_SIZE; num_strips++) {
>          score = 0;
>          size  = 0;
> +        has_mc = 0;
>  
>          for (y = 0, strip = 1; y < s->h; strip++, y = nexty) {
>              int strip_height;
> +            CinepakMode mode_temp;
>  
>              nexty = strip * s->h / num_strips; // <= s->h
>              // make nexty the next multiple of 4 if not already there
> @@ -1101,16 +1117,21 @@ static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
>                                        last_data, last_linesize, data, linesize,
>                                        scratch_data, scratch_linesize,
>                                        s->frame_buf + size + CVID_HEADER_SIZE,
> -                                      &score_temp)) < 0)
> +                                      &score_temp, &mode_temp)) < 0)
>                  return temp_size;
>  
>              score += score_temp;
>              size += temp_size;
> +
> +            if (mode_temp == MODE_MC) {
> +                has_mc = 1;
> +            }
>          }
>  
>          if (best_score == 0 || score < best_score) {
>              best_score = score;
> -            best_size = size + write_cvid_header(s, s->frame_buf, num_strips, size, isakeyframe);
> +            *has_mc_out = has_mc;
> +            best_size = size + write_cvid_header(s, s->frame_buf, num_strips, size, !has_mc);
>  
>              FFSWAP(AVFrame *, s->best_frame, s->scratch_frame);
>              memcpy(buf, s->frame_buf, best_size);
> @@ -1152,21 +1173,25 @@ static int cinepak_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
>                                  const AVFrame *frame, int *got_packet)
>  {
>      CinepakEncContext *s = avctx->priv_data;
> -    int ret;
> +    int ret, has_mc;
>  
>      s->lambda = frame->quality ? frame->quality - 1 : 2 * FF_LAMBDA_SCALE;
>  
>      if ((ret = ff_alloc_packet2(avctx, pkt, s->frame_buf_size, 0)) < 0)
>          return ret;
> -    ret       = rd_frame(s, frame, (s->curframe == 0), pkt->data, s->frame_buf_size);
> +    ret       = rd_frame(s, frame, (s->curframe == 0), pkt->data, s->frame_buf_size, &has_mc);
>      pkt->size = ret;
> -    if (s->curframe == 0)
> +    if (s->curframe == 0 || (!has_mc && s->curframe + 1 >= s->keyint_min)) {
> +        //if the packet doesn't use MODE_MC then we
> +        //mark it as a keyframe and reset curframe
> +        s->curframe = 0;
>          pkt->flags |= AV_PKT_FLAG_KEY;
> +    }
>      *got_packet = 1;
>  
>      FFSWAP(AVFrame *, s->last_frame, s->best_frame);
>  
> -    if (++s->curframe >= s->keyint)
> +    if (++s->curframe >= s->keyint_max)
>          s->curframe = 0;
>  
>      return 0;
> @@ -1194,6 +1219,14 @@ static av_cold int cinepak_encode_end(AVCodecContext *avctx)
>      return 0;
>  }
>  
> +static const AVCodecDefault cinepak_defaults[] = {
> +    //-keyint_min 1 means any frame can be a keyframe,
> +    //2 means there's at least one non-keyframe inbetween, and so on
> +    { "keyint_min",       "1" },
> +    { "g",                "25" },
> +    { NULL },
> +};
> +
>  AVCodec ff_cinepak_encoder = {
>      .name           = "cinepak",
>      .long_name      = NULL_IF_CONFIG_SMALL("Cinepak"),
> @@ -1205,4 +1238,5 @@ AVCodec ff_cinepak_encoder = {
>      .close          = cinepak_encode_end,
>      .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE },
>      .priv_class     = &cinepak_class,
> +    .defaults       = cinepak_defaults,
>  };
> -- 2.20.1
> 
> 
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
> 
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
>

Patch hide | download patch | download mbox

From 49d7ac02d35acbf3e30555eef215ae3a15c06684 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Tomas=20H=C3=A4rdin?= <tjoppen@acc.umu.se>
Date: Thu, 15 Aug 2019 16:20:23 +0200
Subject: [PATCH] cinepakenc: When rd_frame() doesn't use MC, mark pkt as
 keyframe and reset curframe

This allows the encoder to make smarter choices for future frames,
while still keeping keyframe distances within [keyint_min, gop_size].
Defaults and checks prevent nonsensical keyint_min / gop_size combinations.
---
 libavcodec/cinepakenc.c | 54 +++++++++++++++++++++++++++++++++--------
 1 file changed, 44 insertions(+), 10 deletions(-)

diff --git a/libavcodec/cinepakenc.c b/libavcodec/cinepakenc.c
index 93917fafe8..396d3e5273 100644
--- a/libavcodec/cinepakenc.c
+++ b/libavcodec/cinepakenc.c
@@ -112,7 +112,7 @@  typedef struct CinepakEncContext {
     enum AVPixelFormat pix_fmt;
     int w, h;
     int frame_buf_size;
-    int curframe, keyint;
+    int curframe, keyint_min, keyint_max;
     AVLFG randctx;
     uint64_t lambda;
     int *codebook_input;
@@ -168,6 +168,16 @@  static av_cold int cinepak_encode_init(AVCodecContext *avctx)
         return AVERROR(EINVAL);
     }
 
+    if (avctx->keyint_min <= 0) {
+        av_log(avctx, AV_LOG_ERROR, "-keyint_min must be >= 1\n");
+        return AVERROR(EINVAL);
+    }
+
+    if (avctx->keyint_min > avctx->gop_size) {
+        av_log(avctx, AV_LOG_ERROR, "-keyint_min > -g is not allowed. Did you mean -g?\n");
+        return AVERROR(EINVAL);
+    }
+
     if (!(s->last_frame = av_frame_alloc()))
         return AVERROR(ENOMEM);
     if (!(s->best_frame = av_frame_alloc()))
@@ -213,7 +223,8 @@  static av_cold int cinepak_encode_init(AVCodecContext *avctx)
     s->h              = avctx->height;
     s->frame_buf_size = frame_buf_size;
     s->curframe       = 0;
-    s->keyint         = avctx->keyint_min;
+    s->keyint_min     = avctx->keyint_min;
+    s->keyint_max     = avctx->gop_size;
     s->pix_fmt        = avctx->pix_fmt;
 
     // set up AVFrames
@@ -871,7 +882,7 @@  static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe,
                     uint8_t *last_data[4], int last_linesize[4],
                     uint8_t *data[4], int linesize[4],
                     uint8_t *scratch_data[4], int scratch_linesize[4],
-                    unsigned char *buf, int64_t *best_score)
+                    unsigned char *buf, int64_t *best_score, CinepakMode *best_mode)
 {
     int64_t score = 0;
     int best_size = 0;
@@ -970,6 +981,7 @@  static int rd_strip(CinepakEncContext *s, int y, int h, int keyframe,
 
                 if (best_size == 0 || score < *best_score) {
                     *best_score = score;
+                    *best_mode = mode;
                     best_size = encode_mode(s, h,
                                             scratch_data, scratch_linesize,
                                             last_data, last_linesize, &info,
@@ -1000,13 +1012,15 @@  static int write_cvid_header(CinepakEncContext *s, unsigned char *buf,
 }
 
 static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
-                    int isakeyframe, unsigned char *buf, int buf_size)
+                    int isakeyframe, unsigned char *buf, int buf_size,
+                    int *has_mc_out)
 {
     int num_strips, strip, i, y, nexty, size, temp_size, best_size;
     uint8_t *last_data    [4], *data    [4], *scratch_data    [4];
     int      last_linesize[4],  linesize[4],  scratch_linesize[4];
     int64_t best_score = 0, score, score_temp;
     int best_nstrips;
+    int has_mc;
 
     if (s->pix_fmt == AV_PIX_FMT_RGB24) {
         int x;
@@ -1067,9 +1081,11 @@  static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
     for (num_strips = s->min_strips; num_strips <= s->max_strips && num_strips <= s->h / MB_SIZE; num_strips++) {
         score = 0;
         size  = 0;
+        has_mc = 0;
 
         for (y = 0, strip = 1; y < s->h; strip++, y = nexty) {
             int strip_height;
+            CinepakMode mode_temp;
 
             nexty = strip * s->h / num_strips; // <= s->h
             // make nexty the next multiple of 4 if not already there
@@ -1101,16 +1117,21 @@  static int rd_frame(CinepakEncContext *s, const AVFrame *frame,
                                       last_data, last_linesize, data, linesize,
                                       scratch_data, scratch_linesize,
                                       s->frame_buf + size + CVID_HEADER_SIZE,
-                                      &score_temp)) < 0)
+                                      &score_temp, &mode_temp)) < 0)
                 return temp_size;
 
             score += score_temp;
             size += temp_size;
+
+            if (mode_temp == MODE_MC) {
+                has_mc = 1;
+            }
         }
 
         if (best_score == 0 || score < best_score) {
             best_score = score;
-            best_size = size + write_cvid_header(s, s->frame_buf, num_strips, size, isakeyframe);
+            *has_mc_out = has_mc;
+            best_size = size + write_cvid_header(s, s->frame_buf, num_strips, size, !has_mc);
 
             FFSWAP(AVFrame *, s->best_frame, s->scratch_frame);
             memcpy(buf, s->frame_buf, best_size);
@@ -1152,21 +1173,25 @@  static int cinepak_encode_frame(AVCodecContext *avctx, AVPacket *pkt,
                                 const AVFrame *frame, int *got_packet)
 {
     CinepakEncContext *s = avctx->priv_data;
-    int ret;
+    int ret, has_mc;
 
     s->lambda = frame->quality ? frame->quality - 1 : 2 * FF_LAMBDA_SCALE;
 
     if ((ret = ff_alloc_packet2(avctx, pkt, s->frame_buf_size, 0)) < 0)
         return ret;
-    ret       = rd_frame(s, frame, (s->curframe == 0), pkt->data, s->frame_buf_size);
+    ret       = rd_frame(s, frame, (s->curframe == 0), pkt->data, s->frame_buf_size, &has_mc);
     pkt->size = ret;
-    if (s->curframe == 0)
+    if (s->curframe == 0 || (!has_mc && s->curframe + 1 >= s->keyint_min)) {
+        //if the packet doesn't use MODE_MC then we
+        //mark it as a keyframe and reset curframe
+        s->curframe = 0;
         pkt->flags |= AV_PKT_FLAG_KEY;
+    }
     *got_packet = 1;
 
     FFSWAP(AVFrame *, s->last_frame, s->best_frame);
 
-    if (++s->curframe >= s->keyint)
+    if (++s->curframe >= s->keyint_max)
         s->curframe = 0;
 
     return 0;
@@ -1194,6 +1219,14 @@  static av_cold int cinepak_encode_end(AVCodecContext *avctx)
     return 0;
 }
 
+static const AVCodecDefault cinepak_defaults[] = {
+    //-keyint_min 1 means any frame can be a keyframe,
+    //2 means there's at least one non-keyframe inbetween, and so on
+    { "keyint_min",       "1" },
+    { "g",                "25" },
+    { NULL },
+};
+
 AVCodec ff_cinepak_encoder = {
     .name           = "cinepak",
     .long_name      = NULL_IF_CONFIG_SMALL("Cinepak"),
@@ -1205,4 +1238,5 @@  AVCodec ff_cinepak_encoder = {
     .close          = cinepak_encode_end,
     .pix_fmts       = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_GRAY8, AV_PIX_FMT_NONE },
     .priv_class     = &cinepak_class,
+    .defaults       = cinepak_defaults,
 };
-- 
2.20.1