]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/j2kenc.c
avcodec: Constify AVCodecs
[ffmpeg] / libavcodec / j2kenc.c
index e91d932bb7f24834b2dc09887a01b5b6b37b831f..c51adadd137db457e0460264372fc297cde7ce3c 100644 (file)
@@ -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
@@ -72,6 +73,7 @@
 #include "libavutil/pixdesc.h"
 #include "libavutil/opt.h"
 #include "libavutil/intreadwrite.h"
+#include "libavutil/avstring.h"
 
 #define NMSEDEC_BITS 7
 #define NMSEDEC_FRACBITS (NMSEDEC_BITS-1)
@@ -100,6 +102,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,9 +129,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;
 
 
@@ -239,27 +249,36 @@ static void j2k_flush(Jpeg2000EncoderContext *s)
 static void tag_tree_code(Jpeg2000EncoderContext *s, Jpeg2000TgtNode *node, int threshold)
 {
     Jpeg2000TgtNode *stack[30];
-    int sp = 1, curval = 0;
-    stack[0] = node;
+    int sp = -1, curval = 0;
 
-    node = node->parent;
-    while(node){
-        if (node->vis){
-            curval = node->val;
-            break;
-        }
-        node->vis++;
-        stack[sp++] = node;
+    while(node->parent){
+        stack[++sp] = node;
         node = node->parent;
     }
-    while(--sp >= 0){
-        if (stack[sp]->val >= threshold){
+
+    while (1) {
+        if (curval > node->temp_val)
+            node->temp_val = curval;
+        else {
+            curval = node->temp_val;
+        }
+
+        if (node->val >= threshold) {
             put_bits(s, 0, threshold - curval);
-            break;
+            curval = threshold;
+        } else {
+            put_bits(s, 0, node->val - curval);
+            curval = node->val;
+            if (!node->vis) {
+                put_bits(s, 1, 1);
+                node->vis = 1;
+            }
         }
-        put_bits(s, 0, stack[sp]->val - curval);
-        put_bits(s, 1, 1);
-        curval = stack[sp]->val;
+
+        node->temp_val = curval;
+        if (sp < 0)
+            break;
+        node = stack[sp--];
     }
 }
 
@@ -298,7 +317,7 @@ static int put_siz(Jpeg2000EncoderContext *s)
     bytestream_put_be16(&s->buf, s->ncomponents); // CSiz
 
     for (i = 0; i < s->ncomponents; i++){ // Ssiz_i XRsiz_i, YRsiz_i
-        bytestream_put_byte(&s->buf, 7);
+        bytestream_put_byte(&s->buf, s->cbps[i] - 1);
         bytestream_put_byte(&s->buf, i?1<<s->chroma_shift[0]:1);
         bytestream_put_byte(&s->buf, i?1<<s->chroma_shift[1]:1);
     }
@@ -308,16 +327,21 @@ static int put_siz(Jpeg2000EncoderContext *s)
 static int put_cod(Jpeg2000EncoderContext *s)
 {
     Jpeg2000CodingStyle *codsty = &s->codsty;
+    uint8_t scod = 0;
 
     if (s->buf_end - s->buf < 14)
         return -1;
 
     bytestream_put_be16(&s->buf, JPEG2000_COD);
     bytestream_put_be16(&s->buf, 12); // Lcod
-    bytestream_put_byte(&s->buf, 0);  // Scod
+    if (s->sop)
+        scod |= JPEG2000_CSTY_SOP;
+    if (s->eph)
+        scod |= JPEG2000_CSTY_EPH;
+    bytestream_put_byte(&s->buf, scod);  // Scod
     // SGcod
-    bytestream_put_byte(&s->buf, 0); // progression level
-    bytestream_put_be16(&s->buf, 1); // num of layers
+    bytestream_put_byte(&s->buf, s->prog); // progression level
+    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{
@@ -396,6 +420,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
@@ -410,7 +459,7 @@ static int init_tiles(Jpeg2000EncoderContext *s)
     s->numXtiles = ff_jpeg2000_ceildiv(s->width, s->tile_width);
     s->numYtiles = ff_jpeg2000_ceildiv(s->height, s->tile_height);
 
-    s->tile = av_malloc_array(s->numXtiles, s->numYtiles * sizeof(Jpeg2000Tile));
+    s->tile = av_calloc(s->numXtiles, s->numYtiles * sizeof(Jpeg2000Tile));
     if (!s->tile)
         return AVERROR(ENOMEM);
     for (tileno = 0, tiley = 0; tiley < s->numYtiles; tiley++)
@@ -420,6 +469,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;
@@ -444,46 +498,53 @@ static int init_tiles(Jpeg2000EncoderContext *s)
                     return ret;
             }
         }
+    compute_rates(s);
     return 0;
 }
 
-static void copy_frame(Jpeg2000EncoderContext *s)
-{
-    int tileno, compno, i, y, x;
-    uint8_t *line;
-    for (tileno = 0; tileno < s->numXtiles * s->numYtiles; tileno++){
-        Jpeg2000Tile *tile = s->tile + tileno;
-        if (s->planar){
-            for (compno = 0; compno < s->ncomponents; compno++){
-                Jpeg2000Component *comp = tile->comp + compno;
-                int *dst = comp->i_data;
-                line = s->picture->data[compno]
-                       + comp->coord[1][0] * s->picture->linesize[compno]
-                       + comp->coord[0][0];
-                for (y = comp->coord[1][0]; y < comp->coord[1][1]; y++){
-                    uint8_t *ptr = line;
-                    for (x = comp->coord[0][0]; x < comp->coord[0][1]; x++)
-                        *dst++ = *ptr++ - (1 << 7);
-                    line += s->picture->linesize[compno];
-                }
-            }
-        } else{
-            line = s->picture->data[0] + tile->comp[0].coord[1][0] * s->picture->linesize[0]
-                   + tile->comp[0].coord[0][0] * s->ncomponents;
-
-            i = 0;
-            for (y = tile->comp[0].coord[1][0]; y < tile->comp[0].coord[1][1]; y++){
-                uint8_t *ptr = line;
-                for (x = tile->comp[0].coord[0][0]; x < tile->comp[0].coord[0][1]; x++, i++){
-                    for (compno = 0; compno < s->ncomponents; compno++){
-                        tile->comp[compno].i_data[i] = *ptr++  - (1 << 7);
-                    }
-                }
-                line += s->picture->linesize[0];
-            }
-        }
+#define COPY_FRAME(D, PIXEL)                                                                                                \
+    static void copy_frame_ ##D(Jpeg2000EncoderContext *s)                                                                  \
+    {                                                                                                                       \
+        int tileno, compno, i, y, x;                                                                                        \
+        PIXEL *line;                                                                                                        \
+        for (tileno = 0; tileno < s->numXtiles * s->numYtiles; tileno++){                                                   \
+            Jpeg2000Tile *tile = s->tile + tileno;                                                                          \
+            if (s->planar){                                                                                                 \
+                for (compno = 0; compno < s->ncomponents; compno++){                                                        \
+                    Jpeg2000Component *comp = tile->comp + compno;                                                          \
+                    int *dst = comp->i_data;                                                                                \
+                    int cbps = s->cbps[compno];                                                                             \
+                    line = (PIXEL*)s->picture->data[compno]                                                                 \
+                           + comp->coord[1][0] * (s->picture->linesize[compno] / sizeof(PIXEL))                             \
+                           + comp->coord[0][0];                                                                             \
+                    for (y = comp->coord[1][0]; y < comp->coord[1][1]; y++){                                                \
+                        PIXEL *ptr = line;                                                                                  \
+                        for (x = comp->coord[0][0]; x < comp->coord[0][1]; x++)                                             \
+                            *dst++ = *ptr++ - (1 << (cbps - 1));                                                            \
+                        line += s->picture->linesize[compno] / sizeof(PIXEL);                                               \
+                    }                                                                                                       \
+                }                                                                                                           \
+            } else{                                                                                                         \
+                line = (PIXEL*)s->picture->data[0] + tile->comp[0].coord[1][0] * (s->picture->linesize[0] / sizeof(PIXEL))  \
+                       + tile->comp[0].coord[0][0] * s->ncomponents;                                                        \
+                                                                                                                            \
+                i = 0;                                                                                                      \
+                for (y = tile->comp[0].coord[1][0]; y < tile->comp[0].coord[1][1]; y++){                                    \
+                    PIXEL *ptr = line;                                                                                      \
+                    for (x = tile->comp[0].coord[0][0]; x < tile->comp[0].coord[0][1]; x++, i++){                           \
+                        for (compno = 0; compno < s->ncomponents; compno++){                                                \
+                            int cbps = s->cbps[compno];                                                                     \
+                            tile->comp[compno].i_data[i] = *ptr++  - (1 << (cbps - 1));                                     \
+                        }                                                                                                   \
+                    }                                                                                                       \
+                    line += s->picture->linesize[0] / sizeof(PIXEL);                                                        \
+                }                                                                                                           \
+            }                                                                                                               \
+        }                                                                                                                   \
     }
-}
+
+COPY_FRAME(8, uint8_t)
+COPY_FRAME(16, uint16_t)
 
 static void init_quantization(Jpeg2000EncoderContext *s)
 {
@@ -521,13 +582,13 @@ static void init_luts(void)
         mask = ~((1<<NMSEDEC_FRACBITS)-1);
 
     for (i = 0; i < (1 << NMSEDEC_BITS); i++){
-        lut_nmsedec_sig[i]  = FFMAX(6*i - (9<<NMSEDEC_FRACBITS-1) << 12-NMSEDEC_FRACBITS, 0);
+        lut_nmsedec_sig[i]  = FFMAX((3 * i << (13 - NMSEDEC_FRACBITS)) - (9 << 11), 0);
         lut_nmsedec_sig0[i] = FFMAX((i*i + (1<<NMSEDEC_FRACBITS-1) & mask) << 1, 0);
 
         a = (i >> (NMSEDEC_BITS-2)&2) + 1;
-        lut_nmsedec_ref[i]  = FFMAX((-2*i + (1<<NMSEDEC_FRACBITS) + a*i - (a*a<<NMSEDEC_FRACBITS-2))
-                                    << 13-NMSEDEC_FRACBITS, 0);
-        lut_nmsedec_ref0[i] = FFMAX(((i*i + (1-4*i << NMSEDEC_FRACBITS-1) + (1<<2*NMSEDEC_FRACBITS)) & mask)
+        lut_nmsedec_ref[i]  = FFMAX((a - 2) * (i << (13 - NMSEDEC_FRACBITS)) +
+                                    (1 << 13) - (a * a << 11), 0);
+        lut_nmsedec_ref0[i] = FFMAX(((i * i - (i << NMSEDEC_BITS) + (1 << 2 * NMSEDEC_FRACBITS) + (1 << (NMSEDEC_FRACBITS - 1))) & mask)
                                     << 1, 0);
     }
 }
@@ -680,6 +741,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;
 
@@ -691,8 +754,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: */
@@ -712,33 +777,82 @@ static void putnumpasses(Jpeg2000EncoderContext *s, int n)
 }
 
 
-static int encode_packet(Jpeg2000EncoderContext *s, Jpeg2000ResLevel *rlevel, int precno,
-                          uint8_t *expn, int numgbits)
+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;
 
+    if (s->sop) {
+        bytestream_put_be16(&s->buf, JPEG2000_SOP);
+        bytestream_put_be16(&s->buf, 4);
+        bytestream_put_be16(&s->buf, packetno);
+    }
     // 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;
@@ -748,61 +862,70 @@ 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));
             }
         }
     }
     j2k_flush(s);
