diff mbox series

[FFmpeg-devel,RFC,4/4] libavcodec/j2kenc: Support for multiple layers

Message ID 20200819122102.5584-4-gautamramk@gmail.com
State Superseded
Headers show
Series [FFmpeg-devel,RFC,1/4] libavcodec/jpeg2000: Make tag tree functions non static | expand

Checks

Context Check Description
andriy/default pending
andriy/make success Make finished
andriy/make_fate fail Make fate failed

Commit Message

Gautam Ramakrishnan Aug. 19, 2020, 12:21 p.m. UTC
From: Gautam Ramakrishnan <gautamramk@gmail.com>

This patch allows setting a compression ratio and to
set multiple layers. The user has to input a compression
ratio for each layer.
The per layer compression ration can be set as follows:
-layer_rates "r1,r2,...rn"
for to create 'n' layers.
---
 libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
 libavcodec/jpeg2000.c |  13 +-
 libavcodec/jpeg2000.h |  10 +
 3 files changed, 384 insertions(+), 82 deletions(-)

Comments

Gautam Ramakrishnan Aug. 19, 2020, 6:31 p.m. UTC | #1
On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
>
> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>
> This patch allows setting a compression ratio and to
> set multiple layers. The user has to input a compression
> ratio for each layer.
> The per layer compression ration can be set as follows:
> -layer_rates "r1,r2,...rn"
> for to create 'n' layers.
> ---
>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
>  libavcodec/jpeg2000.c |  13 +-
>  libavcodec/jpeg2000.h |  10 +
>  3 files changed, 384 insertions(+), 82 deletions(-)
>
> diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c
> index 8699296434..b09db36c14 100644
> --- a/libavcodec/j2kenc.c
> +++ b/libavcodec/j2kenc.c
> @@ -32,6 +32,7 @@
>   * Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe
>   * Copyright (c) 2005, Herve Drolon, FreeImage Team
>   * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
> + * Copyright (c) 2020, Gautam Ramakrishnan <gautamramk@gmail.com>
>   * All rights reserved.
>   *
>   * Redistribution and use in source and binary forms, with or without
> @@ -100,6 +101,7 @@ static const int dwt_norms[2][4][10] = { // [dwt_type][band][rlevel] (multiplied
>
>  typedef struct {
>     Jpeg2000Component *comp;
> +   double *layer_rates;
>  } Jpeg2000Tile;
>
>  typedef struct {
> @@ -126,12 +128,15 @@ typedef struct {
>      Jpeg2000QuantStyle  qntsty;
>
>      Jpeg2000Tile *tile;
> +    int layer_rates[100];
>
>      int format;
>      int pred;
>      int sop;
>      int eph;
>      int prog;
> +    int nlayers;
> +    char *lr_str;
>  } Jpeg2000EncoderContext;
>
>
> @@ -332,7 +337,7 @@ static int put_cod(Jpeg2000EncoderContext *s)
>      bytestream_put_byte(&s->buf, scod);  // Scod
>      // SGcod
>      bytestream_put_byte(&s->buf, s->prog); // progression level
> -    bytestream_put_be16(&s->buf, 1); // num of layers
> +    bytestream_put_be16(&s->buf, s->nlayers); // num of layers
>      if(s->avctx->pix_fmt == AV_PIX_FMT_YUV444P){
>          bytestream_put_byte(&s->buf, 0); // unspecified
>      }else{
> @@ -411,6 +416,31 @@ static uint8_t *put_sot(Jpeg2000EncoderContext *s, int tileno)
>      return psotptr;
>  }
>
> +static void compute_rates(Jpeg2000EncoderContext* s)
> +{
> +    int i, j;
> +    int layno, compno;
> +    for (i = 0; i < s->numYtiles; i++) {
> +        for (j = 0; j < s->numXtiles; j++) {
> +            Jpeg2000Tile *tile = &s->tile[s->numXtiles * i + j];
> +            for (compno = 0; compno < s->ncomponents; compno++) {
> +                int tilew = tile->comp[compno].coord[0][1] - tile->comp[compno].coord[0][0];
> +                int tileh = tile->comp[compno].coord[1][1] - tile->comp[compno].coord[1][0];
> +                int scale = (compno?1 << s->chroma_shift[0]:1) * (compno?1 << s->chroma_shift[1]:1);
> +                for (layno = 0; layno < s->nlayers; layno++) {
> +                    if (s->layer_rates[layno] > 0.0f) {
> +                        tile->layer_rates[layno] += (double)(tilew * tileh) * s->ncomponents * s->cbps[compno] /
> +                                                    (double)(s->layer_rates[layno] * 8 * scale);
> +                    } else {
> +                        tile->layer_rates[layno] = 0.0f;
> +                    }
> +                }
> +            }
> +        }
> +    }
> +
> +}
> +
>  /**
>   * compute the sizes of tiles, resolution levels, bands, etc.
>   * allocate memory for them
> @@ -448,6 +478,10 @@ static int init_tiles(Jpeg2000EncoderContext *s)
>                          for (j = 0; j < 2; j++)
>                              comp->coord[i][j] = comp->coord_o[i][j] = ff_jpeg2000_ceildivpow2(comp->coord[i][j], s->chroma_shift[i]);
>
> +                tile->layer_rates = av_mallocz_array(s->ncomponents, sizeof(*tile->layer_rates));
> +                if (!tile->layer_rates)
> +                    return AVERROR(ENOMEM);
> +
>                  if ((ret = ff_jpeg2000_init_component(comp,
>                                                  codsty,
>                                                  qntsty,
> @@ -459,6 +493,7 @@ static int init_tiles(Jpeg2000EncoderContext *s)
>                      return ret;
>              }
>          }
> +    compute_rates(s);
>      return 0;
>  }
>
> @@ -701,6 +736,8 @@ static void encode_cblk(Jpeg2000EncoderContext *s, Jpeg2000T1Context *t1, Jpeg20
>          }
>
>          cblk->passes[passno].rate = ff_mqc_flush_to(&t1->mqc, cblk->passes[passno].flushed, &cblk->passes[passno].flushed_len);
> +        cblk->passes[passno].rate -= cblk->passes[passno].flushed_len;
> +
>          wmsedec += (int64_t)nmsedec << (2*bpno);
>          cblk->passes[passno].disto = wmsedec;
>
> @@ -733,10 +770,12 @@ static void putnumpasses(Jpeg2000EncoderContext *s, int n)
>  }
>
>
> -static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, int precno,
> -                          uint8_t *expn, int numgbits, int packetno)
> +static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, int layno,
> +                         int precno, uint8_t *expn, int numgbits, int packetno,
> +                         int nlayers)
>  {
>      int bandno, empty = 1;
> +    int i;
>      // init bitstream
>      *s->buf = 0;
>      s->bit_index = 0;
> @@ -748,18 +787,61 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
>      }
>      // header
>
> +    if (!layno) {
> +        for (bandno = 0; bandno < rlevel->nbands; bandno++){
> +            Jpeg2000Band *band = rlevel->band + bandno;
> +            if (rlevel->band[bandno].coord[0][0] < rlevel->band[bandno].coord[0][1]
> +            &&  rlevel->band[bandno].coord[1][0] < rlevel->band[bandno].coord[1][1]){
> +                Jpeg2000Prec *prec = band->prec + precno;
> +                int nb_cblks = prec->nb_codeblocks_height * prec->nb_codeblocks_width;
> +                int pos;
> +                ff_tag_tree_zero(prec->zerobits, prec->nb_codeblocks_width, prec->nb_codeblocks_height);
> +                ff_tag_tree_zero(prec->cblkincl, prec->nb_codeblocks_width, prec->nb_codeblocks_height);
> +                for (pos = 0; pos < nb_cblks; pos++) {
> +                    Jpeg2000Cblk *cblk = &prec->cblk[pos];
> +                    prec->zerobits[pos].val = expn[bandno] + numgbits - 1 - cblk->nonzerobits;
> +                    cblk->incl = 0;
> +                    cblk->lblock = 3;
> +                    tag_tree_update(prec->zerobits + pos);
> +                    for (i = 0; i < nlayers; i++) {
> +                        if (cblk->layers[i].npasses > 0) {
> +                            prec->cblkincl[pos].val = i;
> +                            break;
> +                        }
> +                    }
> +                    if (i == nlayers)
> +                        prec->cblkincl[pos].val = i;
> +                    tag_tree_update(prec->cblkincl + pos);
> +                }
> +            }
> +        }
> +    }
> +
>      // is the packet empty?
>      for (bandno = 0; bandno < rlevel->nbands; bandno++){
> +        Jpeg2000Band *band = rlevel->band + bandno;
>          if (rlevel->band[bandno].coord[0][0] < rlevel->band[bandno].coord[0][1]
>          &&  rlevel->band[bandno].coord[1][0] < rlevel->band[bandno].coord[1][1]){
> -            empty = 0;
> -            break;
> +            Jpeg2000Prec *prec = band->prec + precno;
> +            int nb_cblks = prec->nb_codeblocks_height * prec->nb_codeblocks_width;
> +            int pos;
> +            for (pos = 0; pos < nb_cblks; pos++) {
> +                Jpeg2000Cblk *cblk = &prec->cblk[pos];
> +                if (cblk->layers[layno].npasses) {
> +                    empty = 0;
> +                    break;
> +                }
> +            }
> +            if (!empty)
> +                break;
>          }
>      }
>
>      put_bits(s, !empty, 1);
>      if (empty){
>          j2k_flush(s);
> +        if (s->eph)
> +            bytestream_put_be16(&s->buf, JPEG2000_EPH);
>          return 0;
>      }
>
> @@ -775,40 +857,44 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
>
>          for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
>              for (xi = 0; xi < cblknw; xi++, pos++){
> -                prec->cblkincl[pos].val = prec->cblk[yi * cblknw + xi].ninclpasses == 0;
> -                tag_tree_update(prec->cblkincl + pos);
> -                prec->zerobits[pos].val = expn[bandno] + numgbits - 1 - prec->cblk[yi * cblknw + xi].nonzerobits;
> -                tag_tree_update(prec->zerobits + pos);
> -            }
> -        }
> -
> -        for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
> -            for (xi = 0; xi < cblknw; xi++, pos++){
> -                int pad = 0, llen, length;
> +                int llen = 0, length;
>                  Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
>
>                  if (s->buf_end - s->buf < 20) // approximately
>                      return -1;
>
>                  // inclusion information
> -                tag_tree_code(s, prec->cblkincl + pos, 1);
> -                if (!cblk->ninclpasses)
> +                if (!cblk->incl)
> +                    tag_tree_code(s, prec->cblkincl + pos, layno + 1);
> +                else {
> +                    put_bits(s, cblk->layers[layno].npasses > 0, 1);
> +                }
> +
> +                if (!cblk->layers[layno].npasses)
>                      continue;
> +
>                  // zerobits information
> -                tag_tree_code(s, prec->zerobits + pos, 100);
> +                if (!cblk->incl) {
> +                    tag_tree_code(s, prec->zerobits + pos, 100);
> +                    cblk->incl = 1;
> +                }
> +
>                  // number of passes
> -                putnumpasses(s, cblk->ninclpasses);
> +                putnumpasses(s, cblk->layers[layno].npasses);
>
> -                length = cblk->passes[cblk->ninclpasses-1].rate;
> -                llen = av_log2(length) - av_log2(cblk->ninclpasses) - 2;
> -                if (llen < 0){
> -                    pad = -llen;
> -                    llen = 0;
> +                length = cblk->layers[layno].data_len;
> +                if (layno == nlayers - 1 && cblk->layers->cum_passes){
> +                    length += cblk->passes[cblk->layers->cum_passes-1].flushed_len;
>                  }
> +                if (cblk->lblock + av_log2(cblk->layers[layno].npasses) < av_log2(length) + 1) {
> +                    llen = av_log2(length) + 1 - cblk->lblock - av_log2(cblk->layers[layno].npasses);
> +                }
> +
>                  // length of code block
> +                cblk->lblock += llen;
>                  put_bits(s, 1, llen);
>                  put_bits(s, 0, 1);
> -                put_num(s, length, av_log2(length)+1+pad);
> +                put_num(s, length, cblk->lblock + av_log2(cblk->layers[layno].npasses));
>              }
>          }
>      }
> @@ -825,13 +911,14 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
>              int xi;
>              for (xi = 0; xi < cblknw; xi++){
>                  Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
> -                if (cblk->ninclpasses){
> -                    if (s->buf_end - s->buf < cblk->passes[cblk->ninclpasses-1].rate)
> +                if (cblk->layers[layno].npasses){
> +                    if (s->buf_end - s->buf < cblk->layers[layno].data_len + 2)
>                          return -1;
> -                    bytestream_put_buffer(&s->buf, cblk->data + 1,   cblk->passes[cblk->ninclpasses-1].rate
> -                                                               - cblk->passes[cblk->ninclpasses-1].flushed_len);
> -                    bytestream_put_buffer(&s->buf, cblk->passes[cblk->ninclpasses-1].flushed,
> -                                                   cblk->passes[cblk->ninclpasses-1].flushed_len);
> +                    bytestream_put_buffer(&s->buf, cblk->layers[layno].data_start + 1, cblk->layers[layno].data_len);
> +                    if (layno == nlayers - 1 && cblk->layers->cum_passes){
> +                        bytestream_put_buffer(&s->buf, cblk->passes[cblk->layers->cum_passes-1].flushed,
> +                                                       cblk->passes[cblk->layers->cum_passes-1].flushed_len);
> +                    }
>                  }
>              }
>          }
> @@ -839,9 +926,9 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
>      return 0;
>  }
>
> -static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno)
> +static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno, int nlayers)
>  {
> -    int compno, reslevelno, ret;
> +    int compno, reslevelno, layno, ret;
>      Jpeg2000CodingStyle *codsty = &s->codsty;
>      Jpeg2000QuantStyle  *qntsty = &s->qntsty;
>      int packetno = 0;
> @@ -858,29 +945,33 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
>
>      av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
>      // lay-rlevel-comp-pos progression
> -    switch (s->prog) {
> +        switch (s->prog) {
>      case JPEG2000_PGOD_LRCP:
> -    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
> -        for (compno = 0; compno < s->ncomponents; compno++){
> -            int precno;
> -            Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
> -            for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> -                if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> -                              qntsty->nguardbits, packetno++)) < 0)
> -                    return ret;
> +    for (layno = 0; layno < nlayers; layno++){
> +        for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
> +            for (compno = 0; compno < s->ncomponents; compno++){
> +                int precno;
> +                Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
> +                for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> +                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                qntsty->nguardbits, packetno++, nlayers)) < 0)
> +                        return ret;
> +                }
>              }
>          }
>      }
>      break;
>      case JPEG2000_PGOD_RLCP:
>      for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
> -        for (compno = 0; compno < s->ncomponents; compno++){
> -            int precno;
> -            Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
> -            for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> -                if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> -                              qntsty->nguardbits, packetno++)) < 0)
> -                    return ret;
> +        for (layno = 0; layno < nlayers; layno++){
> +            for (compno = 0; compno < s->ncomponents; compno++){
> +                int precno;
> +                Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
> +                for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> +                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                qntsty->nguardbits, packetno++, nlayers)) < 0)
> +                        return ret;
> +                }
>              }
>          }
>      }
> @@ -935,10 +1026,11 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
>                                 prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
>                          continue;
>                      }
> -
> -                    if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> -                              qntsty->nguardbits, packetno++)) < 0)
> -                        return ret;
> +                    for (layno = 0; layno < nlayers; layno++){
> +                        if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                qntsty->nguardbits, packetno++, nlayers)) < 0)
> +                            return ret;
> +                        }
>                      }
>                  }
>              }
> @@ -1001,9 +1093,11 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
>                                     prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
>                              continue;
>                          }
> -                        if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> -                                  qntsty->nguardbits, packetno++)) < 0)
> -                            return ret;
> +                        for (layno = 0; layno < nlayers; layno++){
> +                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                    qntsty->nguardbits, packetno++, nlayers)) < 0)
> +                                return ret;
> +                        }
>                      }
>                  }
>              }
> @@ -1062,9 +1156,11 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
>                                     prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
>                              continue;
>                          }
> -                        if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> -                                  qntsty->nguardbits, packetno++)) < 0)
> -                            return ret;
> +                        for (layno = 0; layno < nlayers; layno++){
> +                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                    qntsty->nguardbits, packetno++, nlayers)) < 0)
> +                                return ret;
> +                        }
>                      }
>                  }
>              }
> @@ -1076,27 +1172,98 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
>      return 0;
>  }
>
> -static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm)
> +static void makelayer(Jpeg2000EncoderContext *s, int layno, double thresh, Jpeg2000Tile* tile, int final)
>  {
> -    int passno, res = 0;
> -    for (passno = 0; passno < cblk->npasses; passno++){
> -        int dr;
> -        int64_t dd;
> -
> -        dr = cblk->passes[passno].rate
> -           - (res ? cblk->passes[res-1].rate:0);
> -        dd = cblk->passes[passno].disto
> -           - (res ? cblk->passes[res-1].disto:0);
> -
> -        if (((dd * dwt_norm) >> WMSEDEC_SHIFT) * dwt_norm >= dr * lambda)
> -            res = passno+1;
> +    int compno, resno, bandno, precno, cblkno;
> +    int passno;
> +
> +    for (compno = 0; compno < s->ncomponents; compno++) {
> +        Jpeg2000Component *comp = &tile->comp[compno];
> +
> +        for (resno = 0; resno < s->codsty.nreslevels; resno++) {
> +            Jpeg2000ResLevel *reslevel = comp->reslevel + resno;
> +
> +            for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> +                for (bandno = 0; bandno < reslevel->nbands ; bandno++){
> +                    Jpeg2000Band *band = reslevel->band + bandno;
> +                    Jpeg2000Prec *prec = band->prec + precno;
> +
> +                    for (cblkno = 0; cblkno < prec->nb_codeblocks_height * prec->nb_codeblocks_width; cblkno++){
> +                        Jpeg2000Cblk *cblk = prec->cblk + cblkno;
> +                        Jpeg2000Layer *layer = &cblk->layers[layno];
> +                        int n;
> +
> +                        if (layno == 0) {
> +                            cblk->ninclpasses = 0;
> +                        }
> +
> +                        n = cblk->ninclpasses;
> +
> +                        if (thresh < 0) {
> +                            n = cblk->npasses;
> +                        } else {
> +                            for (passno = cblk->ninclpasses; passno < cblk->npasses; passno++) {
> +                                int32_t dr;
> +                                double dd;
> +                                Jpeg2000Pass *pass = &cblk->passes[passno];
> +
> +                                if (n == 0) {
> +                                    dr = pass->rate;
> +                                    dd = (double)pass->disto;
> +                                } else {
> +                                    dr = pass->rate - cblk->passes[n - 1].rate;
> +                                    dd = (double)pass->disto - (double)cblk->passes[n-1].disto;
> +                                }
> +
> +                                if (!dr) {
> +                                    if (dd) {
> +                                        n = passno + 1;
> +                                    }
> +                                    continue;
> +                                }
> +
> +                                if (thresh - (dd / dr) < DBL_EPSILON)
> +                                    n = passno + 1;
> +                            }
> +                        }
> +                        layer->npasses = n - cblk->ninclpasses;
> +                        layer->cum_passes = n;
> +
> +                        if (layer->npasses == 0) {
> +                            layer->disto = 0;
> +                            layer->data_len = 0;
> +                            continue;
> +                        }
> +
> +                        if (cblk->ninclpasses == 0) {
> +                            layer->data_len = cblk->passes[n - 1].rate;
> +                            layer->data_start = cblk->data;
> +                            layer->disto = cblk->passes[n - 1].disto;
> +                        } else {
> +                            layer->data_len = cblk->passes[n - 1].rate - cblk->passes[cblk->ninclpasses - 1].rate;
> +                            layer->data_start = cblk->data + cblk->passes[cblk->ninclpasses - 1].rate;
> +                            layer->disto = cblk->passes[n - 1].disto -
> +                                           cblk->passes[cblk->ninclpasses - 1].disto;
> +                        }
> +                        if (final) {
> +                            cblk->ninclpasses = n;
> +                        }
> +                    }
> +                }
> +            }
> +        }
>      }
> -    return res;
>  }
>
> -static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
> +static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
>  {
> -    int precno, compno, reslevelno, bandno, cblkno, lev;
> +    int precno, compno, reslevelno, bandno, cblkno, lev, passno, layno;
> +    int i;
> +    double min = DBL_MAX;
> +    double max = 0;
> +    double thresh;
> +    int tile_disto = 0;
> +
>      Jpeg2000CodingStyle *codsty = &s->codsty;
>
>      for (compno = 0; compno < s->ncomponents; compno++){
> @@ -1107,20 +1274,70 @@ static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
>
>              for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
>                  for (bandno = 0; bandno < reslevel->nbands ; bandno++){
> -                    int bandpos = bandno + (reslevelno > 0);
>                      Jpeg2000Band *band = reslevel->band + bandno;
>                      Jpeg2000Prec *prec = band->prec + precno;
>
>                      for (cblkno = 0; cblkno < prec->nb_codeblocks_height * prec->nb_codeblocks_width; cblkno++){
>                          Jpeg2000Cblk *cblk = prec->cblk + cblkno;
> +                        for (passno = 0; passno < cblk->npasses; passno++) {
> +                            Jpeg2000Pass *pass = &cblk->passes[passno];
> +                            int dr;
> +                            double dd, drslope;
> +
> +                            tile_disto += pass->disto;
> +                            if (passno == 0) {
> +                                dr = (int32_t)pass->rate;
> +                                dd = (double)pass->disto;
> +                            } else {
> +                                dr = (int32_t)(pass->rate) - cblk->passes[passno - 1].rate;
> +                                dd = (double)pass->disto - (double)cblk->passes[passno - 1].disto;
> +                            }
> +
> +                            if (dr <= 0)
> +                                continue;
>
> -                        cblk->ninclpasses = getcut(cblk, s->lambda,
> -                                (int64_t)dwt_norms[codsty->transform == FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15);
> +                            drslope = dd / dr;
> +                            if (drslope < min)
> +                                min = drslope;
> +
> +                            if (drslope > max)
> +                                max = drslope;
> +                        }
>                      }
>                  }
>              }
>          }
>      }
> +
> +    for (layno = 0; layno < s->nlayers; layno++) {
> +        double lo = min;
> +        double hi = max;
> +        double stable_thresh = 0;
> +        double good_thresh = 0;
> +        if (!s->layer_rates[layno]) {
> +            good_thresh = -1;
> +        } else {
> +            for (i = 0; i < 128; i++) {
> +                uint8_t *stream_pos = s->buf;
> +                int ret;
> +                thresh = (lo + hi) / 2;
> +                makelayer(s, layno, thresh, tile, 0);
> +                ret = encode_packets(s, tile, (int)(tile - s->tile), layno + 1);
> +                memset(stream_pos, 0, s->buf - stream_pos);
> +                if ((s->buf - stream_pos > ceil(tile->layer_rates[layno])) || ret < 0) {
> +                    lo = thresh;
> +                    s->buf = stream_pos;
> +                    continue;
> +                }
> +                hi = thresh;
> +                stable_thresh = thresh;
> +                s->buf = stream_pos;
> +            }
> +        }
> +        if (good_thresh >= 0)
> +            good_thresh = stable_thresh == 0 ? thresh : stable_thresh;
> +        makelayer(s, layno, good_thresh, tile, 1);
> +    }
>  }
>
>  static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno)
> @@ -1203,8 +1420,8 @@ static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno
>      }
>
>      av_log(s->avctx, AV_LOG_DEBUG, "rate control\n");
> -    truncpasses(s, tile);
> -    if ((ret = encode_packets(s, tile, tileno)) < 0)
> +    makelayers(s, tile);
> +    if ((ret = encode_packets(s, tile, tileno, s->nlayers)) < 0)
>          return ret;
>      av_log(s->avctx, AV_LOG_DEBUG, "after rate control\n");
>      return 0;
> @@ -1221,6 +1438,7 @@ static void cleanup(Jpeg2000EncoderContext *s)
>              ff_jpeg2000_cleanup(comp, codsty, 1);
>          }
>          av_freep(&s->tile[tileno].comp);
> +        av_freep(&s->tile[tileno].layer_rates);
>      }
>      av_freep(&s->tile);
>  }
> @@ -1379,6 +1597,64 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
>      return 0;
>  }
>
> +static int inline check_number(char* st, int* ret) {
> +    int stlen = strlen(st);
> +    int i;
> +    *ret = 0;
> +    if (stlen <= 0) {
> +        return AVERROR_INVALIDDATA;
> +    }
> +    for (i = 0; i < stlen; i++) {
> +        if (st[i] >= '0' && st[i] <= '9') {
> +            *ret = (*ret) * 10 + (st[i] - '0');
> +        } else {
> +            return AVERROR_INVALIDDATA;
> +        }
> +    }
> +    return 0;
> +}
> +
> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
> +{
> +    int i;
> +    char* token;
> +    int rate;
> +    int nlayers = 0;
> +    if (!s->lr_str) {
> +        s->nlayers = 1;
> +        s->layer_rates[0] = 0;
> +        return 0;
> +    }
> +
> +    token = strtok(s->lr_str, ",");
> +    if (!check_number(token, &rate)) {
> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
> +            nlayers++;
> +    } else {
> +            return AVERROR_INVALIDDATA;
> +    }
> +
> +    while (1) {
> +        token = strtok(NULL, ",");
> +        if (!token)
> +            break;
> +        if (!check_number(token, &rate)) {
> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
> +            nlayers++;
> +        } else {
> +            return AVERROR_INVALIDDATA;
> +        }
> +    }
> +
> +    for (i = 1; i < nlayers; i++) {
> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
> +            return AVERROR_INVALIDDATA;
> +        }
> +    }
> +    s->nlayers = nlayers;
> +    return 0;
> +}
> +
>  static av_cold int j2kenc_init(AVCodecContext *avctx)
>  {
>      int i, ret;
> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext *avctx)
>
>      s->avctx = avctx;
>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
> +    if (parse_layer_rates(s)) {
> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
> +        s->nlayers = 1;
> +        s->layer_rates[0] = 0;
> +    }
>
>  #if FF_API_PRIVATE_OPT
>  FF_DISABLE_DEPRECATION_WARNINGS
> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
>      memset(codsty->log2_prec_heights, 15, sizeof(codsty->log2_prec_heights));
>      codsty->nreslevels2decode=
>      codsty->nreslevels       = 7;
> +    codsty->nlayers          = s->nlayers;
>      codsty->log2_cblk_width  = 4;
>      codsty->log2_cblk_height = 4;
>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
>      { "rpcl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,         0,           VE, "prog" },
>      { "pcrl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,         0,           VE, "prog" },
>      { "cprl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,         0,           VE, "prog" },
> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
>      { NULL }
>  };
>
> diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c
> index 70c25a0ca2..1c294ad9e6 100644
> --- a/libavcodec/jpeg2000.c
> +++ b/libavcodec/jpeg2000.c
> @@ -261,9 +261,11 @@ static void init_band_stepsize(AVCodecContext *avctx,
>          band->f_stepsize *= 0.5;
>  }
>
> -static int init_prec(Jpeg2000Band *band,
> +static int init_prec(AVCodecContext *avctx,
> +                     Jpeg2000Band *band,
>                       Jpeg2000ResLevel *reslevel,
>                       Jpeg2000Component *comp,
> +                     Jpeg2000CodingStyle *codsty,
>                       int precno, int bandno, int reslevelno,
>                       int log2_band_prec_width,
>                       int log2_band_prec_height)
> @@ -366,6 +368,11 @@ static int init_prec(Jpeg2000Band *band,
>          cblk->lblock    = 3;
>          cblk->length    = 0;
>          cblk->npasses   = 0;
> +        if (av_codec_is_encoder(avctx->codec)) {
> +            cblk->layers = av_mallocz_array(codsty->nlayers, sizeof(*cblk->layers));
> +            if (!cblk->layers)
> +                return AVERROR(ENOMEM);
> +        }
>      }
>
>      return 0;
> @@ -439,7 +446,7 @@ static int init_band(AVCodecContext *avctx,
>          return AVERROR(ENOMEM);
>
>      for (precno = 0; precno < nb_precincts; precno++) {
> -        ret = init_prec(band, reslevel, comp,
> +        ret = init_prec(avctx, band, reslevel, comp, codsty,
>                          precno, bandno, reslevelno,
>                          log2_band_prec_width, log2_band_prec_height);
>          if (ret < 0)
> @@ -614,6 +621,8 @@ void ff_jpeg2000_cleanup(Jpeg2000Component *comp, Jpeg2000CodingStyle *codsty, i
>                              av_freep(&cblk->passes);
>                              av_freep(&cblk->lengthinc);
>                              av_freep(&cblk->data_start);
> +                            if (isencoder)
> +                                av_freep(&cblk->layers);
>                          }
>                          av_freep(&prec->cblk);
>                      }
> diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
> index fee9607e86..3c6e8ede16 100644
> --- a/libavcodec/jpeg2000.h
> +++ b/libavcodec/jpeg2000.h
> @@ -162,10 +162,19 @@ typedef struct Jpeg2000Pass {
>      int flushed_len;
>  } Jpeg2000Pass;
>
> +typedef struct Jpeg2000Layer {
> +    uint8_t *data_start;
> +    int data_len;
> +    int npasses;
> +    double disto;
> +    int cum_passes;
> +} Jpeg2000Layer;
> +
>  typedef struct Jpeg2000Cblk {
>      uint8_t npasses;
>      uint8_t ninclpasses; // number coding of passes included in codestream
>      uint8_t nonzerobits;
> +    uint8_t incl;
>      uint16_t length;
>      uint16_t *lengthinc;
>      uint8_t nb_lengthinc;
> @@ -176,6 +185,7 @@ typedef struct Jpeg2000Cblk {
>      int nb_terminationsinc;
>      int *data_start;
>      Jpeg2000Pass *passes;
> +    Jpeg2000Layer *layers;
>      int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}}
>  } Jpeg2000Cblk; // code block
>
> --
> 2.17.1
>

This patch seems to be breaking FATE.
I believe that the error is because the patch modifies the encoder
such that the encoded files will be slightly different now.
How can this be handled?
Paul B Mahol Aug. 19, 2020, 6:56 p.m. UTC | #2
On 8/19/20, Gautam Ramakrishnan <gautamramk@gmail.com> wrote:
> On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
>>
>> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>>
>> This patch allows setting a compression ratio and to
>> set multiple layers. The user has to input a compression
>> ratio for each layer.
>> The per layer compression ration can be set as follows:
>> -layer_rates "r1,r2,...rn"
>> for to create 'n' layers.
>> ---
>>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
>>  libavcodec/jpeg2000.c |  13 +-
>>  libavcodec/jpeg2000.h |  10 +
>>  3 files changed, 384 insertions(+), 82 deletions(-)
>>
>> diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c
>> index 8699296434..b09db36c14 100644
>> --- a/libavcodec/j2kenc.c
>> +++ b/libavcodec/j2kenc.c
>> @@ -32,6 +32,7 @@
>>   * Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe
>>   * Copyright (c) 2005, Herve Drolon, FreeImage Team
>>   * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
>> + * Copyright (c) 2020, Gautam Ramakrishnan <gautamramk@gmail.com>
>>   * All rights reserved.
>>   *
>>   * Redistribution and use in source and binary forms, with or without
>> @@ -100,6 +101,7 @@ static const int dwt_norms[2][4][10] = { //
>> [dwt_type][band][rlevel] (multiplied
>>
>>  typedef struct {
>>     Jpeg2000Component *comp;
>> +   double *layer_rates;
>>  } Jpeg2000Tile;
>>
>>  typedef struct {
>> @@ -126,12 +128,15 @@ typedef struct {
>>      Jpeg2000QuantStyle  qntsty;
>>
>>      Jpeg2000Tile *tile;
>> +    int layer_rates[100];
>>
>>      int format;
>>      int pred;
>>      int sop;
>>      int eph;
>>      int prog;
>> +    int nlayers;
>> +    char *lr_str;
>>  } Jpeg2000EncoderContext;
>>
>>
>> @@ -332,7 +337,7 @@ static int put_cod(Jpeg2000EncoderContext *s)
>>      bytestream_put_byte(&s->buf, scod);  // Scod
>>      // SGcod
>>      bytestream_put_byte(&s->buf, s->prog); // progression level
>> -    bytestream_put_be16(&s->buf, 1); // num of layers
>> +    bytestream_put_be16(&s->buf, s->nlayers); // num of layers
>>      if(s->avctx->pix_fmt == AV_PIX_FMT_YUV444P){
>>          bytestream_put_byte(&s->buf, 0); // unspecified
>>      }else{
>> @@ -411,6 +416,31 @@ static uint8_t *put_sot(Jpeg2000EncoderContext *s,
>> int tileno)
>>      return psotptr;
>>  }
>>
>> +static void compute_rates(Jpeg2000EncoderContext* s)
>> +{
>> +    int i, j;
>> +    int layno, compno;
>> +    for (i = 0; i < s->numYtiles; i++) {
>> +        for (j = 0; j < s->numXtiles; j++) {
>> +            Jpeg2000Tile *tile = &s->tile[s->numXtiles * i + j];
>> +            for (compno = 0; compno < s->ncomponents; compno++) {
>> +                int tilew = tile->comp[compno].coord[0][1] -
>> tile->comp[compno].coord[0][0];
>> +                int tileh = tile->comp[compno].coord[1][1] -
>> tile->comp[compno].coord[1][0];
>> +                int scale = (compno?1 << s->chroma_shift[0]:1) *
>> (compno?1 << s->chroma_shift[1]:1);
>> +                for (layno = 0; layno < s->nlayers; layno++) {
>> +                    if (s->layer_rates[layno] > 0.0f) {
>> +                        tile->layer_rates[layno] += (double)(tilew *
>> tileh) * s->ncomponents * s->cbps[compno] /
>> +
>> (double)(s->layer_rates[layno] * 8 * scale);
>> +                    } else {
>> +                        tile->layer_rates[layno] = 0.0f;
>> +                    }
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>> +}
>> +
>>  /**
>>   * compute the sizes of tiles, resolution levels, bands, etc.
>>   * allocate memory for them
>> @@ -448,6 +478,10 @@ static int init_tiles(Jpeg2000EncoderContext *s)
>>                          for (j = 0; j < 2; j++)
>>                              comp->coord[i][j] = comp->coord_o[i][j] =
>> ff_jpeg2000_ceildivpow2(comp->coord[i][j], s->chroma_shift[i]);
>>
>> +                tile->layer_rates = av_mallocz_array(s->ncomponents,
>> sizeof(*tile->layer_rates));
>> +                if (!tile->layer_rates)
>> +                    return AVERROR(ENOMEM);
>> +
>>                  if ((ret = ff_jpeg2000_init_component(comp,
>>                                                  codsty,
>>                                                  qntsty,
>> @@ -459,6 +493,7 @@ static int init_tiles(Jpeg2000EncoderContext *s)
>>                      return ret;
>>              }
>>          }
>> +    compute_rates(s);
>>      return 0;
>>  }
>>
>> @@ -701,6 +736,8 @@ static void encode_cblk(Jpeg2000EncoderContext *s,
>> Jpeg2000T1Context *t1, Jpeg20
>>          }
>>
>>          cblk->passes[passno].rate = ff_mqc_flush_to(&t1->mqc,
>> cblk->passes[passno].flushed, &cblk->passes[passno].flushed_len);
>> +        cblk->passes[passno].rate -= cblk->passes[passno].flushed_len;
>> +
>>          wmsedec += (int64_t)nmsedec << (2*bpno);
>>          cblk->passes[passno].disto = wmsedec;
>>
>> @@ -733,10 +770,12 @@ static void putnumpasses(Jpeg2000EncoderContext *s,
>> int n)
>>  }
>>
>>
>> -static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel
>> *rlevel, int precno,
>> -                          uint8_t *expn, int numgbits, int packetno)
>> +static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel
>> *rlevel, int layno,
>> +                         int precno, uint8_t *expn, int numgbits, int
>> packetno,
>> +                         int nlayers)
>>  {
>>      int bandno, empty = 1;
>> +    int i;
>>      // init bitstream
>>      *s->buf = 0;
>>      s->bit_index = 0;
>> @@ -748,18 +787,61 @@ static int encode_packet(Jpeg2000EncoderContext *s,
>> Jpeg2000ResLevel *rlevel, in
>>      }
>>      // header
>>
>> +    if (!layno) {
>> +        for (bandno = 0; bandno < rlevel->nbands; bandno++){
>> +            Jpeg2000Band *band = rlevel->band + bandno;
>> +            if (rlevel->band[bandno].coord[0][0] <
>> rlevel->band[bandno].coord[0][1]
>> +            &&  rlevel->band[bandno].coord[1][0] <
>> rlevel->band[bandno].coord[1][1]){
>> +                Jpeg2000Prec *prec = band->prec + precno;
>> +                int nb_cblks = prec->nb_codeblocks_height *
>> prec->nb_codeblocks_width;
>> +                int pos;
>> +                ff_tag_tree_zero(prec->zerobits,
>> prec->nb_codeblocks_width, prec->nb_codeblocks_height);
>> +                ff_tag_tree_zero(prec->cblkincl,
>> prec->nb_codeblocks_width, prec->nb_codeblocks_height);
>> +                for (pos = 0; pos < nb_cblks; pos++) {
>> +                    Jpeg2000Cblk *cblk = &prec->cblk[pos];
>> +                    prec->zerobits[pos].val = expn[bandno] + numgbits - 1
>> - cblk->nonzerobits;
>> +                    cblk->incl = 0;
>> +                    cblk->lblock = 3;
>> +                    tag_tree_update(prec->zerobits + pos);
>> +                    for (i = 0; i < nlayers; i++) {
>> +                        if (cblk->layers[i].npasses > 0) {
>> +                            prec->cblkincl[pos].val = i;
>> +                            break;
>> +                        }
>> +                    }
>> +                    if (i == nlayers)
>> +                        prec->cblkincl[pos].val = i;
>> +                    tag_tree_update(prec->cblkincl + pos);
>> +                }
>> +            }
>> +        }
>> +    }
>> +
>>      // is the packet empty?
>>      for (bandno = 0; bandno < rlevel->nbands; bandno++){
>> +        Jpeg2000Band *band = rlevel->band + bandno;
>>          if (rlevel->band[bandno].coord[0][0] <
>> rlevel->band[bandno].coord[0][1]
>>          &&  rlevel->band[bandno].coord[1][0] <
>> rlevel->band[bandno].coord[1][1]){
>> -            empty = 0;
>> -            break;
>> +            Jpeg2000Prec *prec = band->prec + precno;
>> +            int nb_cblks = prec->nb_codeblocks_height *
>> prec->nb_codeblocks_width;
>> +            int pos;
>> +            for (pos = 0; pos < nb_cblks; pos++) {
>> +                Jpeg2000Cblk *cblk = &prec->cblk[pos];
>> +                if (cblk->layers[layno].npasses) {
>> +                    empty = 0;
>> +                    break;
>> +                }
>> +            }
>> +            if (!empty)
>> +                break;
>>          }
>>      }
>>
>>      put_bits(s, !empty, 1);
>>      if (empty){
>>          j2k_flush(s);
>> +        if (s->eph)
>> +            bytestream_put_be16(&s->buf, JPEG2000_EPH);
>>          return 0;
>>      }
>>
>> @@ -775,40 +857,44 @@ static int encode_packet(Jpeg2000EncoderContext *s,
>> Jpeg2000ResLevel *rlevel, in
>>
>>          for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
>>              for (xi = 0; xi < cblknw; xi++, pos++){
>> -                prec->cblkincl[pos].val = prec->cblk[yi * cblknw +
>> xi].ninclpasses == 0;
>> -                tag_tree_update(prec->cblkincl + pos);
>> -                prec->zerobits[pos].val = expn[bandno] + numgbits - 1 -
>> prec->cblk[yi * cblknw + xi].nonzerobits;
>> -                tag_tree_update(prec->zerobits + pos);
>> -            }
>> -        }
>> -
>> -        for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
>> -            for (xi = 0; xi < cblknw; xi++, pos++){
>> -                int pad = 0, llen, length;
>> +                int llen = 0, length;
>>                  Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
>>
>>                  if (s->buf_end - s->buf < 20) // approximately
>>                      return -1;
>>
>>                  // inclusion information
>> -                tag_tree_code(s, prec->cblkincl + pos, 1);
>> -                if (!cblk->ninclpasses)
>> +                if (!cblk->incl)
>> +                    tag_tree_code(s, prec->cblkincl + pos, layno + 1);
>> +                else {
>> +                    put_bits(s, cblk->layers[layno].npasses > 0, 1);
>> +                }
>> +
>> +                if (!cblk->layers[layno].npasses)
>>                      continue;
>> +
>>                  // zerobits information
>> -                tag_tree_code(s, prec->zerobits + pos, 100);
>> +                if (!cblk->incl) {
>> +                    tag_tree_code(s, prec->zerobits + pos, 100);
>> +                    cblk->incl = 1;
>> +                }
>> +
>>                  // number of passes
>> -                putnumpasses(s, cblk->ninclpasses);
>> +                putnumpasses(s, cblk->layers[layno].npasses);
>>
>> -                length = cblk->passes[cblk->ninclpasses-1].rate;
>> -                llen = av_log2(length) - av_log2(cblk->ninclpasses) - 2;
>> -                if (llen < 0){
>> -                    pad = -llen;
>> -                    llen = 0;
>> +                length = cblk->layers[layno].data_len;
>> +                if (layno == nlayers - 1 && cblk->layers->cum_passes){
>> +                    length +=
>> cblk->passes[cblk->layers->cum_passes-1].flushed_len;
>>                  }
>> +                if (cblk->lblock + av_log2(cblk->layers[layno].npasses) <
>> av_log2(length) + 1) {
>> +                    llen = av_log2(length) + 1 - cblk->lblock -
>> av_log2(cblk->layers[layno].npasses);
>> +                }
>> +
>>                  // length of code block
>> +                cblk->lblock += llen;
>>                  put_bits(s, 1, llen);
>>                  put_bits(s, 0, 1);
>> -                put_num(s, length, av_log2(length)+1+pad);
>> +                put_num(s, length, cblk->lblock +
>> av_log2(cblk->layers[layno].npasses));
>>              }
>>          }
>>      }
>> @@ -825,13 +911,14 @@ static int encode_packet(Jpeg2000EncoderContext *s,
>> Jpeg2000ResLevel *rlevel, in
>>              int xi;
>>              for (xi = 0; xi < cblknw; xi++){
>>                  Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
>> -                if (cblk->ninclpasses){
>> -                    if (s->buf_end - s->buf <
>> cblk->passes[cblk->ninclpasses-1].rate)
>> +                if (cblk->layers[layno].npasses){
>> +                    if (s->buf_end - s->buf <
>> cblk->layers[layno].data_len + 2)
>>                          return -1;
>> -                    bytestream_put_buffer(&s->buf, cblk->data + 1,
>> cblk->passes[cblk->ninclpasses-1].rate
>> -                                                               -
>> cblk->passes[cblk->ninclpasses-1].flushed_len);
>> -                    bytestream_put_buffer(&s->buf,
>> cblk->passes[cblk->ninclpasses-1].flushed,
>> -
>> cblk->passes[cblk->ninclpasses-1].flushed_len);
>> +                    bytestream_put_buffer(&s->buf,
>> cblk->layers[layno].data_start + 1, cblk->layers[layno].data_len);
>> +                    if (layno == nlayers - 1 &&
>> cblk->layers->cum_passes){
>> +                        bytestream_put_buffer(&s->buf,
>> cblk->passes[cblk->layers->cum_passes-1].flushed,
>> +
>> cblk->passes[cblk->layers->cum_passes-1].flushed_len);
>> +                    }
>>                  }
>>              }
>>          }
>> @@ -839,9 +926,9 @@ static int encode_packet(Jpeg2000EncoderContext *s,
>> Jpeg2000ResLevel *rlevel, in
>>      return 0;
>>  }
>>
>> -static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile,
>> int tileno)
>> +static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile,
>> int tileno, int nlayers)
>>  {
>> -    int compno, reslevelno, ret;
>> +    int compno, reslevelno, layno, ret;
>>      Jpeg2000CodingStyle *codsty = &s->codsty;
>>      Jpeg2000QuantStyle  *qntsty = &s->qntsty;
>>      int packetno = 0;
>> @@ -858,29 +945,33 @@ static int encode_packets(Jpeg2000EncoderContext *s,
>> Jpeg2000Tile *tile, int til
>>
>>      av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
>>      // lay-rlevel-comp-pos progression
>> -    switch (s->prog) {
>> +        switch (s->prog) {
>>      case JPEG2000_PGOD_LRCP:
>> -    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
>> -        for (compno = 0; compno < s->ncomponents; compno++){
>> -            int precno;
>> -            Jpeg2000ResLevel *reslevel =
>> s->tile[tileno].comp[compno].reslevel + reslevelno;
>> -            for (precno = 0; precno < reslevel->num_precincts_x *
>> reslevel->num_precincts_y; precno++){
>> -                if ((ret = encode_packet(s, reslevel, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> -                              qntsty->nguardbits, packetno++)) < 0)
>> -                    return ret;
>> +    for (layno = 0; layno < nlayers; layno++){
>> +        for (reslevelno = 0; reslevelno < codsty->nreslevels;
>> reslevelno++){
>> +            for (compno = 0; compno < s->ncomponents; compno++){
>> +                int precno;
>> +                Jpeg2000ResLevel *reslevel =
>> s->tile[tileno].comp[compno].reslevel + reslevelno;
>> +                for (precno = 0; precno < reslevel->num_precincts_x *
>> reslevel->num_precincts_y; precno++){
>> +                    if ((ret = encode_packet(s, reslevel, layno, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> +                                qntsty->nguardbits, packetno++, nlayers))
>> < 0)
>> +                        return ret;
>> +                }
>>              }
>>          }
>>      }
>>      break;
>>      case JPEG2000_PGOD_RLCP:
>>      for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
>> -        for (compno = 0; compno < s->ncomponents; compno++){
>> -            int precno;
>> -            Jpeg2000ResLevel *reslevel =
>> s->tile[tileno].comp[compno].reslevel + reslevelno;
>> -            for (precno = 0; precno < reslevel->num_precincts_x *
>> reslevel->num_precincts_y; precno++){
>> -                if ((ret = encode_packet(s, reslevel, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> -                              qntsty->nguardbits, packetno++)) < 0)
>> -                    return ret;
>> +        for (layno = 0; layno < nlayers; layno++){
>> +            for (compno = 0; compno < s->ncomponents; compno++){
>> +                int precno;
>> +                Jpeg2000ResLevel *reslevel =
>> s->tile[tileno].comp[compno].reslevel + reslevelno;
>> +                for (precno = 0; precno < reslevel->num_precincts_x *
>> reslevel->num_precincts_y; precno++){
>> +                    if ((ret = encode_packet(s, reslevel, layno, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> +                                qntsty->nguardbits, packetno++, nlayers))
>> < 0)
>> +                        return ret;
>> +                }
>>              }
>>          }
>>      }
>> @@ -935,10 +1026,11 @@ static int encode_packets(Jpeg2000EncoderContext
>> *s, Jpeg2000Tile *tile, int til
>>                                 prcx, prcy, reslevel->num_precincts_x,
>> reslevel->num_precincts_y);
>>                          continue;
>>                      }
>> -
>> -                    if ((ret = encode_packet(s, reslevel, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> -                              qntsty->nguardbits, packetno++)) < 0)
>> -                        return ret;
>> +                    for (layno = 0; layno < nlayers; layno++){
>> +                        if ((ret = encode_packet(s, reslevel, layno,
>> precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> +                                qntsty->nguardbits, packetno++, nlayers))
>> < 0)
>> +                            return ret;
>> +                        }
>>                      }
>>                  }
>>              }
>> @@ -1001,9 +1093,11 @@ static int encode_packets(Jpeg2000EncoderContext
>> *s, Jpeg2000Tile *tile, int til
>>                                     prcx, prcy, reslevel->num_precincts_x,
>> reslevel->num_precincts_y);
>>                              continue;
>>                          }
>> -                        if ((ret = encode_packet(s, reslevel, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> -                                  qntsty->nguardbits, packetno++)) < 0)
>> -                            return ret;
>> +                        for (layno = 0; layno < nlayers; layno++){
>> +                            if ((ret = encode_packet(s, reslevel, layno,
>> precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> +                                    qntsty->nguardbits, packetno++,
>> nlayers)) < 0)
>> +                                return ret;
>> +                        }
>>                      }
>>                  }
>>              }
>> @@ -1062,9 +1156,11 @@ static int encode_packets(Jpeg2000EncoderContext
>> *s, Jpeg2000Tile *tile, int til
>>                                     prcx, prcy, reslevel->num_precincts_x,
>> reslevel->num_precincts_y);
>>                              continue;
>>                          }
>> -                        if ((ret = encode_packet(s, reslevel, precno,
>> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> -                                  qntsty->nguardbits, packetno++)) < 0)
>> -                            return ret;
>> +                        for (layno = 0; layno < nlayers; layno++){
>> +                            if ((ret = encode_packet(s, reslevel, layno,
>> precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
>> +                                    qntsty->nguardbits, packetno++,
>> nlayers)) < 0)
>> +                                return ret;
>> +                        }
>>                      }
>>                  }
>>              }
>> @@ -1076,27 +1172,98 @@ static int encode_packets(Jpeg2000EncoderContext
>> *s, Jpeg2000Tile *tile, int til
>>      return 0;
>>  }
>>
>> -static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm)
>> +static void makelayer(Jpeg2000EncoderContext *s, int layno, double
>> thresh, Jpeg2000Tile* tile, int final)
>>  {
>> -    int passno, res = 0;
>> -    for (passno = 0; passno < cblk->npasses; passno++){
>> -        int dr;
>> -        int64_t dd;
>> -
>> -        dr = cblk->passes[passno].rate
>> -           - (res ? cblk->passes[res-1].rate:0);
>> -        dd = cblk->passes[passno].disto
>> -           - (res ? cblk->passes[res-1].disto:0);
>> -
>> -        if (((dd * dwt_norm) >> WMSEDEC_SHIFT) * dwt_norm >= dr * lambda)
>> -            res = passno+1;
>> +    int compno, resno, bandno, precno, cblkno;
>> +    int passno;
>> +
>> +    for (compno = 0; compno < s->ncomponents; compno++) {
>> +        Jpeg2000Component *comp = &tile->comp[compno];
>> +
>> +        for (resno = 0; resno < s->codsty.nreslevels; resno++) {
>> +            Jpeg2000ResLevel *reslevel = comp->reslevel + resno;
>> +
>> +            for (precno = 0; precno < reslevel->num_precincts_x *
>> reslevel->num_precincts_y; precno++){
>> +                for (bandno = 0; bandno < reslevel->nbands ; bandno++){
>> +                    Jpeg2000Band *band = reslevel->band + bandno;
>> +                    Jpeg2000Prec *prec = band->prec + precno;
>> +
>> +                    for (cblkno = 0; cblkno < prec->nb_codeblocks_height
>> * prec->nb_codeblocks_width; cblkno++){
>> +                        Jpeg2000Cblk *cblk = prec->cblk + cblkno;
>> +                        Jpeg2000Layer *layer = &cblk->layers[layno];
>> +                        int n;
>> +
>> +                        if (layno == 0) {
>> +                            cblk->ninclpasses = 0;
>> +                        }
>> +
>> +                        n = cblk->ninclpasses;
>> +
>> +                        if (thresh < 0) {
>> +                            n = cblk->npasses;
>> +                        } else {
>> +                            for (passno = cblk->ninclpasses; passno <
>> cblk->npasses; passno++) {
>> +                                int32_t dr;
>> +                                double dd;
>> +                                Jpeg2000Pass *pass =
>> &cblk->passes[passno];
>> +
>> +                                if (n == 0) {
>> +                                    dr = pass->rate;
>> +                                    dd = (double)pass->disto;
>> +                                } else {
>> +                                    dr = pass->rate - cblk->passes[n -
>> 1].rate;
>> +                                    dd = (double)pass->disto -
>> (double)cblk->passes[n-1].disto;
>> +                                }
>> +
>> +                                if (!dr) {
>> +                                    if (dd) {
>> +                                        n = passno + 1;
>> +                                    }
>> +                                    continue;
>> +                                }
>> +
>> +                                if (thresh - (dd / dr) < DBL_EPSILON)
>> +                                    n = passno + 1;
>> +                            }
>> +                        }
>> +                        layer->npasses = n - cblk->ninclpasses;
>> +                        layer->cum_passes = n;
>> +
>> +                        if (layer->npasses == 0) {
>> +                            layer->disto = 0;
>> +                            layer->data_len = 0;
>> +                            continue;
>> +                        }
>> +
>> +                        if (cblk->ninclpasses == 0) {
>> +                            layer->data_len = cblk->passes[n - 1].rate;
>> +                            layer->data_start = cblk->data;
>> +                            layer->disto = cblk->passes[n - 1].disto;
>> +                        } else {
>> +                            layer->data_len = cblk->passes[n - 1].rate -
>> cblk->passes[cblk->ninclpasses - 1].rate;
>> +                            layer->data_start = cblk->data +
>> cblk->passes[cblk->ninclpasses - 1].rate;
>> +                            layer->disto = cblk->passes[n - 1].disto -
>> +                                           cblk->passes[cblk->ninclpasses
>> - 1].disto;
>> +                        }
>> +                        if (final) {
>> +                            cblk->ninclpasses = n;
>> +                        }
>> +                    }
>> +                }
>> +            }
>> +        }
>>      }
>> -    return res;
>>  }
>>
>> -static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
>> +static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
>>  {
>> -    int precno, compno, reslevelno, bandno, cblkno, lev;
>> +    int precno, compno, reslevelno, bandno, cblkno, lev, passno, layno;
>> +    int i;
>> +    double min = DBL_MAX;
>> +    double max = 0;
>> +    double thresh;
>> +    int tile_disto = 0;
>> +
>>      Jpeg2000CodingStyle *codsty = &s->codsty;
>>
>>      for (compno = 0; compno < s->ncomponents; compno++){
>> @@ -1107,20 +1274,70 @@ static void truncpasses(Jpeg2000EncoderContext *s,
>> Jpeg2000Tile *tile)
>>
>>              for (precno = 0; precno < reslevel->num_precincts_x *
>> reslevel->num_precincts_y; precno++){
>>                  for (bandno = 0; bandno < reslevel->nbands ; bandno++){
>> -                    int bandpos = bandno + (reslevelno > 0);
>>                      Jpeg2000Band *band = reslevel->band + bandno;
>>                      Jpeg2000Prec *prec = band->prec + precno;
>>
>>                      for (cblkno = 0; cblkno < prec->nb_codeblocks_height
>> * prec->nb_codeblocks_width; cblkno++){
>>                          Jpeg2000Cblk *cblk = prec->cblk + cblkno;
>> +                        for (passno = 0; passno < cblk->npasses;
>> passno++) {
>> +                            Jpeg2000Pass *pass = &cblk->passes[passno];
>> +                            int dr;
>> +                            double dd, drslope;
>> +
>> +                            tile_disto += pass->disto;
>> +                            if (passno == 0) {
>> +                                dr = (int32_t)pass->rate;
>> +                                dd = (double)pass->disto;
>> +                            } else {
>> +                                dr = (int32_t)(pass->rate) -
>> cblk->passes[passno - 1].rate;
>> +                                dd = (double)pass->disto -
>> (double)cblk->passes[passno - 1].disto;
>> +                            }
>> +
>> +                            if (dr <= 0)
>> +                                continue;
>>
>> -                        cblk->ninclpasses = getcut(cblk, s->lambda,
>> -                                (int64_t)dwt_norms[codsty->transform ==
>> FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15);
>> +                            drslope = dd / dr;
>> +                            if (drslope < min)
>> +                                min = drslope;
>> +
>> +                            if (drslope > max)
>> +                                max = drslope;
>> +                        }
>>                      }
>>                  }
>>              }
>>          }
>>      }
>> +
>> +    for (layno = 0; layno < s->nlayers; layno++) {
>> +        double lo = min;
>> +        double hi = max;
>> +        double stable_thresh = 0;
>> +        double good_thresh = 0;
>> +        if (!s->layer_rates[layno]) {
>> +            good_thresh = -1;
>> +        } else {
>> +            for (i = 0; i < 128; i++) {
>> +                uint8_t *stream_pos = s->buf;
>> +                int ret;
>> +                thresh = (lo + hi) / 2;
>> +                makelayer(s, layno, thresh, tile, 0);
>> +                ret = encode_packets(s, tile, (int)(tile - s->tile),
>> layno + 1);
>> +                memset(stream_pos, 0, s->buf - stream_pos);
>> +                if ((s->buf - stream_pos >
>> ceil(tile->layer_rates[layno])) || ret < 0) {
>> +                    lo = thresh;
>> +                    s->buf = stream_pos;
>> +                    continue;
>> +                }
>> +                hi = thresh;
>> +                stable_thresh = thresh;
>> +                s->buf = stream_pos;
>> +            }
>> +        }
>> +        if (good_thresh >= 0)
>> +            good_thresh = stable_thresh == 0 ? thresh : stable_thresh;
>> +        makelayer(s, layno, good_thresh, tile, 1);
>> +    }
>>  }
>>
>>  static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int
>> tileno)
>> @@ -1203,8 +1420,8 @@ static int encode_tile(Jpeg2000EncoderContext *s,
>> Jpeg2000Tile *tile, int tileno
>>      }
>>
>>      av_log(s->avctx, AV_LOG_DEBUG, "rate control\n");
>> -    truncpasses(s, tile);
>> -    if ((ret = encode_packets(s, tile, tileno)) < 0)
>> +    makelayers(s, tile);
>> +    if ((ret = encode_packets(s, tile, tileno, s->nlayers)) < 0)
>>          return ret;
>>      av_log(s->avctx, AV_LOG_DEBUG, "after rate control\n");
>>      return 0;
>> @@ -1221,6 +1438,7 @@ static void cleanup(Jpeg2000EncoderContext *s)
>>              ff_jpeg2000_cleanup(comp, codsty, 1);
>>          }
>>          av_freep(&s->tile[tileno].comp);
>> +        av_freep(&s->tile[tileno].layer_rates);
>>      }
>>      av_freep(&s->tile);
>>  }
>> @@ -1379,6 +1597,64 @@ static int encode_frame(AVCodecContext *avctx,
>> AVPacket *pkt,
>>      return 0;
>>  }
>>
>> +static int inline check_number(char* st, int* ret) {
>> +    int stlen = strlen(st);
>> +    int i;
>> +    *ret = 0;
>> +    if (stlen <= 0) {
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    for (i = 0; i < stlen; i++) {
>> +        if (st[i] >= '0' && st[i] <= '9') {
>> +            *ret = (*ret) * 10 + (st[i] - '0');
>> +        } else {
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
>> +{
>> +    int i;
>> +    char* token;
>> +    int rate;
>> +    int nlayers = 0;
>> +    if (!s->lr_str) {
>> +        s->nlayers = 1;
>> +        s->layer_rates[0] = 0;
>> +        return 0;
>> +    }
>> +
>> +    token = strtok(s->lr_str, ",");
>> +    if (!check_number(token, &rate)) {
>> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
>> +            nlayers++;
>> +    } else {
>> +            return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    while (1) {
>> +        token = strtok(NULL, ",");
>> +        if (!token)
>> +            break;
>> +        if (!check_number(token, &rate)) {
>> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
>> +            nlayers++;
>> +        } else {
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +    }
>> +
>> +    for (i = 1; i < nlayers; i++) {
>> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +    }
>> +    s->nlayers = nlayers;
>> +    return 0;
>> +}
>> +
>>  static av_cold int j2kenc_init(AVCodecContext *avctx)
>>  {
>>      int i, ret;
>> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext
>> *avctx)
>>
>>      s->avctx = avctx;
>>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
>> +    if (parse_layer_rates(s)) {
>> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with
>> 1 layer.\n");
>> +        s->nlayers = 1;
>> +        s->layer_rates[0] = 0;
>> +    }
>>
>>  #if FF_API_PRIVATE_OPT
>>  FF_DISABLE_DEPRECATION_WARNINGS
>> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
>>      memset(codsty->log2_prec_heights, 15,
>> sizeof(codsty->log2_prec_heights));
>>      codsty->nreslevels2decode=
>>      codsty->nreslevels       = 7;
>> +    codsty->nlayers          = s->nlayers;
>>      codsty->log2_cblk_width  = 4;
>>      codsty->log2_cblk_height = 4;
>>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
>> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
>>      { "rpcl",          NULL,                OFFSET(prog),
>> AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,
>> 0,           VE, "prog" },
>>      { "pcrl",          NULL,                OFFSET(prog),
>> AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,
>> 0,           VE, "prog" },
>>      { "cprl",          NULL,                OFFSET(prog),
>> AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,
>> 0,           VE, "prog" },
>> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),
>> AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
>>      { NULL }
>>  };
>>
>> diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c
>> index 70c25a0ca2..1c294ad9e6 100644
>> --- a/libavcodec/jpeg2000.c
>> +++ b/libavcodec/jpeg2000.c
>> @@ -261,9 +261,11 @@ static void init_band_stepsize(AVCodecContext *avctx,
>>          band->f_stepsize *= 0.5;
>>  }
>>
>> -static int init_prec(Jpeg2000Band *band,
>> +static int init_prec(AVCodecContext *avctx,
>> +                     Jpeg2000Band *band,
>>                       Jpeg2000ResLevel *reslevel,
>>                       Jpeg2000Component *comp,
>> +                     Jpeg2000CodingStyle *codsty,
>>                       int precno, int bandno, int reslevelno,
>>                       int log2_band_prec_width,
>>                       int log2_band_prec_height)
>> @@ -366,6 +368,11 @@ static int init_prec(Jpeg2000Band *band,
>>          cblk->lblock    = 3;
>>          cblk->length    = 0;
>>          cblk->npasses   = 0;
>> +        if (av_codec_is_encoder(avctx->codec)) {
>> +            cblk->layers = av_mallocz_array(codsty->nlayers,
>> sizeof(*cblk->layers));
>> +            if (!cblk->layers)
>> +                return AVERROR(ENOMEM);
>> +        }
>>      }
>>
>>      return 0;
>> @@ -439,7 +446,7 @@ static int init_band(AVCodecContext *avctx,
>>          return AVERROR(ENOMEM);
>>
>>      for (precno = 0; precno < nb_precincts; precno++) {
>> -        ret = init_prec(band, reslevel, comp,
>> +        ret = init_prec(avctx, band, reslevel, comp, codsty,
>>                          precno, bandno, reslevelno,
>>                          log2_band_prec_width, log2_band_prec_height);
>>          if (ret < 0)
>> @@ -614,6 +621,8 @@ void ff_jpeg2000_cleanup(Jpeg2000Component *comp,
>> Jpeg2000CodingStyle *codsty, i
>>                              av_freep(&cblk->passes);
>>                              av_freep(&cblk->lengthinc);
>>                              av_freep(&cblk->data_start);
>> +                            if (isencoder)
>> +                                av_freep(&cblk->layers);
>>                          }
>>                          av_freep(&prec->cblk);
>>                      }
>> diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
>> index fee9607e86..3c6e8ede16 100644
>> --- a/libavcodec/jpeg2000.h
>> +++ b/libavcodec/jpeg2000.h
>> @@ -162,10 +162,19 @@ typedef struct Jpeg2000Pass {
>>      int flushed_len;
>>  } Jpeg2000Pass;
>>
>> +typedef struct Jpeg2000Layer {
>> +    uint8_t *data_start;
>> +    int data_len;
>> +    int npasses;
>> +    double disto;
>> +    int cum_passes;
>> +} Jpeg2000Layer;
>> +
>>  typedef struct Jpeg2000Cblk {
>>      uint8_t npasses;
>>      uint8_t ninclpasses; // number coding of passes included in
>> codestream
>>      uint8_t nonzerobits;
>> +    uint8_t incl;
>>      uint16_t length;
>>      uint16_t *lengthinc;
>>      uint8_t nb_lengthinc;
>> @@ -176,6 +185,7 @@ typedef struct Jpeg2000Cblk {
>>      int nb_terminationsinc;
>>      int *data_start;
>>      Jpeg2000Pass *passes;
>> +    Jpeg2000Layer *layers;
>>      int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}}
>>  } Jpeg2000Cblk; // code block
>>
>> --
>> 2.17.1
>>
>
> This patch seems to be breaking FATE.
> I believe that the error is because the patch modifies the encoder
> such that the encoded files will be slightly different now.
> How can this be handled?

Run fate. Make sure that you have SAMPLES set and rsynced with server.

make fate-name-of-target GEN=1

To generate new hashes for encodes.

Please read http://ffmpeg.org/fate.html


> --
> -------------
> Gautam |
> _______________________________________________
> 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".
Andreas Rheinhardt Aug. 19, 2020, 7:25 p.m. UTC | #3
Gautam Ramakrishnan:
> On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
>>
>> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>>
>> This patch allows setting a compression ratio and to
>> set multiple layers. The user has to input a compression
>> ratio for each layer.
>> The per layer compression ration can be set as follows:
>> -layer_rates "r1,r2,...rn"
>> for to create 'n' layers.
>> ---
>>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
>>  libavcodec/jpeg2000.c |  13 +-
>>  libavcodec/jpeg2000.h |  10 +
>>  3 files changed, 384 insertions(+), 82 deletions(-)
>>

[...]

>> +static int inline check_number(char* st, int* ret) {
>> +    int stlen = strlen(st);
>> +    int i;
>> +    *ret = 0;
>> +    if (stlen <= 0) {
>> +        return AVERROR_INVALIDDATA;
>> +    }
>> +    for (i = 0; i < stlen; i++) {
>> +        if (st[i] >= '0' && st[i] <= '9') {
>> +            *ret = (*ret) * 10 + (st[i] - '0');
>> +        } else {
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
>> +{
>> +    int i;
>> +    char* token;
>> +    int rate;
>> +    int nlayers = 0;
>> +    if (!s->lr_str) {
>> +        s->nlayers = 1;
>> +        s->layer_rates[0] = 0;
>> +        return 0;
>> +    }
>> +
>> +    token = strtok(s->lr_str, ",");
>> +    if (!check_number(token, &rate)) {
>> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
>> +            nlayers++;
>> +    } else {
>> +            return AVERROR_INVALIDDATA;
>> +    }
>> +
>> +    while (1) {
>> +        token = strtok(NULL, ",");
>> +        if (!token)
>> +            break;
>> +        if (!check_number(token, &rate)) {
>> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
>> +            nlayers++;
>> +        } else {
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +    }
>> +
>> +    for (i = 1; i < nlayers; i++) {
>> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
>> +            return AVERROR_INVALIDDATA;
>> +        }
>> +    }
>> +    s->nlayers = nlayers;
>> +    return 0;
>> +}
>> +
>>  static av_cold int j2kenc_init(AVCodecContext *avctx)
>>  {
>>      int i, ret;
>> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext *avctx)
>>
>>      s->avctx = avctx;
>>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
>> +    if (parse_layer_rates(s)) {
>> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
>> +        s->nlayers = 1;
>> +        s->layer_rates[0] = 0;
>> +    }
>>
>>  #if FF_API_PRIVATE_OPT
>>  FF_DISABLE_DEPRECATION_WARNINGS
>> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
>>      memset(codsty->log2_prec_heights, 15, sizeof(codsty->log2_prec_heights));
>>      codsty->nreslevels2decode=
>>      codsty->nreslevels       = 7;
>> +    codsty->nlayers          = s->nlayers;
>>      codsty->log2_cblk_width  = 4;
>>      codsty->log2_cblk_height = 4;
>>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
>> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
>>      { "rpcl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,         0,           VE, "prog" },
>>      { "pcrl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,         0,           VE, "prog" },
>>      { "cprl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,         0,           VE, "prog" },
>> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
>>      { NULL }
>>  };
>>
>>
>> --
>> 2.17.1
>>
> 
> This patch seems to be breaking FATE.
> I believe that the error is because the patch modifies the encoder
> such that the encoded files will be slightly different now.
> How can this be handled?
> 
Look at the created files and see if they are ok (ideally, they should
show an improvement). If not, modify/drop your patch; otherwise update
the reference files by running make fate GEN=1.

Just by looking at https://patchwork.ffmpeg.org/check/15622/ (i.e.
without taking a look at the actual files and without having run fate
with your patch myself) I can say that your patch is not ok:

-8bb707e596f97451fd325dec2dd610a7 *tests/data/fate/vsynth1-jpeg2000-97.avi
-3654620 tests/data/fate/vsynth1-jpeg2000-97.avi
-5073771a78e1f5366a7eb0df341662fc
*tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
-stddev:    4.23 PSNR: 35.59 MAXDIFF:   53 bytes:  7603200/  7603200
+5178195043ecfd671d79a194611d3573 *tests/data/fate/vsynth1-jpeg2000-97.avi
+9986568 tests/data/fate/vsynth1-jpeg2000-97.avi
+023623c97973489ba9cf1618b3cd25d3
*tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
+stddev:    3.58 PSNR: 37.04 MAXDIFF:   49 bytes:  7603200/  7603200

The size of the created files increased from 3654620 to 9986568,
although it seems to me that using multiple layers is supposed to be opt-in.

Besides that: You are using an int[100] array to store the layer rates.
Yet I don't see any check in parse_layer_rates() that actually ensures
that there is no write beyond the end of the array. Besides that
check_number stores the return value of strlen in an int (may be
truncating) and does not check in the calculation.

- Andreas
Moritz Barsnick Aug. 20, 2020, 10:06 a.m. UTC | #4
On Wed, Aug 19, 2020 at 17:51:02 +0530, gautamramk@gmail.com wrote:

Minor nits:

> +static void compute_rates(Jpeg2000EncoderContext* s)
> +{
> +    int i, j;
> +    int layno, compno;
> +    for (i = 0; i < s->numYtiles; i++) {
> +        for (j = 0; j < s->numXtiles; j++) {
> +            Jpeg2000Tile *tile = &s->tile[s->numXtiles * i + j];
> +            for (compno = 0; compno < s->ncomponents; compno++) {
> +                int tilew = tile->comp[compno].coord[0][1] - tile->comp[compno].coord[0][0];
> +                int tileh = tile->comp[compno].coord[1][1] - tile->comp[compno].coord[1][0];
> +                int scale = (compno?1 << s->chroma_shift[0]:1) * (compno?1 << s->chroma_shift[1]:1);
> +                for (layno = 0; layno < s->nlayers; layno++) {
> +                    if (s->layer_rates[layno] > 0.0f) {

Isn't this left hand an array of ints? Why compare an int against a
float? (And even if it were a double: Why compare a double against a
float?)

> +                        tile->layer_rates[layno] = 0.0f;

This left hand is a double. Why assign an explicit float, which the
compiler (or preprocessor?) needs to convert back to double? (I.e. just
"0.0", that's double.)

> +        for (bandno = 0; bandno < rlevel->nbands; bandno++){

Missing space before bracket.

> +            &&  rlevel->band[bandno].coord[1][0] < rlevel->band[bandno].coord[1][1]){

Missing space before bracket.

 +        Jpeg2000Band *band = rlevel->band + bandno;

Couldn't this also be "= rlevel->band[bandno]", as above?

> +                if (layno == nlayers - 1 && cblk->layers->cum_passes){

Missing space before bracket.

> +                if (cblk->layers[layno].npasses){

Missing space before bracket.

>      // lay-rlevel-comp-pos progression
> -    switch (s->prog) {
> +        switch (s->prog) {
>      case JPEG2000_PGOD_LRCP:

Stray incorrect indentation change.

> +                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                qntsty->nguardbits, packetno++, nlayers)) < 0)

Peculiar indentation, and consider shorting the first line even more.

> +                for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> +                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                qntsty->nguardbits, packetno++, nlayers)) < 0)

Ditto.

> +                    for (layno = 0; layno < nlayers; layno++){
> +                        if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                qntsty->nguardbits, packetno++, nlayers)) < 0)

Ditto.

> +                        for (layno = 0; layno < nlayers; layno++){
> +                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                    qntsty->nguardbits, packetno++, nlayers)) < 0)

Ditto.
Also missing space before bracket.
Also please use whitespace around operators.

(This functionality seems repetetive, perhaps this can be refactored?
Just wondering.)

> +                        for (layno = 0; layno < nlayers; layno++){
> +                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> +                                    qntsty->nguardbits, packetno++, nlayers)) < 0)

Again. ;)

> +                                if (n == 0) {
> +                                    dr = pass->rate;
> +                                    dd = (double)pass->disto;

Redundant typecast from int32_t to double.

> +                                } else {
> +                                    dr = pass->rate - cblk->passes[n - 1].rate;
> +                                    dd = (double)pass->disto - (double)cblk->passes[n-1].disto;

Are you casting these ints to double before subtracting them? why not
afterwards (and make the typecast implicit again on assignment)?

> +                                if (!dr) {
> +                                    if (dd) {
> +                                        n = passno + 1;

You're comparing a double against 0.0, in this case it might be
appropriate to make this explicit.

> +                                dr = (int32_t)pass->rate;
> +                                dd = (double)pass->disto;
> +                            } else {
> +                                dr = (int32_t)(pass->rate) - cblk->passes[passno - 1].rate;
> +                                dd = (double)pass->disto - (double)cblk->passes[passno - 1].disto;

Ditto.

> +        double lo = min;
> +        double hi = max;
> +        double stable_thresh = 0;
> +        double good_thresh = 0;

Cosmetic: 0.0

> +            good_thresh = -1;

Cosmetic: -1.0 (but I think I see your intent of marking this as
invalid, right?).

> +        if (good_thresh >= 0)

Cosmetic: 0.0

> +            good_thresh = stable_thresh == 0 ? thresh : stable_thresh;

Cosmetic: 0.0

> +            s->layer_rates[0] = rate <= 1 ? 0:rate;

Use whitespace to separate operators (also ':').

> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;

Ditto.

> +    if (parse_layer_rates(s)) {
> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");

I suggest "Encoding with" instead of "Shall encode with". Not important
though.

> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },

I'm not happy with the capitalization, but that's what the rest uses,
so *sigh*.

Cheers,
Moritz
Gautam Ramakrishnan Aug. 20, 2020, 1:33 p.m. UTC | #5
On Thu, Aug 20, 2020 at 12:55 AM Andreas Rheinhardt
<andreas.rheinhardt@gmail.com> wrote:
>
> Gautam Ramakrishnan:
> > On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
> >>
> >> From: Gautam Ramakrishnan <gautamramk@gmail.com>
> >>
> >> This patch allows setting a compression ratio and to
> >> set multiple layers. The user has to input a compression
> >> ratio for each layer.
> >> The per layer compression ration can be set as follows:
> >> -layer_rates "r1,r2,...rn"
> >> for to create 'n' layers.
> >> ---
> >>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
> >>  libavcodec/jpeg2000.c |  13 +-
> >>  libavcodec/jpeg2000.h |  10 +
> >>  3 files changed, 384 insertions(+), 82 deletions(-)
> >>
>
> [...]
>
> >> +static int inline check_number(char* st, int* ret) {
> >> +    int stlen = strlen(st);
> >> +    int i;
> >> +    *ret = 0;
> >> +    if (stlen <= 0) {
> >> +        return AVERROR_INVALIDDATA;
> >> +    }
> >> +    for (i = 0; i < stlen; i++) {
> >> +        if (st[i] >= '0' && st[i] <= '9') {
> >> +            *ret = (*ret) * 10 + (st[i] - '0');
> >> +        } else {
> >> +            return AVERROR_INVALIDDATA;
> >> +        }
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
> >> +{
> >> +    int i;
> >> +    char* token;
> >> +    int rate;
> >> +    int nlayers = 0;
> >> +    if (!s->lr_str) {
> >> +        s->nlayers = 1;
> >> +        s->layer_rates[0] = 0;
> >> +        return 0;
> >> +    }
> >> +
> >> +    token = strtok(s->lr_str, ",");
> >> +    if (!check_number(token, &rate)) {
> >> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
> >> +            nlayers++;
> >> +    } else {
> >> +            return AVERROR_INVALIDDATA;
> >> +    }
> >> +
> >> +    while (1) {
> >> +        token = strtok(NULL, ",");
> >> +        if (!token)
> >> +            break;
> >> +        if (!check_number(token, &rate)) {
> >> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
> >> +            nlayers++;
> >> +        } else {
> >> +            return AVERROR_INVALIDDATA;
> >> +        }
> >> +    }
> >> +
> >> +    for (i = 1; i < nlayers; i++) {
> >> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
> >> +            return AVERROR_INVALIDDATA;
> >> +        }
> >> +    }
> >> +    s->nlayers = nlayers;
> >> +    return 0;
> >> +}
> >> +
> >>  static av_cold int j2kenc_init(AVCodecContext *avctx)
> >>  {
> >>      int i, ret;
> >> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext *avctx)
> >>
> >>      s->avctx = avctx;
> >>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
> >> +    if (parse_layer_rates(s)) {
> >> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
> >> +        s->nlayers = 1;
> >> +        s->layer_rates[0] = 0;
> >> +    }
> >>
> >>  #if FF_API_PRIVATE_OPT
> >>  FF_DISABLE_DEPRECATION_WARNINGS
> >> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
> >>      memset(codsty->log2_prec_heights, 15, sizeof(codsty->log2_prec_heights));
> >>      codsty->nreslevels2decode=
> >>      codsty->nreslevels       = 7;
> >> +    codsty->nlayers          = s->nlayers;
> >>      codsty->log2_cblk_width  = 4;
> >>      codsty->log2_cblk_height = 4;
> >>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
> >> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
> >>      { "rpcl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,         0,           VE, "prog" },
> >>      { "pcrl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,         0,           VE, "prog" },
> >>      { "cprl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,         0,           VE, "prog" },
> >> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
> >>      { NULL }
> >>  };
> >>
> >>
> >> --
> >> 2.17.1
> >>
> >
> > This patch seems to be breaking FATE.
> > I believe that the error is because the patch modifies the encoder
> > such that the encoded files will be slightly different now.
> > How can this be handled?
> >
> Look at the created files and see if they are ok (ideally, they should
> show an improvement). If not, modify/drop your patch; otherwise update
> the reference files by running make fate GEN=1.
>
> Just by looking at https://patchwork.ffmpeg.org/check/15622/ (i.e.
> without taking a look at the actual files and without having run fate
> with your patch myself) I can say that your patch is not ok:
>
> -8bb707e596f97451fd325dec2dd610a7 *tests/data/fate/vsynth1-jpeg2000-97.avi
> -3654620 tests/data/fate/vsynth1-jpeg2000-97.avi
> -5073771a78e1f5366a7eb0df341662fc
> *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
> -stddev:    4.23 PSNR: 35.59 MAXDIFF:   53 bytes:  7603200/  7603200
> +5178195043ecfd671d79a194611d3573 *tests/data/fate/vsynth1-jpeg2000-97.avi
> +9986568 tests/data/fate/vsynth1-jpeg2000-97.avi
> +023623c97973489ba9cf1618b3cd25d3
> *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
> +stddev:    3.58 PSNR: 37.04 MAXDIFF:   49 bytes:  7603200/  7603200
>
> The size of the created files increased from 3654620 to 9986568,
> although it seems to me that using multiple layers is supposed to be opt-in.
>
> Besides that: You are using an int[100] array to store the layer rates.
> Yet I don't see any check in parse_layer_rates() that actually ensures
> that there is no write beyond the end of the array. Besides that
> check_number stores the return value of strlen in an int (may be
> truncating) and does not check in the calculation.
>
> - Andreas
> _______________________________________________
> 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".

Thanks a lot Andreas. Originally, the encoder used to remove some data
according to a metric called picture quality.
This picture quality is a member of avframe.
However, right now, I have removed that usage and am compressing only
based on the compression rates provided by the user.
Should I preserve the old functionality if the user does not provide a
"layer_rates"
option?
Gautam Ramakrishnan Aug. 20, 2020, 1:35 p.m. UTC | #6
On Thu, Aug 20, 2020 at 3:36 PM Moritz Barsnick <barsnick@gmx.net> wrote:
>
> On Wed, Aug 19, 2020 at 17:51:02 +0530, gautamramk@gmail.com wrote:
>
> Minor nits:
>
> > +static void compute_rates(Jpeg2000EncoderContext* s)
> > +{
> > +    int i, j;
> > +    int layno, compno;
> > +    for (i = 0; i < s->numYtiles; i++) {
> > +        for (j = 0; j < s->numXtiles; j++) {
> > +            Jpeg2000Tile *tile = &s->tile[s->numXtiles * i + j];
> > +            for (compno = 0; compno < s->ncomponents; compno++) {
> > +                int tilew = tile->comp[compno].coord[0][1] - tile->comp[compno].coord[0][0];
> > +                int tileh = tile->comp[compno].coord[1][1] - tile->comp[compno].coord[1][0];
> > +                int scale = (compno?1 << s->chroma_shift[0]:1) * (compno?1 << s->chroma_shift[1]:1);
> > +                for (layno = 0; layno < s->nlayers; layno++) {
> > +                    if (s->layer_rates[layno] > 0.0f) {
>
> Isn't this left hand an array of ints? Why compare an int against a
> float? (And even if it were a double: Why compare a double against a
> float?)
>
> > +                        tile->layer_rates[layno] = 0.0f;
>
> This left hand is a double. Why assign an explicit float, which the
> compiler (or preprocessor?) needs to convert back to double? (I.e. just
> "0.0", that's double.)
>
> > +        for (bandno = 0; bandno < rlevel->nbands; bandno++){
>
> Missing space before bracket.
>
> > +            &&  rlevel->band[bandno].coord[1][0] < rlevel->band[bandno].coord[1][1]){
>
> Missing space before bracket.
>
>  +        Jpeg2000Band *band = rlevel->band + bandno;
>
> Couldn't this also be "= rlevel->band[bandno]", as above?
>
> > +                if (layno == nlayers - 1 && cblk->layers->cum_passes){
>
> Missing space before bracket.
>
> > +                if (cblk->layers[layno].npasses){
>
> Missing space before bracket.
>
> >      // lay-rlevel-comp-pos progression
> > -    switch (s->prog) {
> > +        switch (s->prog) {
> >      case JPEG2000_PGOD_LRCP:
>
> Stray incorrect indentation change.
>
> > +                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> > +                                qntsty->nguardbits, packetno++, nlayers)) < 0)
>
> Peculiar indentation, and consider shorting the first line even more.
>
> > +                for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
> > +                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> > +                                qntsty->nguardbits, packetno++, nlayers)) < 0)
>
> Ditto.
>
> > +                    for (layno = 0; layno < nlayers; layno++){
> > +                        if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> > +                                qntsty->nguardbits, packetno++, nlayers)) < 0)
>
> Ditto.
>
> > +                        for (layno = 0; layno < nlayers; layno++){
> > +                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> > +                                    qntsty->nguardbits, packetno++, nlayers)) < 0)
>
> Ditto.
> Also missing space before bracket.
> Also please use whitespace around operators.
>
> (This functionality seems repetetive, perhaps this can be refactored?
> Just wondering.)
This was a part of a previous patch. I'll try working on refactoring
this separately.
>
> > +                        for (layno = 0; layno < nlayers; layno++){
> > +                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> > +                                    qntsty->nguardbits, packetno++, nlayers)) < 0)
>
> Again. ;)
>
> > +                                if (n == 0) {
> > +                                    dr = pass->rate;
> > +                                    dd = (double)pass->disto;
>
> Redundant typecast from int32_t to double.
>
> > +                                } else {
> > +                                    dr = pass->rate - cblk->passes[n - 1].rate;
> > +                                    dd = (double)pass->disto - (double)cblk->passes[n-1].disto;
>
> Are you casting these ints to double before subtracting them? why not
> afterwards (and make the typecast implicit again on assignment)?
>
> > +                                if (!dr) {
> > +                                    if (dd) {
> > +                                        n = passno + 1;
>
> You're comparing a double against 0.0, in this case it might be
> appropriate to make this explicit.
>
> > +                                dr = (int32_t)pass->rate;
> > +                                dd = (double)pass->disto;
> > +                            } else {
> > +                                dr = (int32_t)(pass->rate) - cblk->passes[passno - 1].rate;
> > +                                dd = (double)pass->disto - (double)cblk->passes[passno - 1].disto;
>
> Ditto.
>
> > +        double lo = min;
> > +        double hi = max;
> > +        double stable_thresh = 0;
> > +        double good_thresh = 0;
>
> Cosmetic: 0.0
>
> > +            good_thresh = -1;
>
> Cosmetic: -1.0 (but I think I see your intent of marking this as
> invalid, right?).
>
> > +        if (good_thresh >= 0)
>
> Cosmetic: 0.0
>
> > +            good_thresh = stable_thresh == 0 ? thresh : stable_thresh;
>
> Cosmetic: 0.0
>
> > +            s->layer_rates[0] = rate <= 1 ? 0:rate;
>
> Use whitespace to separate operators (also ':').
>
> > +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
>
> Ditto.
>
> > +    if (parse_layer_rates(s)) {
> > +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
>
> I suggest "Encoding with" instead of "Shall encode with". Not important
> though.
>
> > +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
>
> I'm not happy with the capitalization, but that's what the rest uses,
> so *sigh*.
>
> Cheers,
> Moritz
> _______________________________________________
> 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".

Thanks a lot for the suggestions Moritz, I'll make these changes and resubmit.
Andreas Rheinhardt Aug. 20, 2020, 2:55 p.m. UTC | #7
Gautam Ramakrishnan:
> On Thu, Aug 20, 2020 at 12:55 AM Andreas Rheinhardt
> <andreas.rheinhardt@gmail.com> wrote:
>>
>> Gautam Ramakrishnan:
>>> On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
>>>>
>>>> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>>>>
>>>> This patch allows setting a compression ratio and to
>>>> set multiple layers. The user has to input a compression
>>>> ratio for each layer.
>>>> The per layer compression ration can be set as follows:
>>>> -layer_rates "r1,r2,...rn"
>>>> for to create 'n' layers.
>>>> ---
>>>>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
>>>>  libavcodec/jpeg2000.c |  13 +-
>>>>  libavcodec/jpeg2000.h |  10 +
>>>>  3 files changed, 384 insertions(+), 82 deletions(-)
>>>>
>>
>> [...]
>>
>>>> +static int inline check_number(char* st, int* ret) {
>>>> +    int stlen = strlen(st);
>>>> +    int i;
>>>> +    *ret = 0;
>>>> +    if (stlen <= 0) {
>>>> +        return AVERROR_INVALIDDATA;
>>>> +    }
>>>> +    for (i = 0; i < stlen; i++) {
>>>> +        if (st[i] >= '0' && st[i] <= '9') {
>>>> +            *ret = (*ret) * 10 + (st[i] - '0');
>>>> +        } else {
>>>> +            return AVERROR_INVALIDDATA;
>>>> +        }
>>>> +    }
>>>> +    return 0;
>>>> +}
>>>> +
>>>> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
>>>> +{
>>>> +    int i;
>>>> +    char* token;
>>>> +    int rate;
>>>> +    int nlayers = 0;
>>>> +    if (!s->lr_str) {
>>>> +        s->nlayers = 1;
>>>> +        s->layer_rates[0] = 0;
>>>> +        return 0;
>>>> +    }
>>>> +
>>>> +    token = strtok(s->lr_str, ",");
>>>> +    if (!check_number(token, &rate)) {
>>>> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
>>>> +            nlayers++;
>>>> +    } else {
>>>> +            return AVERROR_INVALIDDATA;
>>>> +    }
>>>> +
>>>> +    while (1) {
>>>> +        token = strtok(NULL, ",");
>>>> +        if (!token)
>>>> +            break;
>>>> +        if (!check_number(token, &rate)) {
>>>> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
>>>> +            nlayers++;
>>>> +        } else {
>>>> +            return AVERROR_INVALIDDATA;
>>>> +        }
>>>> +    }
>>>> +
>>>> +    for (i = 1; i < nlayers; i++) {
>>>> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
>>>> +            return AVERROR_INVALIDDATA;
>>>> +        }
>>>> +    }
>>>> +    s->nlayers = nlayers;
>>>> +    return 0;
>>>> +}
>>>> +
>>>>  static av_cold int j2kenc_init(AVCodecContext *avctx)
>>>>  {
>>>>      int i, ret;
>>>> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext *avctx)
>>>>
>>>>      s->avctx = avctx;
>>>>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
>>>> +    if (parse_layer_rates(s)) {
>>>> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
>>>> +        s->nlayers = 1;
>>>> +        s->layer_rates[0] = 0;
>>>> +    }
>>>>
>>>>  #if FF_API_PRIVATE_OPT
>>>>  FF_DISABLE_DEPRECATION_WARNINGS
>>>> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
>>>>      memset(codsty->log2_prec_heights, 15, sizeof(codsty->log2_prec_heights));
>>>>      codsty->nreslevels2decode=
>>>>      codsty->nreslevels       = 7;
>>>> +    codsty->nlayers          = s->nlayers;
>>>>      codsty->log2_cblk_width  = 4;
>>>>      codsty->log2_cblk_height = 4;
>>>>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
>>>> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
>>>>      { "rpcl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,         0,           VE, "prog" },
>>>>      { "pcrl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,         0,           VE, "prog" },
>>>>      { "cprl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,         0,           VE, "prog" },
>>>> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
>>>>      { NULL }
>>>>  };
>>>>
>>>>
>>>> --
>>>> 2.17.1
>>>>
>>>
>>> This patch seems to be breaking FATE.
>>> I believe that the error is because the patch modifies the encoder
>>> such that the encoded files will be slightly different now.
>>> How can this be handled?
>>>
>> Look at the created files and see if they are ok (ideally, they should
>> show an improvement). If not, modify/drop your patch; otherwise update
>> the reference files by running make fate GEN=1.
>>
>> Just by looking at https://patchwork.ffmpeg.org/check/15622/ (i.e.
>> without taking a look at the actual files and without having run fate
>> with your patch myself) I can say that your patch is not ok:
>>
>> -8bb707e596f97451fd325dec2dd610a7 *tests/data/fate/vsynth1-jpeg2000-97.avi
>> -3654620 tests/data/fate/vsynth1-jpeg2000-97.avi
>> -5073771a78e1f5366a7eb0df341662fc
>> *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
>> -stddev:    4.23 PSNR: 35.59 MAXDIFF:   53 bytes:  7603200/  7603200
>> +5178195043ecfd671d79a194611d3573 *tests/data/fate/vsynth1-jpeg2000-97.avi
>> +9986568 tests/data/fate/vsynth1-jpeg2000-97.avi
>> +023623c97973489ba9cf1618b3cd25d3
>> *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
>> +stddev:    3.58 PSNR: 37.04 MAXDIFF:   49 bytes:  7603200/  7603200
>>
>> The size of the created files increased from 3654620 to 9986568,
>> although it seems to me that using multiple layers is supposed to be opt-in.
>>
>> Besides that: You are using an int[100] array to store the layer rates.
>> Yet I don't see any check in parse_layer_rates() that actually ensures
>> that there is no write beyond the end of the array. Besides that
>> check_number stores the return value of strlen in an int (may be
>> truncating) and does not check in the calculation.
>>
>> - Andreas
>> _______________________________________________
>> 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".
> 
> Thanks a lot Andreas. Originally, the encoder used to remove some data
> according to a metric called picture quality.
> This picture quality is a member of avframe.
> However, right now, I have removed that usage and am compressing only
> based on the compression rates provided by the user.

This is confusing: If I am not mistaken, then the encoder did not only
use to remove some data based upon the picture quality metric; it (i.e.
git master) still does and it is this very patch that intends to change
this. Typically we use the present tense to talk about current git master.

> Should I preserve the old functionality if the user does not provide a
> "layer_rates"
> option?
> 

If your patch breaks the current way of signalling the desired quality
and leads to a massive increase in filesize for people using the current
way of signalling the quality, then you will be breaking lots of current
users' usecases. I don't think that's acceptable.

- Andreas
Gautam Ramakrishnan Aug. 20, 2020, 3:16 p.m. UTC | #8
On Thu, Aug 20, 2020 at 8:25 PM Andreas Rheinhardt
<andreas.rheinhardt@gmail.com> wrote:
>
> Gautam Ramakrishnan:
> > On Thu, Aug 20, 2020 at 12:55 AM Andreas Rheinhardt
> > <andreas.rheinhardt@gmail.com> wrote:
> >>
> >> Gautam Ramakrishnan:
> >>> On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
> >>>>
> >>>> From: Gautam Ramakrishnan <gautamramk@gmail.com>
> >>>>
> >>>> This patch allows setting a compression ratio and to
> >>>> set multiple layers. The user has to input a compression
> >>>> ratio for each layer.
> >>>> The per layer compression ration can be set as follows:
> >>>> -layer_rates "r1,r2,...rn"
> >>>> for to create 'n' layers.
> >>>> ---
> >>>>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
> >>>>  libavcodec/jpeg2000.c |  13 +-
> >>>>  libavcodec/jpeg2000.h |  10 +
> >>>>  3 files changed, 384 insertions(+), 82 deletions(-)
> >>>>
> >>
> >> [...]
> >>
> >>>> +static int inline check_number(char* st, int* ret) {
> >>>> +    int stlen = strlen(st);
> >>>> +    int i;
> >>>> +    *ret = 0;
> >>>> +    if (stlen <= 0) {
> >>>> +        return AVERROR_INVALIDDATA;
> >>>> +    }
> >>>> +    for (i = 0; i < stlen; i++) {
> >>>> +        if (st[i] >= '0' && st[i] <= '9') {
> >>>> +            *ret = (*ret) * 10 + (st[i] - '0');
> >>>> +        } else {
> >>>> +            return AVERROR_INVALIDDATA;
> >>>> +        }
> >>>> +    }
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
> >>>> +{
> >>>> +    int i;
> >>>> +    char* token;
> >>>> +    int rate;
> >>>> +    int nlayers = 0;
> >>>> +    if (!s->lr_str) {
> >>>> +        s->nlayers = 1;
> >>>> +        s->layer_rates[0] = 0;
> >>>> +        return 0;
> >>>> +    }
> >>>> +
> >>>> +    token = strtok(s->lr_str, ",");
> >>>> +    if (!check_number(token, &rate)) {
> >>>> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
> >>>> +            nlayers++;
> >>>> +    } else {
> >>>> +            return AVERROR_INVALIDDATA;
> >>>> +    }
> >>>> +
> >>>> +    while (1) {
> >>>> +        token = strtok(NULL, ",");
> >>>> +        if (!token)
> >>>> +            break;
> >>>> +        if (!check_number(token, &rate)) {
> >>>> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
> >>>> +            nlayers++;
> >>>> +        } else {
> >>>> +            return AVERROR_INVALIDDATA;
> >>>> +        }
> >>>> +    }
> >>>> +
> >>>> +    for (i = 1; i < nlayers; i++) {
> >>>> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
> >>>> +            return AVERROR_INVALIDDATA;
> >>>> +        }
> >>>> +    }
> >>>> +    s->nlayers = nlayers;
> >>>> +    return 0;
> >>>> +}
> >>>> +
> >>>>  static av_cold int j2kenc_init(AVCodecContext *avctx)
> >>>>  {
> >>>>      int i, ret;
> >>>> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext *avctx)
> >>>>
> >>>>      s->avctx = avctx;
> >>>>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
> >>>> +    if (parse_layer_rates(s)) {
> >>>> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
> >>>> +        s->nlayers = 1;
> >>>> +        s->layer_rates[0] = 0;
> >>>> +    }
> >>>>
> >>>>  #if FF_API_PRIVATE_OPT
> >>>>  FF_DISABLE_DEPRECATION_WARNINGS
> >>>> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
> >>>>      memset(codsty->log2_prec_heights, 15, sizeof(codsty->log2_prec_heights));
> >>>>      codsty->nreslevels2decode=
> >>>>      codsty->nreslevels       = 7;
> >>>> +    codsty->nlayers          = s->nlayers;
> >>>>      codsty->log2_cblk_width  = 4;
> >>>>      codsty->log2_cblk_height = 4;
> >>>>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
> >>>> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
> >>>>      { "rpcl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,         0,           VE, "prog" },
> >>>>      { "pcrl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,         0,           VE, "prog" },
> >>>>      { "cprl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,         0,           VE, "prog" },
> >>>> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
> >>>>      { NULL }
> >>>>  };
> >>>>
> >>>>
> >>>> --
> >>>> 2.17.1
> >>>>
> >>>
> >>> This patch seems to be breaking FATE.
> >>> I believe that the error is because the patch modifies the encoder
> >>> such that the encoded files will be slightly different now.
> >>> How can this be handled?
> >>>
> >> Look at the created files and see if they are ok (ideally, they should
> >> show an improvement). If not, modify/drop your patch; otherwise update
> >> the reference files by running make fate GEN=1.
> >>
> >> Just by looking at https://patchwork.ffmpeg.org/check/15622/ (i.e.
> >> without taking a look at the actual files and without having run fate
> >> with your patch myself) I can say that your patch is not ok:
> >>
> >> -8bb707e596f97451fd325dec2dd610a7 *tests/data/fate/vsynth1-jpeg2000-97.avi
> >> -3654620 tests/data/fate/vsynth1-jpeg2000-97.avi
> >> -5073771a78e1f5366a7eb0df341662fc
> >> *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
> >> -stddev:    4.23 PSNR: 35.59 MAXDIFF:   53 bytes:  7603200/  7603200
> >> +5178195043ecfd671d79a194611d3573 *tests/data/fate/vsynth1-jpeg2000-97.avi
> >> +9986568 tests/data/fate/vsynth1-jpeg2000-97.avi
> >> +023623c97973489ba9cf1618b3cd25d3
> >> *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
> >> +stddev:    3.58 PSNR: 37.04 MAXDIFF:   49 bytes:  7603200/  7603200
> >>
> >> The size of the created files increased from 3654620 to 9986568,
> >> although it seems to me that using multiple layers is supposed to be opt-in.
> >>
> >> Besides that: You are using an int[100] array to store the layer rates.
> >> Yet I don't see any check in parse_layer_rates() that actually ensures
> >> that there is no write beyond the end of the array. Besides that
> >> check_number stores the return value of strlen in an int (may be
> >> truncating) and does not check in the calculation.
> >>
> >> - Andreas
> >> _______________________________________________
> >> 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".
> >
> > Thanks a lot Andreas. Originally, the encoder used to remove some data
> > according to a metric called picture quality.
> > This picture quality is a member of avframe.
> > However, right now, I have removed that usage and am compressing only
> > based on the compression rates provided by the user.
>
> This is confusing: If I am not mistaken, then the encoder did not only
> use to remove some data based upon the picture quality metric; it (i.e.
> git master) still does and it is this very patch that intends to change
> this. Typically we use the present tense to talk about current git master.
>
Got it.
> > Should I preserve the old functionality if the user does not provide a
> > "layer_rates"
> > option?
> >
>
> If your patch breaks the current way of signalling the desired quality
> and leads to a massive increase in filesize for people using the current
> way of signalling the quality, then you will be breaking lots of current
> users' usecases. I don't think that's acceptable.
Got it. From my understanding, Jpeg2000 compression could be done on
quality metric or compression ratio. So it seems right to preserve the quality
metric based compression and add the use of compression ratio as a new
feature instead of replacing the current functionality.
Thanks a lot for the comments!
>
> - Andreas
> _______________________________________________
> 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".
Gautam Ramakrishnan Aug. 20, 2020, 6:47 p.m. UTC | #9
On Thu, Aug 20, 2020 at 12:55 AM Paul B Mahol <onemda@gmail.com> wrote:
>
> On 8/19/20, Gautam Ramakrishnan <gautamramk@gmail.com> wrote:
> > On Wed, Aug 19, 2020 at 5:51 PM <gautamramk@gmail.com> wrote:
> >>
> >> From: Gautam Ramakrishnan <gautamramk@gmail.com>
> >>
> >> This patch allows setting a compression ratio and to
> >> set multiple layers. The user has to input a compression
> >> ratio for each layer.
> >> The per layer compression ration can be set as follows:
> >> -layer_rates "r1,r2,...rn"
> >> for to create 'n' layers.
> >> ---
> >>  libavcodec/j2kenc.c   | 443 ++++++++++++++++++++++++++++++++++--------
> >>  libavcodec/jpeg2000.c |  13 +-
> >>  libavcodec/jpeg2000.h |  10 +
> >>  3 files changed, 384 insertions(+), 82 deletions(-)
> >>
> >> diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c
> >> index 8699296434..b09db36c14 100644
> >> --- a/libavcodec/j2kenc.c
> >> +++ b/libavcodec/j2kenc.c
> >> @@ -32,6 +32,7 @@
> >>   * Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe
> >>   * Copyright (c) 2005, Herve Drolon, FreeImage Team
> >>   * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
> >> + * Copyright (c) 2020, Gautam Ramakrishnan <gautamramk@gmail.com>
> >>   * All rights reserved.
> >>   *
> >>   * Redistribution and use in source and binary forms, with or without
> >> @@ -100,6 +101,7 @@ static const int dwt_norms[2][4][10] = { //
> >> [dwt_type][band][rlevel] (multiplied
> >>
> >>  typedef struct {
> >>     Jpeg2000Component *comp;
> >> +   double *layer_rates;
> >>  } Jpeg2000Tile;
> >>
> >>  typedef struct {
> >> @@ -126,12 +128,15 @@ typedef struct {
> >>      Jpeg2000QuantStyle  qntsty;
> >>
> >>      Jpeg2000Tile *tile;
> >> +    int layer_rates[100];
> >>
> >>      int format;
> >>      int pred;
> >>      int sop;
> >>      int eph;
> >>      int prog;
> >> +    int nlayers;
> >> +    char *lr_str;
> >>  } Jpeg2000EncoderContext;
> >>
> >>
> >> @@ -332,7 +337,7 @@ static int put_cod(Jpeg2000EncoderContext *s)
> >>      bytestream_put_byte(&s->buf, scod);  // Scod
> >>      // SGcod
> >>      bytestream_put_byte(&s->buf, s->prog); // progression level
> >> -    bytestream_put_be16(&s->buf, 1); // num of layers
> >> +    bytestream_put_be16(&s->buf, s->nlayers); // num of layers
> >>      if(s->avctx->pix_fmt == AV_PIX_FMT_YUV444P){
> >>          bytestream_put_byte(&s->buf, 0); // unspecified
> >>      }else{
> >> @@ -411,6 +416,31 @@ static uint8_t *put_sot(Jpeg2000EncoderContext *s,
> >> int tileno)
> >>      return psotptr;
> >>  }
> >>
> >> +static void compute_rates(Jpeg2000EncoderContext* s)
> >> +{
> >> +    int i, j;
> >> +    int layno, compno;
> >> +    for (i = 0; i < s->numYtiles; i++) {
> >> +        for (j = 0; j < s->numXtiles; j++) {
> >> +            Jpeg2000Tile *tile = &s->tile[s->numXtiles * i + j];
> >> +            for (compno = 0; compno < s->ncomponents; compno++) {
> >> +                int tilew = tile->comp[compno].coord[0][1] -
> >> tile->comp[compno].coord[0][0];
> >> +                int tileh = tile->comp[compno].coord[1][1] -
> >> tile->comp[compno].coord[1][0];
> >> +                int scale = (compno?1 << s->chroma_shift[0]:1) *
> >> (compno?1 << s->chroma_shift[1]:1);
> >> +                for (layno = 0; layno < s->nlayers; layno++) {
> >> +                    if (s->layer_rates[layno] > 0.0f) {
> >> +                        tile->layer_rates[layno] += (double)(tilew *
> >> tileh) * s->ncomponents * s->cbps[compno] /
> >> +
> >> (double)(s->layer_rates[layno] * 8 * scale);
> >> +                    } else {
> >> +                        tile->layer_rates[layno] = 0.0f;
> >> +                    }
> >> +                }
> >> +            }
> >> +        }
> >> +    }
> >> +
> >> +}
> >> +
> >>  /**
> >>   * compute the sizes of tiles, resolution levels, bands, etc.
> >>   * allocate memory for them
> >> @@ -448,6 +478,10 @@ static int init_tiles(Jpeg2000EncoderContext *s)
> >>                          for (j = 0; j < 2; j++)
> >>                              comp->coord[i][j] = comp->coord_o[i][j] =
> >> ff_jpeg2000_ceildivpow2(comp->coord[i][j], s->chroma_shift[i]);
> >>
> >> +                tile->layer_rates = av_mallocz_array(s->ncomponents,
> >> sizeof(*tile->layer_rates));
> >> +                if (!tile->layer_rates)
> >> +                    return AVERROR(ENOMEM);
> >> +
> >>                  if ((ret = ff_jpeg2000_init_component(comp,
> >>                                                  codsty,
> >>                                                  qntsty,
> >> @@ -459,6 +493,7 @@ static int init_tiles(Jpeg2000EncoderContext *s)
> >>                      return ret;
> >>              }
> >>          }
> >> +    compute_rates(s);
> >>      return 0;
> >>  }
> >>
> >> @@ -701,6 +736,8 @@ static void encode_cblk(Jpeg2000EncoderContext *s,
> >> Jpeg2000T1Context *t1, Jpeg20
> >>          }
> >>
> >>          cblk->passes[passno].rate = ff_mqc_flush_to(&t1->mqc,
> >> cblk->passes[passno].flushed, &cblk->passes[passno].flushed_len);
> >> +        cblk->passes[passno].rate -= cblk->passes[passno].flushed_len;
> >> +
> >>          wmsedec += (int64_t)nmsedec << (2*bpno);
> >>          cblk->passes[passno].disto = wmsedec;
> >>
> >> @@ -733,10 +770,12 @@ static void putnumpasses(Jpeg2000EncoderContext *s,
> >> int n)
> >>  }
> >>
> >>
> >> -static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel
> >> *rlevel, int precno,
> >> -                          uint8_t *expn, int numgbits, int packetno)
> >> +static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel
> >> *rlevel, int layno,
> >> +                         int precno, uint8_t *expn, int numgbits, int
> >> packetno,
> >> +                         int nlayers)
> >>  {
> >>      int bandno, empty = 1;
> >> +    int i;
> >>      // init bitstream
> >>      *s->buf = 0;
> >>      s->bit_index = 0;
> >> @@ -748,18 +787,61 @@ static int encode_packet(Jpeg2000EncoderContext *s,
> >> Jpeg2000ResLevel *rlevel, in
> >>      }
> >>      // header
> >>
> >> +    if (!layno) {
> >> +        for (bandno = 0; bandno < rlevel->nbands; bandno++){
> >> +            Jpeg2000Band *band = rlevel->band + bandno;
> >> +            if (rlevel->band[bandno].coord[0][0] <
> >> rlevel->band[bandno].coord[0][1]
> >> +            &&  rlevel->band[bandno].coord[1][0] <
> >> rlevel->band[bandno].coord[1][1]){
> >> +                Jpeg2000Prec *prec = band->prec + precno;
> >> +                int nb_cblks = prec->nb_codeblocks_height *
> >> prec->nb_codeblocks_width;
> >> +                int pos;
> >> +                ff_tag_tree_zero(prec->zerobits,
> >> prec->nb_codeblocks_width, prec->nb_codeblocks_height);
> >> +                ff_tag_tree_zero(prec->cblkincl,
> >> prec->nb_codeblocks_width, prec->nb_codeblocks_height);
> >> +                for (pos = 0; pos < nb_cblks; pos++) {
> >> +                    Jpeg2000Cblk *cblk = &prec->cblk[pos];
> >> +                    prec->zerobits[pos].val = expn[bandno] + numgbits - 1
> >> - cblk->nonzerobits;
> >> +                    cblk->incl = 0;
> >> +                    cblk->lblock = 3;
> >> +                    tag_tree_update(prec->zerobits + pos);
> >> +                    for (i = 0; i < nlayers; i++) {
> >> +                        if (cblk->layers[i].npasses > 0) {
> >> +                            prec->cblkincl[pos].val = i;
> >> +                            break;
> >> +                        }
> >> +                    }
> >> +                    if (i == nlayers)
> >> +                        prec->cblkincl[pos].val = i;
> >> +                    tag_tree_update(prec->cblkincl + pos);
> >> +                }
> >> +            }
> >> +        }
> >> +    }
> >> +
> >>      // is the packet empty?
> >>      for (bandno = 0; bandno < rlevel->nbands; bandno++){
> >> +        Jpeg2000Band *band = rlevel->band + bandno;
> >>          if (rlevel->band[bandno].coord[0][0] <
> >> rlevel->band[bandno].coord[0][1]
> >>          &&  rlevel->band[bandno].coord[1][0] <
> >> rlevel->band[bandno].coord[1][1]){
> >> -            empty = 0;
> >> -            break;
> >> +            Jpeg2000Prec *prec = band->prec + precno;
> >> +            int nb_cblks = prec->nb_codeblocks_height *
> >> prec->nb_codeblocks_width;
> >> +            int pos;
> >> +            for (pos = 0; pos < nb_cblks; pos++) {
> >> +                Jpeg2000Cblk *cblk = &prec->cblk[pos];
> >> +                if (cblk->layers[layno].npasses) {
> >> +                    empty = 0;
> >> +                    break;
> >> +                }
> >> +            }
> >> +            if (!empty)
> >> +                break;
> >>          }
> >>      }
> >>
> >>      put_bits(s, !empty, 1);
> >>      if (empty){
> >>          j2k_flush(s);
> >> +        if (s->eph)
> >> +            bytestream_put_be16(&s->buf, JPEG2000_EPH);
> >>          return 0;
> >>      }
> >>
> >> @@ -775,40 +857,44 @@ static int encode_packet(Jpeg2000EncoderContext *s,
> >> Jpeg2000ResLevel *rlevel, in
> >>
> >>          for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
> >>              for (xi = 0; xi < cblknw; xi++, pos++){
> >> -                prec->cblkincl[pos].val = prec->cblk[yi * cblknw +
> >> xi].ninclpasses == 0;
> >> -                tag_tree_update(prec->cblkincl + pos);
> >> -                prec->zerobits[pos].val = expn[bandno] + numgbits - 1 -
> >> prec->cblk[yi * cblknw + xi].nonzerobits;
> >> -                tag_tree_update(prec->zerobits + pos);
> >> -            }
> >> -        }
> >> -
> >> -        for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
> >> -            for (xi = 0; xi < cblknw; xi++, pos++){
> >> -                int pad = 0, llen, length;
> >> +                int llen = 0, length;
> >>                  Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
> >>
> >>                  if (s->buf_end - s->buf < 20) // approximately
> >>                      return -1;
> >>
> >>                  // inclusion information
> >> -                tag_tree_code(s, prec->cblkincl + pos, 1);
> >> -                if (!cblk->ninclpasses)
> >> +                if (!cblk->incl)
> >> +                    tag_tree_code(s, prec->cblkincl + pos, layno + 1);
> >> +                else {
> >> +                    put_bits(s, cblk->layers[layno].npasses > 0, 1);
> >> +                }
> >> +
> >> +                if (!cblk->layers[layno].npasses)
> >>                      continue;
> >> +
> >>                  // zerobits information
> >> -                tag_tree_code(s, prec->zerobits + pos, 100);
> >> +                if (!cblk->incl) {
> >> +                    tag_tree_code(s, prec->zerobits + pos, 100);
> >> +                    cblk->incl = 1;
> >> +                }
> >> +
> >>                  // number of passes
> >> -                putnumpasses(s, cblk->ninclpasses);
> >> +                putnumpasses(s, cblk->layers[layno].npasses);
> >>
> >> -                length = cblk->passes[cblk->ninclpasses-1].rate;
> >> -                llen = av_log2(length) - av_log2(cblk->ninclpasses) - 2;
> >> -                if (llen < 0){
> >> -                    pad = -llen;
> >> -                    llen = 0;
> >> +                length = cblk->layers[layno].data_len;
> >> +                if (layno == nlayers - 1 && cblk->layers->cum_passes){
> >> +                    length +=
> >> cblk->passes[cblk->layers->cum_passes-1].flushed_len;
> >>                  }
> >> +                if (cblk->lblock + av_log2(cblk->layers[layno].npasses) <
> >> av_log2(length) + 1) {
> >> +                    llen = av_log2(length) + 1 - cblk->lblock -
> >> av_log2(cblk->layers[layno].npasses);
> >> +                }
> >> +
> >>                  // length of code block
> >> +                cblk->lblock += llen;
> >>                  put_bits(s, 1, llen);
> >>                  put_bits(s, 0, 1);
> >> -                put_num(s, length, av_log2(length)+1+pad);
> >> +                put_num(s, length, cblk->lblock +
> >> av_log2(cblk->layers[layno].npasses));
> >>              }
> >>          }
> >>      }
> >> @@ -825,13 +911,14 @@ static int encode_packet(Jpeg2000EncoderContext *s,
> >> Jpeg2000ResLevel *rlevel, in
> >>              int xi;
> >>              for (xi = 0; xi < cblknw; xi++){
> >>                  Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
> >> -                if (cblk->ninclpasses){
> >> -                    if (s->buf_end - s->buf <
> >> cblk->passes[cblk->ninclpasses-1].rate)
> >> +                if (cblk->layers[layno].npasses){
> >> +                    if (s->buf_end - s->buf <
> >> cblk->layers[layno].data_len + 2)
> >>                          return -1;
> >> -                    bytestream_put_buffer(&s->buf, cblk->data + 1,
> >> cblk->passes[cblk->ninclpasses-1].rate
> >> -                                                               -
> >> cblk->passes[cblk->ninclpasses-1].flushed_len);
> >> -                    bytestream_put_buffer(&s->buf,
> >> cblk->passes[cblk->ninclpasses-1].flushed,
> >> -
> >> cblk->passes[cblk->ninclpasses-1].flushed_len);
> >> +                    bytestream_put_buffer(&s->buf,
> >> cblk->layers[layno].data_start + 1, cblk->layers[layno].data_len);
> >> +                    if (layno == nlayers - 1 &&
> >> cblk->layers->cum_passes){
> >> +                        bytestream_put_buffer(&s->buf,
> >> cblk->passes[cblk->layers->cum_passes-1].flushed,
> >> +
> >> cblk->passes[cblk->layers->cum_passes-1].flushed_len);
> >> +                    }
> >>                  }
> >>              }
> >>          }
> >> @@ -839,9 +926,9 @@ static int encode_packet(Jpeg2000EncoderContext *s,
> >> Jpeg2000ResLevel *rlevel, in
> >>      return 0;
> >>  }
> >>
> >> -static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile,
> >> int tileno)
> >> +static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile,
> >> int tileno, int nlayers)
> >>  {
> >> -    int compno, reslevelno, ret;
> >> +    int compno, reslevelno, layno, ret;
> >>      Jpeg2000CodingStyle *codsty = &s->codsty;
> >>      Jpeg2000QuantStyle  *qntsty = &s->qntsty;
> >>      int packetno = 0;
> >> @@ -858,29 +945,33 @@ static int encode_packets(Jpeg2000EncoderContext *s,
> >> Jpeg2000Tile *tile, int til
> >>
> >>      av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
> >>      // lay-rlevel-comp-pos progression
> >> -    switch (s->prog) {
> >> +        switch (s->prog) {
> >>      case JPEG2000_PGOD_LRCP:
> >> -    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
> >> -        for (compno = 0; compno < s->ncomponents; compno++){
> >> -            int precno;
> >> -            Jpeg2000ResLevel *reslevel =
> >> s->tile[tileno].comp[compno].reslevel + reslevelno;
> >> -            for (precno = 0; precno < reslevel->num_precincts_x *
> >> reslevel->num_precincts_y; precno++){
> >> -                if ((ret = encode_packet(s, reslevel, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> -                              qntsty->nguardbits, packetno++)) < 0)
> >> -                    return ret;
> >> +    for (layno = 0; layno < nlayers; layno++){
> >> +        for (reslevelno = 0; reslevelno < codsty->nreslevels;
> >> reslevelno++){
> >> +            for (compno = 0; compno < s->ncomponents; compno++){
> >> +                int precno;
> >> +                Jpeg2000ResLevel *reslevel =
> >> s->tile[tileno].comp[compno].reslevel + reslevelno;
> >> +                for (precno = 0; precno < reslevel->num_precincts_x *
> >> reslevel->num_precincts_y; precno++){
> >> +                    if ((ret = encode_packet(s, reslevel, layno, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> +                                qntsty->nguardbits, packetno++, nlayers))
> >> < 0)
> >> +                        return ret;
> >> +                }
> >>              }
> >>          }
> >>      }
> >>      break;
> >>      case JPEG2000_PGOD_RLCP:
> >>      for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
> >> -        for (compno = 0; compno < s->ncomponents; compno++){
> >> -            int precno;
> >> -            Jpeg2000ResLevel *reslevel =
> >> s->tile[tileno].comp[compno].reslevel + reslevelno;
> >> -            for (precno = 0; precno < reslevel->num_precincts_x *
> >> reslevel->num_precincts_y; precno++){
> >> -                if ((ret = encode_packet(s, reslevel, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> -                              qntsty->nguardbits, packetno++)) < 0)
> >> -                    return ret;
> >> +        for (layno = 0; layno < nlayers; layno++){
> >> +            for (compno = 0; compno < s->ncomponents; compno++){
> >> +                int precno;
> >> +                Jpeg2000ResLevel *reslevel =
> >> s->tile[tileno].comp[compno].reslevel + reslevelno;
> >> +                for (precno = 0; precno < reslevel->num_precincts_x *
> >> reslevel->num_precincts_y; precno++){
> >> +                    if ((ret = encode_packet(s, reslevel, layno, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> +                                qntsty->nguardbits, packetno++, nlayers))
> >> < 0)
> >> +                        return ret;
> >> +                }
> >>              }
> >>          }
> >>      }
> >> @@ -935,10 +1026,11 @@ static int encode_packets(Jpeg2000EncoderContext
> >> *s, Jpeg2000Tile *tile, int til
> >>                                 prcx, prcy, reslevel->num_precincts_x,
> >> reslevel->num_precincts_y);
> >>                          continue;
> >>                      }
> >> -
> >> -                    if ((ret = encode_packet(s, reslevel, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> -                              qntsty->nguardbits, packetno++)) < 0)
> >> -                        return ret;
> >> +                    for (layno = 0; layno < nlayers; layno++){
> >> +                        if ((ret = encode_packet(s, reslevel, layno,
> >> precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> +                                qntsty->nguardbits, packetno++, nlayers))
> >> < 0)
> >> +                            return ret;
> >> +                        }
> >>                      }
> >>                  }
> >>              }
> >> @@ -1001,9 +1093,11 @@ static int encode_packets(Jpeg2000EncoderContext
> >> *s, Jpeg2000Tile *tile, int til
> >>                                     prcx, prcy, reslevel->num_precincts_x,
> >> reslevel->num_precincts_y);
> >>                              continue;
> >>                          }
> >> -                        if ((ret = encode_packet(s, reslevel, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> -                                  qntsty->nguardbits, packetno++)) < 0)
> >> -                            return ret;
> >> +                        for (layno = 0; layno < nlayers; layno++){
> >> +                            if ((ret = encode_packet(s, reslevel, layno,
> >> precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> +                                    qntsty->nguardbits, packetno++,
> >> nlayers)) < 0)
> >> +                                return ret;
> >> +                        }
> >>                      }
> >>                  }
> >>              }
> >> @@ -1062,9 +1156,11 @@ static int encode_packets(Jpeg2000EncoderContext
> >> *s, Jpeg2000Tile *tile, int til
> >>                                     prcx, prcy, reslevel->num_precincts_x,
> >> reslevel->num_precincts_y);
> >>                              continue;
> >>                          }
> >> -                        if ((ret = encode_packet(s, reslevel, precno,
> >> qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> -                                  qntsty->nguardbits, packetno++)) < 0)
> >> -                            return ret;
> >> +                        for (layno = 0; layno < nlayers; layno++){
> >> +                            if ((ret = encode_packet(s, reslevel, layno,
> >> precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
> >> +                                    qntsty->nguardbits, packetno++,
> >> nlayers)) < 0)
> >> +                                return ret;
> >> +                        }
> >>                      }
> >>                  }
> >>              }
> >> @@ -1076,27 +1172,98 @@ static int encode_packets(Jpeg2000EncoderContext
> >> *s, Jpeg2000Tile *tile, int til
> >>      return 0;
> >>  }
> >>
> >> -static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm)
> >> +static void makelayer(Jpeg2000EncoderContext *s, int layno, double
> >> thresh, Jpeg2000Tile* tile, int final)
> >>  {
> >> -    int passno, res = 0;
> >> -    for (passno = 0; passno < cblk->npasses; passno++){
> >> -        int dr;
> >> -        int64_t dd;
> >> -
> >> -        dr = cblk->passes[passno].rate
> >> -           - (res ? cblk->passes[res-1].rate:0);
> >> -        dd = cblk->passes[passno].disto
> >> -           - (res ? cblk->passes[res-1].disto:0);
> >> -
> >> -        if (((dd * dwt_norm) >> WMSEDEC_SHIFT) * dwt_norm >= dr * lambda)
> >> -            res = passno+1;
> >> +    int compno, resno, bandno, precno, cblkno;
> >> +    int passno;
> >> +
> >> +    for (compno = 0; compno < s->ncomponents; compno++) {
> >> +        Jpeg2000Component *comp = &tile->comp[compno];
> >> +
> >> +        for (resno = 0; resno < s->codsty.nreslevels; resno++) {
> >> +            Jpeg2000ResLevel *reslevel = comp->reslevel + resno;
> >> +
> >> +            for (precno = 0; precno < reslevel->num_precincts_x *
> >> reslevel->num_precincts_y; precno++){
> >> +                for (bandno = 0; bandno < reslevel->nbands ; bandno++){
> >> +                    Jpeg2000Band *band = reslevel->band + bandno;
> >> +                    Jpeg2000Prec *prec = band->prec + precno;
> >> +
> >> +                    for (cblkno = 0; cblkno < prec->nb_codeblocks_height
> >> * prec->nb_codeblocks_width; cblkno++){
> >> +                        Jpeg2000Cblk *cblk = prec->cblk + cblkno;
> >> +                        Jpeg2000Layer *layer = &cblk->layers[layno];
> >> +                        int n;
> >> +
> >> +                        if (layno == 0) {
> >> +                            cblk->ninclpasses = 0;
> >> +                        }
> >> +
> >> +                        n = cblk->ninclpasses;
> >> +
> >> +                        if (thresh < 0) {
> >> +                            n = cblk->npasses;
> >> +                        } else {
> >> +                            for (passno = cblk->ninclpasses; passno <
> >> cblk->npasses; passno++) {
> >> +                                int32_t dr;
> >> +                                double dd;
> >> +                                Jpeg2000Pass *pass =
> >> &cblk->passes[passno];
> >> +
> >> +                                if (n == 0) {
> >> +                                    dr = pass->rate;
> >> +                                    dd = (double)pass->disto;
> >> +                                } else {
> >> +                                    dr = pass->rate - cblk->passes[n -
> >> 1].rate;
> >> +                                    dd = (double)pass->disto -
> >> (double)cblk->passes[n-1].disto;
> >> +                                }
> >> +
> >> +                                if (!dr) {
> >> +                                    if (dd) {
> >> +                                        n = passno + 1;
> >> +                                    }
> >> +                                    continue;
> >> +                                }
> >> +
> >> +                                if (thresh - (dd / dr) < DBL_EPSILON)
> >> +                                    n = passno + 1;
> >> +                            }
> >> +                        }
> >> +                        layer->npasses = n - cblk->ninclpasses;
> >> +                        layer->cum_passes = n;
> >> +
> >> +                        if (layer->npasses == 0) {
> >> +                            layer->disto = 0;
> >> +                            layer->data_len = 0;
> >> +                            continue;
> >> +                        }
> >> +
> >> +                        if (cblk->ninclpasses == 0) {
> >> +                            layer->data_len = cblk->passes[n - 1].rate;
> >> +                            layer->data_start = cblk->data;
> >> +                            layer->disto = cblk->passes[n - 1].disto;
> >> +                        } else {
> >> +                            layer->data_len = cblk->passes[n - 1].rate -
> >> cblk->passes[cblk->ninclpasses - 1].rate;
> >> +                            layer->data_start = cblk->data +
> >> cblk->passes[cblk->ninclpasses - 1].rate;
> >> +                            layer->disto = cblk->passes[n - 1].disto -
> >> +                                           cblk->passes[cblk->ninclpasses
> >> - 1].disto;
> >> +                        }
> >> +                        if (final) {
> >> +                            cblk->ninclpasses = n;
> >> +                        }
> >> +                    }
> >> +                }
> >> +            }
> >> +        }
> >>      }
> >> -    return res;
> >>  }
> >>
> >> -static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
> >> +static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
> >>  {
> >> -    int precno, compno, reslevelno, bandno, cblkno, lev;
> >> +    int precno, compno, reslevelno, bandno, cblkno, lev, passno, layno;
> >> +    int i;
> >> +    double min = DBL_MAX;
> >> +    double max = 0;
> >> +    double thresh;
> >> +    int tile_disto = 0;
> >> +
> >>      Jpeg2000CodingStyle *codsty = &s->codsty;
> >>
> >>      for (compno = 0; compno < s->ncomponents; compno++){
> >> @@ -1107,20 +1274,70 @@ static void truncpasses(Jpeg2000EncoderContext *s,
> >> Jpeg2000Tile *tile)
> >>
> >>              for (precno = 0; precno < reslevel->num_precincts_x *
> >> reslevel->num_precincts_y; precno++){
> >>                  for (bandno = 0; bandno < reslevel->nbands ; bandno++){
> >> -                    int bandpos = bandno + (reslevelno > 0);
> >>                      Jpeg2000Band *band = reslevel->band + bandno;
> >>                      Jpeg2000Prec *prec = band->prec + precno;
> >>
> >>                      for (cblkno = 0; cblkno < prec->nb_codeblocks_height
> >> * prec->nb_codeblocks_width; cblkno++){
> >>                          Jpeg2000Cblk *cblk = prec->cblk + cblkno;
> >> +                        for (passno = 0; passno < cblk->npasses;
> >> passno++) {
> >> +                            Jpeg2000Pass *pass = &cblk->passes[passno];
> >> +                            int dr;
> >> +                            double dd, drslope;
> >> +
> >> +                            tile_disto += pass->disto;
> >> +                            if (passno == 0) {
> >> +                                dr = (int32_t)pass->rate;
> >> +                                dd = (double)pass->disto;
> >> +                            } else {
> >> +                                dr = (int32_t)(pass->rate) -
> >> cblk->passes[passno - 1].rate;
> >> +                                dd = (double)pass->disto -
> >> (double)cblk->passes[passno - 1].disto;
> >> +                            }
> >> +
> >> +                            if (dr <= 0)
> >> +                                continue;
> >>
> >> -                        cblk->ninclpasses = getcut(cblk, s->lambda,
> >> -                                (int64_t)dwt_norms[codsty->transform ==
> >> FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15);
> >> +                            drslope = dd / dr;
> >> +                            if (drslope < min)
> >> +                                min = drslope;
> >> +
> >> +                            if (drslope > max)
> >> +                                max = drslope;
> >> +                        }
> >>                      }
> >>                  }
> >>              }
> >>          }
> >>      }
> >> +
> >> +    for (layno = 0; layno < s->nlayers; layno++) {
> >> +        double lo = min;
> >> +        double hi = max;
> >> +        double stable_thresh = 0;
> >> +        double good_thresh = 0;
> >> +        if (!s->layer_rates[layno]) {
> >> +            good_thresh = -1;
> >> +        } else {
> >> +            for (i = 0; i < 128; i++) {
> >> +                uint8_t *stream_pos = s->buf;
> >> +                int ret;
> >> +                thresh = (lo + hi) / 2;
> >> +                makelayer(s, layno, thresh, tile, 0);
> >> +                ret = encode_packets(s, tile, (int)(tile - s->tile),
> >> layno + 1);
> >> +                memset(stream_pos, 0, s->buf - stream_pos);
> >> +                if ((s->buf - stream_pos >
> >> ceil(tile->layer_rates[layno])) || ret < 0) {
> >> +                    lo = thresh;
> >> +                    s->buf = stream_pos;
> >> +                    continue;
> >> +                }
> >> +                hi = thresh;
> >> +                stable_thresh = thresh;
> >> +                s->buf = stream_pos;
> >> +            }
> >> +        }
> >> +        if (good_thresh >= 0)
> >> +            good_thresh = stable_thresh == 0 ? thresh : stable_thresh;
> >> +        makelayer(s, layno, good_thresh, tile, 1);
> >> +    }
> >>  }
> >>
> >>  static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int
> >> tileno)
> >> @@ -1203,8 +1420,8 @@ static int encode_tile(Jpeg2000EncoderContext *s,
> >> Jpeg2000Tile *tile, int tileno
> >>      }
> >>
> >>      av_log(s->avctx, AV_LOG_DEBUG, "rate control\n");
> >> -    truncpasses(s, tile);
> >> -    if ((ret = encode_packets(s, tile, tileno)) < 0)
> >> +    makelayers(s, tile);
> >> +    if ((ret = encode_packets(s, tile, tileno, s->nlayers)) < 0)
> >>          return ret;
> >>      av_log(s->avctx, AV_LOG_DEBUG, "after rate control\n");
> >>      return 0;
> >> @@ -1221,6 +1438,7 @@ static void cleanup(Jpeg2000EncoderContext *s)
> >>              ff_jpeg2000_cleanup(comp, codsty, 1);
> >>          }
> >>          av_freep(&s->tile[tileno].comp);
> >> +        av_freep(&s->tile[tileno].layer_rates);
> >>      }
> >>      av_freep(&s->tile);
> >>  }
> >> @@ -1379,6 +1597,64 @@ static int encode_frame(AVCodecContext *avctx,
> >> AVPacket *pkt,
> >>      return 0;
> >>  }
> >>
> >> +static int inline check_number(char* st, int* ret) {
> >> +    int stlen = strlen(st);
> >> +    int i;
> >> +    *ret = 0;
> >> +    if (stlen <= 0) {
> >> +        return AVERROR_INVALIDDATA;
> >> +    }
> >> +    for (i = 0; i < stlen; i++) {
> >> +        if (st[i] >= '0' && st[i] <= '9') {
> >> +            *ret = (*ret) * 10 + (st[i] - '0');
> >> +        } else {
> >> +            return AVERROR_INVALIDDATA;
> >> +        }
> >> +    }
> >> +    return 0;
> >> +}
> >> +
> >> +static int parse_layer_rates(Jpeg2000EncoderContext *s)
> >> +{
> >> +    int i;
> >> +    char* token;
> >> +    int rate;
> >> +    int nlayers = 0;
> >> +    if (!s->lr_str) {
> >> +        s->nlayers = 1;
> >> +        s->layer_rates[0] = 0;
> >> +        return 0;
> >> +    }
> >> +
> >> +    token = strtok(s->lr_str, ",");
> >> +    if (!check_number(token, &rate)) {
> >> +            s->layer_rates[0] = rate <= 1 ? 0:rate;
> >> +            nlayers++;
> >> +    } else {
> >> +            return AVERROR_INVALIDDATA;
> >> +    }
> >> +
> >> +    while (1) {
> >> +        token = strtok(NULL, ",");
> >> +        if (!token)
> >> +            break;
> >> +        if (!check_number(token, &rate)) {
> >> +            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
> >> +            nlayers++;
> >> +        } else {
> >> +            return AVERROR_INVALIDDATA;
> >> +        }
> >> +    }
> >> +
> >> +    for (i = 1; i < nlayers; i++) {
> >> +        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
> >> +            return AVERROR_INVALIDDATA;
> >> +        }
> >> +    }
> >> +    s->nlayers = nlayers;
> >> +    return 0;
> >> +}
> >> +
> >>  static av_cold int j2kenc_init(AVCodecContext *avctx)
> >>  {
> >>      int i, ret;
> >> @@ -1388,6 +1664,11 @@ static av_cold int j2kenc_init(AVCodecContext
> >> *avctx)
> >>
> >>      s->avctx = avctx;
> >>      av_log(s->avctx, AV_LOG_DEBUG, "init\n");
> >> +    if (parse_layer_rates(s)) {
> >> +        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with
> >> 1 layer.\n");
> >> +        s->nlayers = 1;
> >> +        s->layer_rates[0] = 0;
> >> +    }
> >>
> >>  #if FF_API_PRIVATE_OPT
> >>  FF_DISABLE_DEPRECATION_WARNINGS
> >> @@ -1408,6 +1689,7 @@ FF_ENABLE_DEPRECATION_WARNINGS
> >>      memset(codsty->log2_prec_heights, 15,
> >> sizeof(codsty->log2_prec_heights));
> >>      codsty->nreslevels2decode=
> >>      codsty->nreslevels       = 7;
> >> +    codsty->nlayers          = s->nlayers;
> >>      codsty->log2_cblk_width  = 4;
> >>      codsty->log2_cblk_height = 4;
> >>      codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
> >> @@ -1489,6 +1771,7 @@ static const AVOption options[] = {
> >>      { "rpcl",          NULL,                OFFSET(prog),
> >> AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,
> >> 0,           VE, "prog" },
> >>      { "pcrl",          NULL,                OFFSET(prog),
> >> AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,
> >> 0,           VE, "prog" },
> >>      { "cprl",          NULL,                OFFSET(prog),
> >> AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,
> >> 0,           VE, "prog" },
> >> +    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),
> >> AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
> >>      { NULL }
> >>  };
> >>
> >> diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c
> >> index 70c25a0ca2..1c294ad9e6 100644
> >> --- a/libavcodec/jpeg2000.c
> >> +++ b/libavcodec/jpeg2000.c
> >> @@ -261,9 +261,11 @@ static void init_band_stepsize(AVCodecContext *avctx,
> >>          band->f_stepsize *= 0.5;
> >>  }
> >>
> >> -static int init_prec(Jpeg2000Band *band,
> >> +static int init_prec(AVCodecContext *avctx,
> >> +                     Jpeg2000Band *band,
> >>                       Jpeg2000ResLevel *reslevel,
> >>                       Jpeg2000Component *comp,
> >> +                     Jpeg2000CodingStyle *codsty,
> >>                       int precno, int bandno, int reslevelno,
> >>                       int log2_band_prec_width,
> >>                       int log2_band_prec_height)
> >> @@ -366,6 +368,11 @@ static int init_prec(Jpeg2000Band *band,
> >>          cblk->lblock    = 3;
> >>          cblk->length    = 0;
> >>          cblk->npasses   = 0;
> >> +        if (av_codec_is_encoder(avctx->codec)) {
> >> +            cblk->layers = av_mallocz_array(codsty->nlayers,
> >> sizeof(*cblk->layers));
> >> +            if (!cblk->layers)
> >> +                return AVERROR(ENOMEM);
> >> +        }
> >>      }
> >>
> >>      return 0;
> >> @@ -439,7 +446,7 @@ static int init_band(AVCodecContext *avctx,
> >>          return AVERROR(ENOMEM);
> >>
> >>      for (precno = 0; precno < nb_precincts; precno++) {
> >> -        ret = init_prec(band, reslevel, comp,
> >> +        ret = init_prec(avctx, band, reslevel, comp, codsty,
> >>                          precno, bandno, reslevelno,
> >>                          log2_band_prec_width, log2_band_prec_height);
> >>          if (ret < 0)
> >> @@ -614,6 +621,8 @@ void ff_jpeg2000_cleanup(Jpeg2000Component *comp,
> >> Jpeg2000CodingStyle *codsty, i
> >>                              av_freep(&cblk->passes);
> >>                              av_freep(&cblk->lengthinc);
> >>                              av_freep(&cblk->data_start);
> >> +                            if (isencoder)
> >> +                                av_freep(&cblk->layers);
> >>                          }
> >>                          av_freep(&prec->cblk);
> >>                      }
> >> diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
> >> index fee9607e86..3c6e8ede16 100644
> >> --- a/libavcodec/jpeg2000.h
> >> +++ b/libavcodec/jpeg2000.h
> >> @@ -162,10 +162,19 @@ typedef struct Jpeg2000Pass {
> >>      int flushed_len;
> >>  } Jpeg2000Pass;
> >>
> >> +typedef struct Jpeg2000Layer {
> >> +    uint8_t *data_start;
> >> +    int data_len;
> >> +    int npasses;
> >> +    double disto;
> >> +    int cum_passes;
> >> +} Jpeg2000Layer;
> >> +
> >>  typedef struct Jpeg2000Cblk {
> >>      uint8_t npasses;
> >>      uint8_t ninclpasses; // number coding of passes included in
> >> codestream
> >>      uint8_t nonzerobits;
> >> +    uint8_t incl;
> >>      uint16_t length;
> >>      uint16_t *lengthinc;
> >>      uint8_t nb_lengthinc;
> >> @@ -176,6 +185,7 @@ typedef struct Jpeg2000Cblk {
> >>      int nb_terminationsinc;
> >>      int *data_start;
> >>      Jpeg2000Pass *passes;
> >> +    Jpeg2000Layer *layers;
> >>      int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}}
> >>  } Jpeg2000Cblk; // code block
> >>
> >> --
> >> 2.17.1
> >>
> >
> > This patch seems to be breaking FATE.
> > I believe that the error is because the patch modifies the encoder
> > such that the encoded files will be slightly different now.
> > How can this be handled?
>
> Run fate. Make sure that you have SAMPLES set and rsynced with server.
>
> make fate-name-of-target GEN=1
>
> To generate new hashes for encodes.
>
> Please read http://ffmpeg.org/fate.html
>
>
> > --
> > -------------
> > Gautam |

I made all the changes as per suggestions and tried to generate new
hashes for the encodes.
However, when I run make fate with GEN=1 for target fate-vsynth1-jpeg2000,
the file tests/ref/vsynth/vsynth1-jpeg2000 becomes empty instead of
being filled with new data.
The file tests/data/fate/vsynth1-jpeg2000.avi, which is to be encoded
by this test seems to be
encoded correctly. What am I missing here?
diff mbox series

Patch

diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c
index 8699296434..b09db36c14 100644
--- a/libavcodec/j2kenc.c
+++ b/libavcodec/j2kenc.c
@@ -32,6 +32,7 @@ 
  * Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe
  * Copyright (c) 2005, Herve Drolon, FreeImage Team
  * Copyright (c) 2007, Callum Lerwick <seg@haxxed.com>
+ * Copyright (c) 2020, Gautam Ramakrishnan <gautamramk@gmail.com>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -100,6 +101,7 @@  static const int dwt_norms[2][4][10] = { // [dwt_type][band][rlevel] (multiplied
 
 typedef struct {
    Jpeg2000Component *comp;
+   double *layer_rates;
 } Jpeg2000Tile;
 
 typedef struct {
@@ -126,12 +128,15 @@  typedef struct {
     Jpeg2000QuantStyle  qntsty;
 
     Jpeg2000Tile *tile;
+    int layer_rates[100];
 
     int format;
     int pred;
     int sop;
     int eph;
     int prog;
+    int nlayers;
+    char *lr_str;
 } Jpeg2000EncoderContext;
 
 
@@ -332,7 +337,7 @@  static int put_cod(Jpeg2000EncoderContext *s)
     bytestream_put_byte(&s->buf, scod);  // Scod
     // SGcod
     bytestream_put_byte(&s->buf, s->prog); // progression level
-    bytestream_put_be16(&s->buf, 1); // num of layers
+    bytestream_put_be16(&s->buf, s->nlayers); // num of layers
     if(s->avctx->pix_fmt == AV_PIX_FMT_YUV444P){
         bytestream_put_byte(&s->buf, 0); // unspecified
     }else{
@@ -411,6 +416,31 @@  static uint8_t *put_sot(Jpeg2000EncoderContext *s, int tileno)
     return psotptr;
 }
 
+static void compute_rates(Jpeg2000EncoderContext* s)
+{
+    int i, j;
+    int layno, compno;
+    for (i = 0; i < s->numYtiles; i++) {
+        for (j = 0; j < s->numXtiles; j++) {
+            Jpeg2000Tile *tile = &s->tile[s->numXtiles * i + j];
+            for (compno = 0; compno < s->ncomponents; compno++) {
+                int tilew = tile->comp[compno].coord[0][1] - tile->comp[compno].coord[0][0];
+                int tileh = tile->comp[compno].coord[1][1] - tile->comp[compno].coord[1][0];
+                int scale = (compno?1 << s->chroma_shift[0]:1) * (compno?1 << s->chroma_shift[1]:1);
+                for (layno = 0; layno < s->nlayers; layno++) {
+                    if (s->layer_rates[layno] > 0.0f) {
+                        tile->layer_rates[layno] += (double)(tilew * tileh) * s->ncomponents * s->cbps[compno] /
+                                                    (double)(s->layer_rates[layno] * 8 * scale);
+                    } else {
+                        tile->layer_rates[layno] = 0.0f;
+                    }
+                }
+            }
+        }
+    }
+
+}
+
 /**
  * compute the sizes of tiles, resolution levels, bands, etc.
  * allocate memory for them
@@ -448,6 +478,10 @@  static int init_tiles(Jpeg2000EncoderContext *s)
                         for (j = 0; j < 2; j++)
                             comp->coord[i][j] = comp->coord_o[i][j] = ff_jpeg2000_ceildivpow2(comp->coord[i][j], s->chroma_shift[i]);
 
+                tile->layer_rates = av_mallocz_array(s->ncomponents, sizeof(*tile->layer_rates));
+                if (!tile->layer_rates)
+                    return AVERROR(ENOMEM);
+
                 if ((ret = ff_jpeg2000_init_component(comp,
                                                 codsty,
                                                 qntsty,
@@ -459,6 +493,7 @@  static int init_tiles(Jpeg2000EncoderContext *s)
                     return ret;
             }
         }
+    compute_rates(s);
     return 0;
 }
 
@@ -701,6 +736,8 @@  static void encode_cblk(Jpeg2000EncoderContext *s, Jpeg2000T1Context *t1, Jpeg20
         }
 
         cblk->passes[passno].rate = ff_mqc_flush_to(&t1->mqc, cblk->passes[passno].flushed, &cblk->passes[passno].flushed_len);
+        cblk->passes[passno].rate -= cblk->passes[passno].flushed_len;
+
         wmsedec += (int64_t)nmsedec << (2*bpno);
         cblk->passes[passno].disto = wmsedec;
 
@@ -733,10 +770,12 @@  static void putnumpasses(Jpeg2000EncoderContext *s, int n)
 }
 
 
-static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, int precno,
-                          uint8_t *expn, int numgbits, int packetno)
+static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, int layno,
+                         int precno, uint8_t *expn, int numgbits, int packetno,
+                         int nlayers)
 {
     int bandno, empty = 1;
+    int i;
     // init bitstream
     *s->buf = 0;
     s->bit_index = 0;
@@ -748,18 +787,61 @@  static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
     }
     // header
 
+    if (!layno) {
+        for (bandno = 0; bandno < rlevel->nbands; bandno++){
+            Jpeg2000Band *band = rlevel->band + bandno;
+            if (rlevel->band[bandno].coord[0][0] < rlevel->band[bandno].coord[0][1]
+            &&  rlevel->band[bandno].coord[1][0] < rlevel->band[bandno].coord[1][1]){
+                Jpeg2000Prec *prec = band->prec + precno;
+                int nb_cblks = prec->nb_codeblocks_height * prec->nb_codeblocks_width;
+                int pos;
+                ff_tag_tree_zero(prec->zerobits, prec->nb_codeblocks_width, prec->nb_codeblocks_height);
+                ff_tag_tree_zero(prec->cblkincl, prec->nb_codeblocks_width, prec->nb_codeblocks_height);
+                for (pos = 0; pos < nb_cblks; pos++) {
+                    Jpeg2000Cblk *cblk = &prec->cblk[pos];
+                    prec->zerobits[pos].val = expn[bandno] + numgbits - 1 - cblk->nonzerobits;
+                    cblk->incl = 0;
+                    cblk->lblock = 3;
+                    tag_tree_update(prec->zerobits + pos);
+                    for (i = 0; i < nlayers; i++) {
+                        if (cblk->layers[i].npasses > 0) {
+                            prec->cblkincl[pos].val = i;
+                            break;
+                        }
+                    }
+                    if (i == nlayers)
+                        prec->cblkincl[pos].val = i;
+                    tag_tree_update(prec->cblkincl + pos);
+                }
+            }
+        }
+    }
+
     // is the packet empty?
     for (bandno = 0; bandno < rlevel->nbands; bandno++){
+        Jpeg2000Band *band = rlevel->band + bandno;
         if (rlevel->band[bandno].coord[0][0] < rlevel->band[bandno].coord[0][1]
         &&  rlevel->band[bandno].coord[1][0] < rlevel->band[bandno].coord[1][1]){
-            empty = 0;
-            break;
+            Jpeg2000Prec *prec = band->prec + precno;
+            int nb_cblks = prec->nb_codeblocks_height * prec->nb_codeblocks_width;
+            int pos;
+            for (pos = 0; pos < nb_cblks; pos++) {
+                Jpeg2000Cblk *cblk = &prec->cblk[pos];
+                if (cblk->layers[layno].npasses) {
+                    empty = 0;
+                    break;
+                }
+            }
+            if (!empty)
+                break;
         }
     }
 
     put_bits(s, !empty, 1);
     if (empty){
         j2k_flush(s);
+        if (s->eph)
+            bytestream_put_be16(&s->buf, JPEG2000_EPH);
         return 0;
     }
 
@@ -775,40 +857,44 @@  static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
 
         for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
             for (xi = 0; xi < cblknw; xi++, pos++){
-                prec->cblkincl[pos].val = prec->cblk[yi * cblknw + xi].ninclpasses == 0;
-                tag_tree_update(prec->cblkincl + pos);
-                prec->zerobits[pos].val = expn[bandno] + numgbits - 1 - prec->cblk[yi * cblknw + xi].nonzerobits;
-                tag_tree_update(prec->zerobits + pos);
-            }
-        }
-
-        for (pos=0, yi = 0; yi < prec->nb_codeblocks_height; yi++){
-            for (xi = 0; xi < cblknw; xi++, pos++){
-                int pad = 0, llen, length;
+                int llen = 0, length;
                 Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
 
                 if (s->buf_end - s->buf < 20) // approximately
                     return -1;
 
                 // inclusion information
-                tag_tree_code(s, prec->cblkincl + pos, 1);
-                if (!cblk->ninclpasses)
+                if (!cblk->incl)
+                    tag_tree_code(s, prec->cblkincl + pos, layno + 1);
+                else {
+                    put_bits(s, cblk->layers[layno].npasses > 0, 1);
+                }
+
+                if (!cblk->layers[layno].npasses)
                     continue;
+
                 // zerobits information
-                tag_tree_code(s, prec->zerobits + pos, 100);
+                if (!cblk->incl) {
+                    tag_tree_code(s, prec->zerobits + pos, 100);
+                    cblk->incl = 1;
+                }
+
                 // number of passes
-                putnumpasses(s, cblk->ninclpasses);
+                putnumpasses(s, cblk->layers[layno].npasses);
 
-                length = cblk->passes[cblk->ninclpasses-1].rate;
-                llen = av_log2(length) - av_log2(cblk->ninclpasses) - 2;
-                if (llen < 0){
-                    pad = -llen;
-                    llen = 0;
+                length = cblk->layers[layno].data_len;
+                if (layno == nlayers - 1 && cblk->layers->cum_passes){
+                    length += cblk->passes[cblk->layers->cum_passes-1].flushed_len;
                 }
+                if (cblk->lblock + av_log2(cblk->layers[layno].npasses) < av_log2(length) + 1) {
+                    llen = av_log2(length) + 1 - cblk->lblock - av_log2(cblk->layers[layno].npasses);
+                }
+
                 // length of code block
+                cblk->lblock += llen;
                 put_bits(s, 1, llen);
                 put_bits(s, 0, 1);
-                put_num(s, length, av_log2(length)+1+pad);
+                put_num(s, length, cblk->lblock + av_log2(cblk->layers[layno].npasses));
             }
         }
     }
@@ -825,13 +911,14 @@  static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
             int xi;
             for (xi = 0; xi < cblknw; xi++){
                 Jpeg2000Cblk *cblk = prec->cblk + yi * cblknw + xi;
-                if (cblk->ninclpasses){
-                    if (s->buf_end - s->buf < cblk->passes[cblk->ninclpasses-1].rate)
+                if (cblk->layers[layno].npasses){
+                    if (s->buf_end - s->buf < cblk->layers[layno].data_len + 2)
                         return -1;
-                    bytestream_put_buffer(&s->buf, cblk->data + 1,   cblk->passes[cblk->ninclpasses-1].rate
-                                                               - cblk->passes[cblk->ninclpasses-1].flushed_len);
-                    bytestream_put_buffer(&s->buf, cblk->passes[cblk->ninclpasses-1].flushed,
-                                                   cblk->passes[cblk->ninclpasses-1].flushed_len);
+                    bytestream_put_buffer(&s->buf, cblk->layers[layno].data_start + 1, cblk->layers[layno].data_len);
+                    if (layno == nlayers - 1 && cblk->layers->cum_passes){
+                        bytestream_put_buffer(&s->buf, cblk->passes[cblk->layers->cum_passes-1].flushed,
+                                                       cblk->passes[cblk->layers->cum_passes-1].flushed_len);
+                    }
                 }
             }
         }
@@ -839,9 +926,9 @@  static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
     return 0;
 }
 
-static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno)
+static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno, int nlayers)
 {
-    int compno, reslevelno, ret;
+    int compno, reslevelno, layno, ret;
     Jpeg2000CodingStyle *codsty = &s->codsty;
     Jpeg2000QuantStyle  *qntsty = &s->qntsty;
     int packetno = 0;
@@ -858,29 +945,33 @@  static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
 
     av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
     // lay-rlevel-comp-pos progression
-    switch (s->prog) {
+        switch (s->prog) {
     case JPEG2000_PGOD_LRCP:
-    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
-        for (compno = 0; compno < s->ncomponents; compno++){
-            int precno;
-            Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
-            for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
-                if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
-                              qntsty->nguardbits, packetno++)) < 0)
-                    return ret;
+    for (layno = 0; layno < nlayers; layno++){
+        for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
+            for (compno = 0; compno < s->ncomponents; compno++){
+                int precno;
+                Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
+                for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
+                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
+                                qntsty->nguardbits, packetno++, nlayers)) < 0)
+                        return ret;
+                }
             }
         }
     }
     break;
     case JPEG2000_PGOD_RLCP:
     for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++){
-        for (compno = 0; compno < s->ncomponents; compno++){
-            int precno;
-            Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
-            for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
-                if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
-                              qntsty->nguardbits, packetno++)) < 0)
-                    return ret;
+        for (layno = 0; layno < nlayers; layno++){
+            for (compno = 0; compno < s->ncomponents; compno++){
+                int precno;
+                Jpeg2000ResLevel *reslevel = s->tile[tileno].comp[compno].reslevel + reslevelno;
+                for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
+                    if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
+                                qntsty->nguardbits, packetno++, nlayers)) < 0)
+                        return ret;
+                }
             }
         }
     }
@@ -935,10 +1026,11 @@  static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
                                prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
                         continue;
                     }
-
-                    if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
-                              qntsty->nguardbits, packetno++)) < 0)
-                        return ret;
+                    for (layno = 0; layno < nlayers; layno++){
+                        if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
+                                qntsty->nguardbits, packetno++, nlayers)) < 0)
+                            return ret;
+                        }
                     }
                 }
             }
@@ -1001,9 +1093,11 @@  static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
                                    prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
                             continue;
                         }
-                        if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
-                                  qntsty->nguardbits, packetno++)) < 0)
-                            return ret;
+                        for (layno = 0; layno < nlayers; layno++){
+                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
+                                    qntsty->nguardbits, packetno++, nlayers)) < 0)
+                                return ret;
+                        }
                     }
                 }
             }
@@ -1062,9 +1156,11 @@  static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
                                    prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
                             continue;
                         }
-                        if ((ret = encode_packet(s, reslevel, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
-                                  qntsty->nguardbits, packetno++)) < 0)
-                            return ret;
+                        for (layno = 0; layno < nlayers; layno++){
+                            if ((ret = encode_packet(s, reslevel, layno, precno, qntsty->expn + (reslevelno ? 3*reslevelno-2 : 0),
+                                    qntsty->nguardbits, packetno++, nlayers)) < 0)
+                                return ret;
+                        }
                     }
                 }
             }
@@ -1076,27 +1172,98 @@  static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
     return 0;
 }
 
-static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm)
+static void makelayer(Jpeg2000EncoderContext *s, int layno, double thresh, Jpeg2000Tile* tile, int final)
 {
-    int passno, res = 0;
-    for (passno = 0; passno < cblk->npasses; passno++){
-        int dr;
-        int64_t dd;
-
-        dr = cblk->passes[passno].rate
-           - (res ? cblk->passes[res-1].rate:0);
-        dd = cblk->passes[passno].disto
-           - (res ? cblk->passes[res-1].disto:0);
-
-        if (((dd * dwt_norm) >> WMSEDEC_SHIFT) * dwt_norm >= dr * lambda)
-            res = passno+1;
+    int compno, resno, bandno, precno, cblkno;
+    int passno;
+
+    for (compno = 0; compno < s->ncomponents; compno++) {
+        Jpeg2000Component *comp = &tile->comp[compno];
+
+        for (resno = 0; resno < s->codsty.nreslevels; resno++) {
+            Jpeg2000ResLevel *reslevel = comp->reslevel + resno;
+
+            for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
+                for (bandno = 0; bandno < reslevel->nbands ; bandno++){
+                    Jpeg2000Band *band = reslevel->band + bandno;
+                    Jpeg2000Prec *prec = band->prec + precno;
+
+                    for (cblkno = 0; cblkno < prec->nb_codeblocks_height * prec->nb_codeblocks_width; cblkno++){
+                        Jpeg2000Cblk *cblk = prec->cblk + cblkno;
+                        Jpeg2000Layer *layer = &cblk->layers[layno];
+                        int n;
+
+                        if (layno == 0) {
+                            cblk->ninclpasses = 0;
+                        }
+
+                        n = cblk->ninclpasses;
+
+                        if (thresh < 0) {
+                            n = cblk->npasses;
+                        } else {
+                            for (passno = cblk->ninclpasses; passno < cblk->npasses; passno++) {
+                                int32_t dr;
+                                double dd;
+                                Jpeg2000Pass *pass = &cblk->passes[passno];
+
+                                if (n == 0) {
+                                    dr = pass->rate;
+                                    dd = (double)pass->disto;
+                                } else {
+                                    dr = pass->rate - cblk->passes[n - 1].rate;
+                                    dd = (double)pass->disto - (double)cblk->passes[n-1].disto;
+                                }
+
+                                if (!dr) {
+                                    if (dd) {
+                                        n = passno + 1;
+                                    }
+                                    continue;
+                                }
+
+                                if (thresh - (dd / dr) < DBL_EPSILON)
+                                    n = passno + 1;
+                            }
+                        }
+                        layer->npasses = n - cblk->ninclpasses;
+                        layer->cum_passes = n;
+
+                        if (layer->npasses == 0) {
+                            layer->disto = 0;
+                            layer->data_len = 0;
+                            continue;
+                        }
+
+                        if (cblk->ninclpasses == 0) {
+                            layer->data_len = cblk->passes[n - 1].rate;
+                            layer->data_start = cblk->data;
+                            layer->disto = cblk->passes[n - 1].disto;
+                        } else {
+                            layer->data_len = cblk->passes[n - 1].rate - cblk->passes[cblk->ninclpasses - 1].rate;
+                            layer->data_start = cblk->data + cblk->passes[cblk->ninclpasses - 1].rate;
+                            layer->disto = cblk->passes[n - 1].disto -
+                                           cblk->passes[cblk->ninclpasses - 1].disto;
+                        }
+                        if (final) {
+                            cblk->ninclpasses = n;
+                        }
+                    }
+                }
+            }
+        }
     }
-    return res;
 }
 
-static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
+static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
 {
-    int precno, compno, reslevelno, bandno, cblkno, lev;
+    int precno, compno, reslevelno, bandno, cblkno, lev, passno, layno;
+    int i;
+    double min = DBL_MAX;
+    double max = 0;
+    double thresh;
+    int tile_disto = 0;
+
     Jpeg2000CodingStyle *codsty = &s->codsty;
 
     for (compno = 0; compno < s->ncomponents; compno++){
@@ -1107,20 +1274,70 @@  static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
 
             for (precno = 0; precno < reslevel->num_precincts_x * reslevel->num_precincts_y; precno++){
                 for (bandno = 0; bandno < reslevel->nbands ; bandno++){
-                    int bandpos = bandno + (reslevelno > 0);
                     Jpeg2000Band *band = reslevel->band + bandno;
                     Jpeg2000Prec *prec = band->prec + precno;
 
                     for (cblkno = 0; cblkno < prec->nb_codeblocks_height * prec->nb_codeblocks_width; cblkno++){
                         Jpeg2000Cblk *cblk = prec->cblk + cblkno;
+                        for (passno = 0; passno < cblk->npasses; passno++) {
+                            Jpeg2000Pass *pass = &cblk->passes[passno];
+                            int dr;
+                            double dd, drslope;
+
+                            tile_disto += pass->disto;
+                            if (passno == 0) {
+                                dr = (int32_t)pass->rate;
+                                dd = (double)pass->disto;
+                            } else {
+                                dr = (int32_t)(pass->rate) - cblk->passes[passno - 1].rate;
+                                dd = (double)pass->disto - (double)cblk->passes[passno - 1].disto;
+                            }
+
+                            if (dr <= 0)
+                                continue;
 
-                        cblk->ninclpasses = getcut(cblk, s->lambda,
-                                (int64_t)dwt_norms[codsty->transform == FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15);
+                            drslope = dd / dr;
+                            if (drslope < min)
+                                min = drslope;
+
+                            if (drslope > max)
+                                max = drslope;
+                        }
                     }
                 }
             }
         }
     }
+
+    for (layno = 0; layno < s->nlayers; layno++) {
+        double lo = min;
+        double hi = max;
+        double stable_thresh = 0;
+        double good_thresh = 0;
+        if (!s->layer_rates[layno]) {
+            good_thresh = -1;
+        } else {
+            for (i = 0; i < 128; i++) {
+                uint8_t *stream_pos = s->buf;
+                int ret;
+                thresh = (lo + hi) / 2;
+                makelayer(s, layno, thresh, tile, 0);
+                ret = encode_packets(s, tile, (int)(tile - s->tile), layno + 1);
+                memset(stream_pos, 0, s->buf - stream_pos);
+                if ((s->buf - stream_pos > ceil(tile->layer_rates[layno])) || ret < 0) {
+                    lo = thresh;
+                    s->buf = stream_pos;
+                    continue;
+                }
+                hi = thresh;
+                stable_thresh = thresh;
+                s->buf = stream_pos;
+            }
+        }
+        if (good_thresh >= 0)
+            good_thresh = stable_thresh == 0 ? thresh : stable_thresh;
+        makelayer(s, layno, good_thresh, tile, 1);
+    }
 }
 
 static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno)
@@ -1203,8 +1420,8 @@  static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno
     }
 
     av_log(s->avctx, AV_LOG_DEBUG, "rate control\n");
-    truncpasses(s, tile);
-    if ((ret = encode_packets(s, tile, tileno)) < 0)
+    makelayers(s, tile);
+    if ((ret = encode_packets(s, tile, tileno, s->nlayers)) < 0)
         return ret;
     av_log(s->avctx, AV_LOG_DEBUG, "after rate control\n");
     return 0;
@@ -1221,6 +1438,7 @@  static void cleanup(Jpeg2000EncoderContext *s)
             ff_jpeg2000_cleanup(comp, codsty, 1);
         }
         av_freep(&s->tile[tileno].comp);
+        av_freep(&s->tile[tileno].layer_rates);
     }
     av_freep(&s->tile);
 }
@@ -1379,6 +1597,64 @@  static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     return 0;
 }
 
+static int inline check_number(char* st, int* ret) {
+    int stlen = strlen(st);
+    int i;
+    *ret = 0;
+    if (stlen <= 0) {
+        return AVERROR_INVALIDDATA;
+    }
+    for (i = 0; i < stlen; i++) {
+        if (st[i] >= '0' && st[i] <= '9') {
+            *ret = (*ret) * 10 + (st[i] - '0');
+        } else {
+            return AVERROR_INVALIDDATA;
+        }
+    }
+    return 0;
+}
+
+static int parse_layer_rates(Jpeg2000EncoderContext *s)
+{
+    int i;
+    char* token;
+    int rate;
+    int nlayers = 0;
+    if (!s->lr_str) {
+        s->nlayers = 1;
+        s->layer_rates[0] = 0;
+        return 0;
+    }
+
+    token = strtok(s->lr_str, ",");
+    if (!check_number(token, &rate)) {
+            s->layer_rates[0] = rate <= 1 ? 0:rate;
+            nlayers++;
+    } else {
+            return AVERROR_INVALIDDATA;
+    }
+
+    while (1) {
+        token = strtok(NULL, ",");
+        if (!token)
+            break;
+        if (!check_number(token, &rate)) {
+            s->layer_rates[nlayers] = rate <= 1 ? 0:rate;
+            nlayers++;
+        } else {
+            return AVERROR_INVALIDDATA;
+        }
+    }
+
+    for (i = 1; i < nlayers; i++) {
+        if (s->layer_rates[i] >= s->layer_rates[i-1]) {
+            return AVERROR_INVALIDDATA;
+        }
+    }
+    s->nlayers = nlayers;
+    return 0;
+}
+
 static av_cold int j2kenc_init(AVCodecContext *avctx)
 {
     int i, ret;
@@ -1388,6 +1664,11 @@  static av_cold int j2kenc_init(AVCodecContext *avctx)
 
     s->avctx = avctx;
     av_log(s->avctx, AV_LOG_DEBUG, "init\n");
+    if (parse_layer_rates(s)) {
+        av_log(s, AV_LOG_WARNING, "Layer rates invalid. Shall encode with 1 layer.\n");
+        s->nlayers = 1;
+        s->layer_rates[0] = 0;
+    }
 
 #if FF_API_PRIVATE_OPT
 FF_DISABLE_DEPRECATION_WARNINGS
@@ -1408,6 +1689,7 @@  FF_ENABLE_DEPRECATION_WARNINGS
     memset(codsty->log2_prec_heights, 15, sizeof(codsty->log2_prec_heights));
     codsty->nreslevels2decode=
     codsty->nreslevels       = 7;
+    codsty->nlayers          = s->nlayers;
     codsty->log2_cblk_width  = 4;
     codsty->log2_cblk_height = 4;
     codsty->transform        = s->pred ? FF_DWT53 : FF_DWT97_INT;
@@ -1489,6 +1771,7 @@  static const AVOption options[] = {
     { "rpcl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RPCL            }, 0,         0,           VE, "prog" },
     { "pcrl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_PCRL            }, 0,         0,           VE, "prog" },
     { "cprl",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_CPRL            }, 0,         0,           VE, "prog" },
+    { "layer_rates",   "Layer Rates",       OFFSET(lr_str),        AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VE },
     { NULL }
 };
 
diff --git a/libavcodec/jpeg2000.c b/libavcodec/jpeg2000.c
index 70c25a0ca2..1c294ad9e6 100644
--- a/libavcodec/jpeg2000.c
+++ b/libavcodec/jpeg2000.c
@@ -261,9 +261,11 @@  static void init_band_stepsize(AVCodecContext *avctx,
         band->f_stepsize *= 0.5;
 }
 
-static int init_prec(Jpeg2000Band *band,
+static int init_prec(AVCodecContext *avctx,
+                     Jpeg2000Band *band,
                      Jpeg2000ResLevel *reslevel,
                      Jpeg2000Component *comp,
+                     Jpeg2000CodingStyle *codsty,
                      int precno, int bandno, int reslevelno,
                      int log2_band_prec_width,
                      int log2_band_prec_height)
@@ -366,6 +368,11 @@  static int init_prec(Jpeg2000Band *band,
         cblk->lblock    = 3;
         cblk->length    = 0;
         cblk->npasses   = 0;
+        if (av_codec_is_encoder(avctx->codec)) {
+            cblk->layers = av_mallocz_array(codsty->nlayers, sizeof(*cblk->layers));
+            if (!cblk->layers)
+                return AVERROR(ENOMEM);
+        }
     }
 
     return 0;
@@ -439,7 +446,7 @@  static int init_band(AVCodecContext *avctx,
         return AVERROR(ENOMEM);
 
     for (precno = 0; precno < nb_precincts; precno++) {
-        ret = init_prec(band, reslevel, comp,
+        ret = init_prec(avctx, band, reslevel, comp, codsty,
                         precno, bandno, reslevelno,
                         log2_band_prec_width, log2_band_prec_height);
         if (ret < 0)
@@ -614,6 +621,8 @@  void ff_jpeg2000_cleanup(Jpeg2000Component *comp, Jpeg2000CodingStyle *codsty, i
                             av_freep(&cblk->passes);
                             av_freep(&cblk->lengthinc);
                             av_freep(&cblk->data_start);
+                            if (isencoder)
+                                av_freep(&cblk->layers);
                         }
                         av_freep(&prec->cblk);
                     }
diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
index fee9607e86..3c6e8ede16 100644
--- a/libavcodec/jpeg2000.h
+++ b/libavcodec/jpeg2000.h
@@ -162,10 +162,19 @@  typedef struct Jpeg2000Pass {
     int flushed_len;
 } Jpeg2000Pass;
 
+typedef struct Jpeg2000Layer {
+    uint8_t *data_start;
+    int data_len;
+    int npasses;
+    double disto;
+    int cum_passes;
+} Jpeg2000Layer;
+
 typedef struct Jpeg2000Cblk {
     uint8_t npasses;
     uint8_t ninclpasses; // number coding of passes included in codestream
     uint8_t nonzerobits;
+    uint8_t incl;
     uint16_t length;
     uint16_t *lengthinc;
     uint8_t nb_lengthinc;
@@ -176,6 +185,7 @@  typedef struct Jpeg2000Cblk {
     int nb_terminationsinc;
     int *data_start;
     Jpeg2000Pass *passes;
+    Jpeg2000Layer *layers;
     int coord[2][2]; // border coordinates {{x0, x1}, {y0, y1}}
 } Jpeg2000Cblk; // code block