[FFmpeg-devel] [RFC PATCH v5 3/3] libavcodec/j2kenc: Support for multiple layers

gautamramk at gmail.com gautamramk at gmail.com
Tue Aug 25 16:55:45 EEST 2020


From: Gautam Ramakrishnan <gautamramk at 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.
---
 doc/encoders.texi                        |  16 +
 libavcodec/j2kenc.c                      | 492 +++++++++++++++++++----
 libavcodec/jpeg2000.c                    |  12 +-
 libavcodec/jpeg2000.h                    |  10 +
 tests/ref/vsynth/vsynth1-jpeg2000        |   8 +-
 tests/ref/vsynth/vsynth1-jpeg2000-97     |   6 +-
 tests/ref/vsynth/vsynth2-jpeg2000        |   8 +-
 tests/ref/vsynth/vsynth2-jpeg2000-97     |   8 +-
 tests/ref/vsynth/vsynth3-jpeg2000        |   8 +-
 tests/ref/vsynth/vsynth3-jpeg2000-97     |   8 +-
 tests/ref/vsynth/vsynth_lena-jpeg2000    |   8 +-
 tests/ref/vsynth/vsynth_lena-jpeg2000-97 |   8 +-
 12 files changed, 490 insertions(+), 102 deletions(-)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index fd9235a05a..481ad2fb3d 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -1401,6 +1401,22 @@ Possible values are:
 @end table
 Set to @code{lrcp} by default.
 
+ at item layer_rates @var{string}
+By default, compression is done using the quality metric. This option allows for
+compression using compression ratio. The compression ratio for each level could
+be specified. The compression ratio of a layer @code{l} species the what ratio of
+total file size is contained in the first @code{l} layers.
+
+Example usage:
+
+ at example
+ffmpeg -i input.bmp -c:v jpeg2000 -layer_rates "100,10,1" output.j2k
+ at end example
+
+This would compress the image to contain 3 layers, where the data contained in the
+first layer would be compressed by 1000 times, compressed by 100 in the first two layers,
+and shall contain all data while using all 3 layers.
+
 @end table
 
 @section librav1e
diff --git a/libavcodec/j2kenc.c b/libavcodec/j2kenc.c
index 87acd2d5c9..ed3ad73c8e 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 at haxxed.com>
+ * Copyright (c) 2020, Gautam Ramakrishnan <gautamramk at 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,16 @@ typedef struct {
     Jpeg2000QuantStyle  qntsty;
 
     Jpeg2000Tile *tile;
+    int layer_rates[100];
+    uint8_t compression_rate_enc; ///< Is compression done using compression ratio?
 
     int format;
     int pred;
     int sop;
     int eph;
     int prog;
+    int nlayers;
+    char *lr_str;
 } Jpeg2000EncoderContext;
 
 
@@ -334,7 +340,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{
@@ -413,6 +419,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) {
+                        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.0;
+                    }
+                }
+            }
+        }
+    }
+
+}
+
 /**
  * compute the sizes of tiles, resolution levels, bands, etc.
  * allocate memory for them
@@ -437,6 +468,11 @@ static int init_tiles(Jpeg2000EncoderContext *s)
             tile->comp = av_mallocz_array(s->ncomponents, sizeof(Jpeg2000Component));
             if (!tile->comp)
                 return AVERROR(ENOMEM);
+
+            tile->layer_rates = av_mallocz_array(s->nlayers, sizeof(*tile->layer_rates));
+            if (!tile->layer_rates)
+                return AVERROR(ENOMEM);
+
             for (compno = 0; compno < s->ncomponents; compno++){
                 Jpeg2000Component *comp = tile->comp + compno;
                 int ret, i, j;
@@ -461,6 +497,7 @@ static int init_tiles(Jpeg2000EncoderContext *s)
                     return ret;
             }
         }
+    compute_rates(s);
     return 0;
 }
 
@@ -703,6 +740,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;
 
@@ -714,8 +753,10 @@ static void encode_cblk(Jpeg2000EncoderContext *s, Jpeg2000T1Context *t1, Jpeg20
     cblk->npasses = passno;
     cblk->ninclpasses = passno;
 
-    if (passno)
+    if (passno) {
         cblk->passes[passno-1].rate = ff_mqc_flush_to(&t1->mqc, cblk->passes[passno-1].flushed, &cblk->passes[passno-1].flushed_len);
+        cblk->passes[passno-1].rate -= cblk->passes[passno-1].flushed_len;
+    }
 }
 
 /* tier-2 routines: */