-    for (bandno = 0; bandno < rlevel->nbands; bandno++){
+    if (s->eph) {
+        bytestream_put_be16(&s->buf, JPEG2000_EPH);
+    }
+
+    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);
+                    }
                 }
             }
         }
@@ -810,29 +933,420 @@ 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;
+    int step_x, step_y;
+    int x, y;
+    int tile_coord[2][2];
+    int col = tileno % s->numXtiles;
+    int row = tileno / s->numXtiles;
+
+    tile_coord[0][0] = col * s->tile_width;
+    tile_coord[0][1] = FFMIN(tile_coord[0][0] + s->tile_width, s->width);
+    tile_coord[1][0] = row * s->tile_height;
+    tile_coord[1][1] = FFMIN(tile_coord[1][0] + s->tile_height, s->height);
 
     av_log(s->avctx, AV_LOG_DEBUG, "tier2\n");
     // lay-rlevel-comp-pos progression
+    switch (s->prog) {
+    case JPEG2000_PGOD_LRCP:
+    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)) < 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;
+                }
             }
         }
     }
+    break;
+    case JPEG2000_PGOD_RPCL:
+    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) {
+        int precno;
+        step_x = 30;
+        step_y = 30;
+        for (compno = 0; compno < s->ncomponents; compno++) {
+            Jpeg2000Component *comp     = tile->comp + compno;
+            if (reslevelno < codsty->nreslevels) {
+                uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; //  ==> N_L - r
+                Jpeg2000ResLevel *rlevel = comp->reslevel + reslevelno;
+                step_x = FFMIN(step_x, rlevel->log2_prec_width  + reducedresno);
+                step_y = FFMIN(step_y, rlevel->log2_prec_height + reducedresno);
+            }
+        }
+
+        step_x = 1<<step_x;
+        step_y = 1<<step_y;
+        for (y = tile_coord[1][0]; y < tile_coord[1][1]; y = (y/step_y + 1)*step_y) {
+            for (x = tile_coord[0][0]; x < tile_coord[0][1]; x = (x/step_x + 1)*step_x) {
+                for (compno = 0; compno < s->ncomponents; compno++) {
+                    Jpeg2000Component *comp     = tile->comp + compno;
+                    uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; //  ==> N_L - r
+                    Jpeg2000ResLevel *reslevel = comp->reslevel + reslevelno;
+                    int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0};
+                    unsigned prcx, prcy;
+                    int trx0, try0;
+
+                    trx0 = ff_jpeg2000_ceildivpow2(tile_coord[0][0], log_subsampling[0] + reducedresno);
+                    try0 = ff_jpeg2000_ceildivpow2(tile_coord[1][0], log_subsampling[1] + reducedresno);
+
+                    if (!(y % ((uint64_t)1 << (reslevel->log2_prec_height + reducedresno + log_subsampling[1])) == 0 ||
+                        (y == tile_coord[1][0] && (try0 << reducedresno) % (1U << (reducedresno + reslevel->log2_prec_height)))))
+                        continue;
+
+                    if (!(x % ((uint64_t)1 << (reslevel->log2_prec_width + reducedresno + log_subsampling[0])) == 0 ||
+                        (x == tile_coord[0][0] && (trx0 << reducedresno) % (1U << (reducedresno + reslevel->log2_prec_width)))))
+                        continue;
+
+                    // check if a precinct exists
+                    prcx   = ff_jpeg2000_ceildivpow2(x, log_subsampling[0] + reducedresno) >> reslevel->log2_prec_width;
+                    prcy   = ff_jpeg2000_ceildivpow2(y, log_subsampling[1] + reducedresno) >> reslevel->log2_prec_height;
+                    prcx  -= ff_jpeg2000_ceildivpow2(comp->coord_o[0][0], reducedresno) >> reslevel->log2_prec_width;
+                    prcy  -= ff_jpeg2000_ceildivpow2(comp->coord_o[1][0], reducedresno) >> reslevel->log2_prec_height;
+                    precno = prcx + reslevel->num_precincts_x * prcy;
+
+                    if (prcx >= reslevel->num_precincts_x || prcy >= reslevel->num_precincts_y) {
+                        av_log(s->avctx, AV_LOG_WARNING, "prc %d %d outside limits %d %d\n",
+                               prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
+                        continue;
+                    }
+                    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;
+                        }
+                    }
+                }
+            }
+    }
+    break;
+    case JPEG2000_PGOD_PCRL:
+        step_x = 32;
+        step_y = 32;
+        for (compno = 0; compno < s->ncomponents; compno++) {
+            Jpeg2000Component *comp     = tile->comp + compno;
+
+            for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) {
+                uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; //  ==> N_L - r
+                Jpeg2000ResLevel *rlevel = comp->reslevel + reslevelno;
+                step_x = FFMIN(step_x, rlevel->log2_prec_width  + reducedresno);
+                step_y = FFMIN(step_y, rlevel->log2_prec_height + reducedresno);
+            }
+        }
+        if (step_x >= 31 || step_y >= 31){
+            avpriv_request_sample(s->avctx, "PCRL with large step");
+            return AVERROR_PATCHWELCOME;
+        }
+        step_x = 1<<step_x;
+        step_y = 1<<step_y;
+
+        for (y = tile_coord[1][0]; y < tile_coord[1][1]; y = (y/step_y + 1)*step_y) {
+            for (x = tile_coord[0][0]; x < tile_coord[0][1]; x = (x/step_x + 1)*step_x) {
+                for (compno = 0; compno < s->ncomponents; compno++) {
+                    Jpeg2000Component *comp     = tile->comp + compno;
+                    int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0};
+
+                    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) {
+                        unsigned prcx, prcy;
+                        int precno;
+                        uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; //  ==> N_L - r
+                        Jpeg2000ResLevel *reslevel = comp->reslevel + reslevelno;
+                        int trx0, try0;
+
+                        trx0 = ff_jpeg2000_ceildivpow2(tile_coord[0][0], log_subsampling[0] + reducedresno);
+                        try0 = ff_jpeg2000_ceildivpow2(tile_coord[1][0], log_subsampling[1] + reducedresno);
+
+                        if (!(y % ((uint64_t)1 << (reslevel->log2_prec_height + reducedresno + log_subsampling[1])) == 0 ||
+                            (y == tile_coord[1][0] && (try0 << reducedresno) % (1U << (reducedresno + reslevel->log2_prec_height)))))
+                            continue;
+
+                        if (!(x % ((uint64_t)1 << (reslevel->log2_prec_width + reducedresno + log_subsampling[0])) == 0 ||
+                            (x == tile_coord[0][0] && (trx0 << reducedresno) % (1U << (reducedresno + reslevel->log2_prec_width)))))
+                            continue;
+
+                        // check if a precinct exists
+                        prcx   = ff_jpeg2000_ceildivpow2(x, log_subsampling[0] + reducedresno) >> reslevel->log2_prec_width;
+                        prcy   = ff_jpeg2000_ceildivpow2(y, log_subsampling[1] + reducedresno) >> reslevel->log2_prec_height;
+                        prcx  -= ff_jpeg2000_ceildivpow2(comp->coord_o[0][0], reducedresno) >> reslevel->log2_prec_width;
+                        prcy  -= ff_jpeg2000_ceildivpow2(comp->coord_o[1][0], reducedresno) >> reslevel->log2_prec_height;
+
+                        precno = prcx + reslevel->num_precincts_x * prcy;
+
+                        if (prcx >= reslevel->num_precincts_x || prcy >= reslevel->num_precincts_y) {
+                            av_log(s->avctx, AV_LOG_WARNING, "prc %d %d outside limits %d %d\n",
+                                   prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
+                            continue;
+                        }
+                        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;
+                        }
+                    }
+                }
+            }
+        }
+    break;
+    case JPEG2000_PGOD_CPRL:
+        for (compno = 0; compno < s->ncomponents; compno++) {
+            Jpeg2000Component *comp     = tile->comp + compno;
+            int log_subsampling[2] = { compno?s->chroma_shift[0]:0, compno?s->chroma_shift[1]:0};
+            step_x = 32;
+            step_y = 32;
+
+            for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) {
+                uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; //  ==> N_L - r
+                Jpeg2000ResLevel *rlevel = comp->reslevel + reslevelno;
+                step_x = FFMIN(step_x, rlevel->log2_prec_width  + reducedresno);
+                step_y = FFMIN(step_y, rlevel->log2_prec_height + reducedresno);
+            }
+            if (step_x >= 31 || step_y >= 31){
+                avpriv_request_sample(s->avctx, "CPRL with large step");
+                return AVERROR_PATCHWELCOME;
+            }
+            step_x = 1<<step_x;
+            step_y = 1<<step_y;
+
+            for (y = tile_coord[1][0]; y < tile_coord[1][1]; y = (y/step_y + 1)*step_y) {
+                for (x = tile_coord[0][0]; x < tile_coord[0][1]; x = (x/step_x + 1)*step_x) {
+                    for (reslevelno = 0; reslevelno < codsty->nreslevels; reslevelno++) {
+                        unsigned prcx, prcy;
+                        int precno;
+                        int trx0, try0;
+                        uint8_t reducedresno = codsty->nreslevels - 1 -reslevelno; //  ==> N_L - r
+                        Jpeg2000ResLevel *reslevel = comp->reslevel + reslevelno;
+
+                        trx0 = ff_jpeg2000_ceildivpow2(tile_coord[0][0], log_subsampling[0] + reducedresno);
+                        try0 = ff_jpeg2000_ceildivpow2(tile_coord[1][0], log_subsampling[1] + reducedresno);
+
+                        if (!(y % ((uint64_t)1 << (reslevel->log2_prec_height + reducedresno + log_subsampling[1])) == 0 ||
+                            (y == tile_coord[1][0] && (try0 << reducedresno) % (1U << (reducedresno + reslevel->log2_prec_height)))))
+                            continue;
+
+                        if (!(x % ((uint64_t)1 << (reslevel->log2_prec_width + reducedresno + log_subsampling[0])) == 0 ||
+                            (x == tile_coord[0][0] && (trx0 << reducedresno) % (1U << (reducedresno + reslevel->log2_prec_width)))))
+                            continue;
+
+                        // check if a precinct exists
+                        prcx   = ff_jpeg2000_ceildivpow2(x, log_subsampling[0] + reducedresno) >> reslevel->log2_prec_width;
+                        prcy   = ff_jpeg2000_ceildivpow2(y, log_subsampling[1] + reducedresno) >> reslevel->log2_prec_height;
+                        prcx  -= ff_jpeg2000_ceildivpow2(comp->coord_o[0][0], reducedresno) >> reslevel->log2_prec_width;
+                        prcy  -= ff_jpeg2000_ceildivpow2(comp->coord_o[1][0], reducedresno) >> reslevel->log2_prec_height;
+
+                        precno = prcx + reslevel->num_precincts_x * prcy;
+
+                        if (prcx >= reslevel->num_precincts_x || prcy >= reslevel->num_precincts_y) {
+                            av_log(s->avctx, AV_LOG_WARNING, "prc %d %d outside limits %d %d\n",
+                                   prcx, prcy, reslevel->num_precincts_x, reslevel->num_precincts_y);
+                            continue;
+                        }
+                        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;
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
     av_log(s->avctx, AV_LOG_DEBUG, "after tier2\n");
     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;
@@ -841,9 +1355,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;
@@ -873,6 +1387,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;
                     }
                 }
             }
@@ -927,7 +1446,7 @@ static int encode_tile(Jpeg2000EncoderContext *s, Jpeg2000Tile *tile, int tileno
                             for (y = yy0; y < yy1; y++){
                                 int *ptr = t1.data + (y-yy0)*t1.stride;
                                 for (x = xx0; x < xx1; x++){
-                                    *ptr++ = comp->i_data[(comp->coord[0][1] - comp->coord[0][0]) * y + x] << NMSEDEC_FRACBITS;
+                                    *ptr++ = comp->i_data[(comp->coord[0][1] - comp->coord[0][0]) * y + x] * (1 << NMSEDEC_FRACBITS);
                                 }
                             }
                         } else{
@@ -960,8 +1479,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;
@@ -972,12 +1495,17 @@ static void cleanup(Jpeg2000EncoderContext *s)
     int tileno, compno;
     Jpeg2000CodingStyle *codsty = &s->codsty;
 
+    if (!s->tile)
+        return;
     for (tileno = 0; tileno < s->numXtiles * s->numYtiles; tileno++){
-        for (compno = 0; compno < s->ncomponents; compno++){
-            Jpeg2000Component *comp = s->tile[tileno].comp + compno;
-            ff_jpeg2000_cleanup(comp, codsty);
+        if (s->tile[tileno].comp) {
+            for (compno = 0; compno < s->ncomponents; compno++){
+                Jpeg2000Component *comp = s->tile[tileno].comp + compno;
+                ff_jpeg2000_cleanup(comp, codsty);
+            }
+            av_freep(&s->tile[tileno].comp);
         }
-        av_freep(&s->tile[tileno].comp);
+        av_freep(&s->tile[tileno].layer_rates);
     }
     av_freep(&s->tile);
 }
@@ -1015,7 +1543,11 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
 
     s->lambda = s->picture->quality * LAMBDA_SCALE;
 
-    copy_frame(s);
+    if (avctx->pix_fmt == AV_PIX_FMT_BGR48 || avctx->pix_fmt == AV_PIX_FMT_GRAY16)
+        copy_frame_16(s);
+    else
+        copy_frame_8(s);
+
     reinit(s);
 
     if (s->format == CODEC_JP2) {
@@ -1132,6 +1664,53 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     return 0;
 }
 
+static int parse_layer_rates(Jpeg2000EncoderContext *s)
+{
+    int i;
+    char *token;
+    char *saveptr = NULL;
+    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 = av_strtok(s->lr_str, ",", &saveptr);
+    if (rate = strtol(token, NULL, 10)) {
+            s->layer_rates[0] = rate <= 1 ? 0:rate;
+            nlayers++;
+    } else {
+            return AVERROR_INVALIDDATA;
+    }
+
+    while (1) {
+        token = av_strtok(NULL, ",", &saveptr);
+        if (!token)
+            break;
+        if (rate = strtol(token, NULL, 10)) {
+            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;
@@ -1141,13 +1720,12 @@ static av_cold int j2kenc_init(AVCodecContext *avctx)
 
     s->avctx = avctx;
     av_log(s->avctx, AV_LOG_DEBUG, "init\n");
-
-#if FF_API_PRIVATE_OPT
-FF_DISABLE_DEPRECATION_WARNINGS
-    if (avctx->prediction_method)
-        s->pred = avctx->prediction_method;
-FF_ENABLE_DEPRECATION_WARNINGS
-#endif
+    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 (avctx->pix_fmt == AV_PIX_FMT_PAL8 && (s->pred != FF_DWT97_INT || s->format != CODEC_JP2)) {
         av_log(s->avctx, AV_LOG_WARNING, "Forcing lossless jp2 for pal8\n");
@@ -1161,6 +1739,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;
@@ -1180,12 +1759,16 @@ FF_ENABLE_DEPRECATION_WARNINGS
     s->width = avctx->width;
     s->height = avctx->height;
 
-    for (i = 0; i < 3; i++)
-        s->cbps[i] = 8;
+    for (i = 0; i < 3; i++) {
+        if (avctx->pix_fmt == AV_PIX_FMT_GRAY16 || avctx->pix_fmt == AV_PIX_FMT_RGB48)
+            s->cbps[i] = 16;
+        else
+            s->cbps[i] = 8;
+    }
 
-    if (avctx->pix_fmt == AV_PIX_FMT_RGB24){
+    if (avctx->pix_fmt == AV_PIX_FMT_RGB24 || avctx->pix_fmt == AV_PIX_FMT_RGB48){
         s->ncomponents = 3;
-    } else if (avctx->pix_fmt == AV_PIX_FMT_GRAY8 || avctx->pix_fmt == AV_PIX_FMT_PAL8){
+    } else if (avctx->pix_fmt == AV_PIX_FMT_GRAY8 || avctx->pix_fmt == AV_PIX_FMT_PAL8 || avctx->pix_fmt == AV_PIX_FMT_GRAY16){
         s->ncomponents = 1;
     } else{ // planar YUV
         s->planar = 1;
@@ -1230,7 +1813,15 @@ static const AVOption options[] = {
     { "pred",          "DWT Type",          OFFSET(pred),          AV_OPT_TYPE_INT,   { .i64 = 0           }, 0,         1,           VE, "pred"        },
     { "dwt97int",      NULL,                0,                     AV_OPT_TYPE_CONST, { .i64 = 0           }, INT_MIN, INT_MAX,       VE, "pred"        },
     { "dwt53",         NULL,                0,                     AV_OPT_TYPE_CONST, { .i64 = 0           }, INT_MIN, INT_MAX,       VE, "pred"        },
-
+    { "sop",           "SOP marker",        OFFSET(sop),           AV_OPT_TYPE_INT,   { .i64 = 0           }, 0,         1,           VE, },
+    { "eph",           "EPH marker",        OFFSET(eph),           AV_OPT_TYPE_INT,   { .i64 = 0           }, 0,         1,           VE, },
+    { "prog",          "Progression Order", OFFSET(prog),          AV_OPT_TYPE_INT,   { .i64 = 0           }, JPEG2000_PGOD_LRCP,         JPEG2000_PGOD_CPRL,           VE, "prog" },
+    { "lrcp",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_LRCP           }, 0,         0,           VE, "prog" },
+    { "rlcp",          NULL,                OFFSET(prog),          AV_OPT_TYPE_CONST,   { .i64 = JPEG2000_PGOD_RLCP            }, 0,         0,           VE, "prog" },
+    { "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 }
 };
 
@@ -1241,7 +1832,7 @@ static const AVClass j2k_class = {
     .version    = LIBAVUTIL_VERSION_INT,
 };
 
-AVCodec ff_jpeg2000_encoder = {
+const AVCodec ff_jpeg2000_encoder = {
     .name           = "jpeg2000",
     .long_name      = NULL_IF_CONFIG_SMALL("JPEG 2000"),
     .type           = AVMEDIA_TYPE_VIDEO,
@@ -1255,7 +1846,9 @@ AVCodec ff_jpeg2000_encoder = {
         AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P,
         AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,
         AV_PIX_FMT_PAL8,
+        AV_PIX_FMT_RGB48, AV_PIX_FMT_GRAY16,
         AV_PIX_FMT_NONE
     },
     .priv_class     = &j2k_class,
+    .caps_internal  = FF_CODEC_CAP_INIT_CLEANUP,
 };