@@ -735,10 +776,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;
@@ -750,22 +793,65 @@ 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 (band->coord[0][0] < band->coord[0][1]
+            &&  band->coord[1][0] < band->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, 99);
+                ff_tag_tree_zero(prec->cblkincl, prec->nb_codeblocks_width, prec->nb_codeblocks_height, 99);
+                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++){
-        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;
+        Jpeg2000Band *band = rlevel->band + bandno;
+        if (band->coord[0][0] < band->coord[0][1]
+        &&  band->coord[1][0] < band->coord[1][1]) {
+            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;
     }
 
-    for (bandno = 0; bandno < rlevel->nbands; bandno++){
+    for (bandno = 0; bandno < rlevel->nbands; bandno++) {
         Jpeg2000Band *band = rlevel->band + bandno;
         Jpeg2000Prec *prec = band->prec + precno;
         int yi, xi, pos;
@@ -775,42 +861,46 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
         ||  band->coord[1][0] == band->coord[1][1])
             continue;
 
-        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 (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[layno].cum_passes){
+                    length += cblk->passes[cblk->layers[layno].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));
             }
         }
     }
@@ -819,21 +909,22 @@ static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, in
         bytestream_put_be16(&s->buf, JPEG2000_EPH);
     }
 
-    for (bandno = 0; bandno < rlevel->nbands; bandno++){
+    for (bandno = 0; bandno < rlevel->nbands; bandno++) {
         Jpeg2000Band *band = rlevel->band + bandno;
         Jpeg2000Prec *prec = band->prec + precno;
         int yi, cblknw = prec->nb_codeblocks_width;
-        for (yi =0; yi < prec->nb_codeblocks_height; yi++){
+        for (yi =0; yi < prec->nb_codeblocks_height; yi++) {
             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[layno].cum_passes) {
+                        bytestream_put_buffer(&s->buf, cblk->passes[cblk->layers[layno].cum_passes-1].flushed,
+                                                       cblk->passes[cblk->layers[layno].cum_passes-1].flushed_len);
+                    }
                 }
             }
         }
@@ -841,9 +932,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;
@@ -862,27 +953,31 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
     // lay-rlevel-comp-pos progression
     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;
+                }
             }
         }
     }
@@ -937,10 +1032,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;
+                        }
                     }
                 }
             }
@@ -1003,9 +1099,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;
+                        }
                     }
                 }
             }
@@ -1064,9 +1162,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;
+                        }
                     }
                 }
             }
@@ -1078,6 +1178,174 @@ static int encode_packets(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int til
     return 0;
 }
 
+static void makelayer(Jpeg2000EncoderContext *s, int layno, double thresh, Jpeg2000Tile* tile, int final)
+{
+    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 = pass->disto;
+                                } else {
+                                    dr = pass->rate - cblk->passes[n - 1].rate;
+                                    dd = pass->disto - cblk->passes[n-1].disto;
+                                }
+
+                                if (!dr) {
+                                    if (dd != 0.0) {
+                                        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;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+static void makelayers(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
+{
+    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++){
+        Jpeg2000Component *comp = tile->comp + compno;
+
+        for (reslevelno = 0, lev = codsty->nreslevels-1; reslevelno < codsty->nreslevels; reslevelno++, lev--){
+            Jpeg2000ResLevel *reslevel = comp->reslevel + reslevelno;
+
+            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;
+                        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 = pass->disto;
+                            } else {
+                                dr = (int32_t)(pass->rate - cblk->passes[passno - 1].rate);
+                                dd = pass->disto - cblk->passes[passno - 1].disto;
+                            }
+
+                            if (dr <= 0)
+                                continue;
+
+                            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.0;
+        double good_thresh = 0.0;
+        if (!s->layer_rates[layno]) {
+            good_thresh = -1.0;
+        } 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.0)
+            good_thresh = stable_thresh == 0.0 ? thresh : stable_thresh;
+        makelayer(s, layno, good_thresh, tile, 1);
+    }
+}
+
 static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm)
 {
     int passno, res = 0;
@@ -1086,9 +1354,9 @@ static int getcut(Jpeg2000Cblk *cblk, int64_t lambda, int dwt_norm)
         int64_t dd;
 
         dr = cblk->passes[passno].rate
-           - (res ? cblk->passes[res-1].rate:0);
+           - (res ? cblk->passes[res-1].rate : 0);
         dd = cblk->passes[passno].disto
-           - (res ? cblk->passes[res-1].disto:0);
+           - (res ? cblk->passes[res-1].disto : 0);
 
         if (((dd * dwt_norm) >> WMSEDEC_SHIFT) * dwt_norm >= dr * lambda)
             res = passno+1;
@@ -1118,6 +1386,11 @@ static void truncpasses(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile)
 
                         cblk->ninclpasses = getcut(cblk, s->lambda,
                                 (int64_t)dwt_norms[codsty->transform == FF_DWT53][bandpos][lev] * (int64_t)band->i_stepsize >> 15);
+                        cblk->layers[0].data_start = cblk->data;
+                        cblk->layers[0].cum_passes = cblk->ninclpasses;
+                        cblk->layers[0].npasses = cblk->ninclpasses;
+                        if (cblk->ninclpasses)
+                            cblk->layers[0].data_len = cblk->passes[cblk->ninclpasses - 1].rate;
                     }
                 }
             }
@@ -1205,8 +1478,12 @@ 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)
+    if (s->compression_rate_enc)
+        makelayers(s, tile);
+    else
+        truncpasses(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;
@@ -1223,6 +1500,7 @@ static void cleanup(Jpeg2000EncoderContext *s)
             ff_jpeg2000_cleanup(comp, codsty);
         }
         av_freep(&s->tile[tileno].comp);
+        av_freep(&s->tile[tileno].layer_rates);
     }
     av_freep(&s->tile);
 }
@@ -1381,6 +1659,74 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     return 0;
 }
 
+static int inline check_number(char* st, int* ret) {
+    size_t stlen;
+    int i;
+    *ret = 0;
+
+    if (!st)
+        return AVERROR_INVALIDDATA;
+
+    stlen = strlen(st);
+    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;
+        s->compression_rate_enc = 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)) {
+            if (nlayers >= 100) {
+                return AVERROR_INVALIDDATA;
+            }
+            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;
+    s->compression_rate_enc = 1;
+    return 0;
+}
+
 static av_cold int j2kenc_init(AVCodecContext *avctx)
 {
     int i, ret;
@@ -1390,6 +1736,12 @@ 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. Encoding with 1 layer based on quality metric.\n");
+        s->nlayers = 1;
+        s->layer_rates[0] = 0;
+        s->compression_rate_enc = 0;
+    }
 
 #if FF_API_PRIVATE_OPT
 FF_DISABLE_DEPRECATION_WARNINGS
@@ -1410,6 +1762,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;
@@ -1491,6 +1844,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 2e26bc5b00..56d98c8a89 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,7 @@ void ff_jpeg2000_cleanup(Jpeg2000Component *comp, Jpeg2000CodingStyle *codsty)
                             av_freep(&cblk->passes);
                             av_freep(&cblk->lengthinc);
                             av_freep(&cblk->data_start);
+                            av_freep(&cblk->layers);
                         }
                         av_freep(&prec->cblk);
                     }
diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
index a9f2e01632..612832c872 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
 
diff --git a/tests/ref/vsynth/vsynth1-jpeg2000 b/tests/ref/vsynth/vsynth1-jpeg2000
index 48accf500a..f8e987f95d 100644
--- a/tests/ref/vsynth/vsynth1-jpeg2000
+++ b/tests/ref/vsynth/vsynth1-jpeg2000
@@ -1,4 +1,4 @@
-d2a06ad916711d29b30977a06335bb76 *tests/data/fate/vsynth1-jpeg2000.avi
-2265698 tests/data/fate/vsynth1-jpeg2000.avi
-15a8e49f6fd014193bbafd72f84936c7 *tests/data/fate/vsynth1-jpeg2000.out.rawvideo
-stddev:    5.36 PSNR: 33.55 MAXDIFF:   61 bytes:  7603200/  7603200
+95add005faf68fcf8f16e86eab079ca2 *tests/data/fate/vsynth1-jpeg2000.avi
+2263192 tests/data/fate/vsynth1-jpeg2000.avi
+b7f48a8965f78011c76483277befc6fc *tests/data/fate/vsynth1-jpeg2000.out.rawvideo
+stddev:    5.35 PSNR: 33.56 MAXDIFF:   59 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth1-jpeg2000-97 b/tests/ref/vsynth/vsynth1-jpeg2000-97
index 33b3299ad1..6ab5aa4237 100644
--- a/tests/ref/vsynth/vsynth1-jpeg2000-97
+++ b/tests/ref/vsynth/vsynth1-jpeg2000-97
@@ -1,4 +1,4 @@
-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
+e4d03b2e3c03e56c7f831b1e662c4031 *tests/data/fate/vsynth1-jpeg2000-97.avi
+3643928 tests/data/fate/vsynth1-jpeg2000-97.avi
+a2262f1da2f49bc196b780a6b47ec4e8 *tests/data/fate/vsynth1-jpeg2000-97.out.rawvideo
 stddev:    4.23 PSNR: 35.59 MAXDIFF:   53 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth2-jpeg2000 b/tests/ref/vsynth/vsynth2-jpeg2000
index 094f416882..d0df0099ea 100644
--- a/tests/ref/vsynth/vsynth2-jpeg2000
+++ b/tests/ref/vsynth/vsynth2-jpeg2000
@@ -1,4 +1,4 @@
-6c2f979e4a33a36f36aec86f2d464143 *tests/data/fate/vsynth2-jpeg2000.avi
-1494516 tests/data/fate/vsynth2-jpeg2000.avi
-36afd96d6e55bc83166fd615351ba366 *tests/data/fate/vsynth2-jpeg2000.out.rawvideo
-stddev:    5.00 PSNR: 34.15 MAXDIFF:   59 bytes:  7603200/  7603200
+8c8a68ca748190c71b3ea43e5ab7f502 *tests/data/fate/vsynth2-jpeg2000.avi
+1538736 tests/data/fate/vsynth2-jpeg2000.avi
+64fadc87447268cf90503cb294db7f61 *tests/data/fate/vsynth2-jpeg2000.out.rawvideo
+stddev:    4.91 PSNR: 34.29 MAXDIFF:   55 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth2-jpeg2000-97 b/tests/ref/vsynth/vsynth2-jpeg2000-97
index 38153ea0ea..33c1fb2425 100644
--- a/tests/ref/vsynth/vsynth2-jpeg2000-97
+++ b/tests/ref/vsynth/vsynth2-jpeg2000-97
@@ -1,4 +1,4 @@
-2e43f004a55f4a55a19c4b79fc8e8743 *tests/data/fate/vsynth2-jpeg2000-97.avi
-2448706 tests/data/fate/vsynth2-jpeg2000-97.avi
-a6e2453118a0de135836a868b2ca0e60 *tests/data/fate/vsynth2-jpeg2000-97.out.rawvideo
-stddev:    3.23 PSNR: 37.94 MAXDIFF:   29 bytes:  7603200/  7603200
+c8f76055f59804ca72dbd66eb4db83a2 *tests/data/fate/vsynth2-jpeg2000-97.avi
+2464138 tests/data/fate/vsynth2-jpeg2000-97.avi
+1f63c8b065e847e4c63d57ce23442ea8 *tests/data/fate/vsynth2-jpeg2000-97.out.rawvideo
+stddev:    3.21 PSNR: 37.99 MAXDIFF:   26 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth3-jpeg2000 b/tests/ref/vsynth/vsynth3-jpeg2000
index a1e3a8090a..ecc286b9a4 100644
--- a/tests/ref/vsynth/vsynth3-jpeg2000
+++ b/tests/ref/vsynth/vsynth3-jpeg2000
@@ -1,4 +1,4 @@
-0b8aa8113c10772cffff60f9c8ffd902 *tests/data/fate/vsynth3-jpeg2000.avi
-65548 tests/data/fate/vsynth3-jpeg2000.avi
-2d8bd94d558755c47d7e23fd9556e164 *tests/data/fate/vsynth3-jpeg2000.out.rawvideo
-stddev:    5.48 PSNR: 33.34 MAXDIFF:   47 bytes:    86700/    86700
+776bf3234cbf25002f129b89baab42ea *tests/data/fate/vsynth3-jpeg2000.avi
+67400 tests/data/fate/vsynth3-jpeg2000.avi
+098f5980667e1fcd50452b1dc1a74f61 *tests/data/fate/vsynth3-jpeg2000.out.rawvideo
+stddev:    5.47 PSNR: 33.36 MAXDIFF:   48 bytes:    86700/    86700
diff --git a/tests/ref/vsynth/vsynth3-jpeg2000-97 b/tests/ref/vsynth/vsynth3-jpeg2000-97
index 191956f580..df10f43270 100644
--- a/tests/ref/vsynth/vsynth3-jpeg2000-97
+++ b/tests/ref/vsynth/vsynth3-jpeg2000-97
@@ -1,4 +1,4 @@
-b6c88a623c3296ca945346d2203f0af0 *tests/data/fate/vsynth3-jpeg2000-97.avi
-83870 tests/data/fate/vsynth3-jpeg2000-97.avi
-0cd707bfb1bbe5312b00c094f695b1fa *tests/data/fate/vsynth3-jpeg2000-97.out.rawvideo
-stddev:    4.52 PSNR: 35.02 MAXDIFF:   47 bytes:    86700/    86700
+cd023db503f03ef72dd83e4617a90c7b *tests/data/fate/vsynth3-jpeg2000-97.avi
+85606 tests/data/fate/vsynth3-jpeg2000-97.avi
+8def36ad1413ab3a5c2af2e1af4603f9 *tests/data/fate/vsynth3-jpeg2000-97.out.rawvideo
+stddev:    4.51 PSNR: 35.04 MAXDIFF:   47 bytes:    86700/    86700
diff --git a/tests/ref/vsynth/vsynth_lena-jpeg2000 b/tests/ref/vsynth/vsynth_lena-jpeg2000
index 72550eb816..88629add21 100644
--- a/tests/ref/vsynth/vsynth_lena-jpeg2000
+++ b/tests/ref/vsynth/vsynth_lena-jpeg2000
@@ -1,4 +1,4 @@
-1f2cf6061c78905b8011091a9a7c425f *tests/data/fate/vsynth_lena-jpeg2000.avi
-1138054 tests/data/fate/vsynth_lena-jpeg2000.avi
-955653ca7a08447e7b1501b444f24562 *tests/data/fate/vsynth_lena-jpeg2000.out.rawvideo
-stddev:    4.40 PSNR: 35.25 MAXDIFF:   58 bytes:  7603200/  7603200
+b8aaa45236f77a2a626791d462fd8ac1 *tests/data/fate/vsynth_lena-jpeg2000.avi
+1188886 tests/data/fate/vsynth_lena-jpeg2000.avi
+39a2c5b61cd0cf2821c6fb4cceba2fa8 *tests/data/fate/vsynth_lena-jpeg2000.out.rawvideo
+stddev:    4.30 PSNR: 35.45 MAXDIFF:   45 bytes:  7603200/  7603200
diff --git a/tests/ref/vsynth/vsynth_lena-jpeg2000-97 b/tests/ref/vsynth/vsynth_lena-jpeg2000-97
index 5c12665b66..b6f5f75f77 100644
--- a/tests/ref/vsynth/vsynth_lena-jpeg2000-97
+++ b/tests/ref/vsynth/vsynth_lena-jpeg2000-97
@@ -1,4 +1,4 @@
-e5a756e97910420c90e76259c56261cb *tests/data/fate/vsynth_lena-jpeg2000-97.avi
-1918956 tests/data/fate/vsynth_lena-jpeg2000-97.avi
-93a4ba0c230f2430a813df594676e58a *tests/data/fate/vsynth_lena-jpeg2000-97.out.rawvideo
-stddev:    2.84 PSNR: 39.04 MAXDIFF:   28 bytes:  7603200/  7603200
+b2d9525433c6300674f504922d762437 *tests/data/fate/vsynth_lena-jpeg2000-97.avi
+1937232 tests/data/fate/vsynth_lena-jpeg2000-97.avi
+1b97333a8dc115a5ba609b0070d89d4d *tests/data/fate/vsynth_lena-jpeg2000-97.out.rawvideo
+stddev:    2.82 PSNR: 39.10 MAXDIFF:   24 bytes:  7603200/  7603200
-- 
2.17.1



More information about the ffmpeg-devel mailing list