diff mbox series

[FFmpeg-devel] avcodec/jpeg2000dec Support for PPT marker

Message ID 20200327133954.27082-1-gautamramk@gmail.com
State New
Headers show
Series [FFmpeg-devel] avcodec/jpeg2000dec Support for PPT marker
Related show

Checks

Context Check Description
andriy/ffmpeg-patchwork pending
andriy/ffmpeg-patchwork success Applied patch
andriy/ffmpeg-patchwork success Configure finished
andriy/ffmpeg-patchwork warning New warnings during build
andriy/ffmpeg-patchwork success Make finished
andriy/ffmpeg-patchwork success Make fate finished

Commit Message

Gautam Ramakrishnan March 27, 2020, 1:39 p.m. UTC
From: Gautam Ramakrishnan <gautamramk@gmail.com>

This patch adds support for the JPEG2000 PPT marker. This patch hence,
fixes #4610. The patch was tested on the samples posted along with
ticket #4610.
---
 libavcodec/jpeg2000dec.c | 348 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 331 insertions(+), 17 deletions(-)

Comments

Carl Eugen Hoyos March 27, 2020, 2:29 p.m. UTC | #1
Am Fr., 27. März 2020 um 14:47 Uhr schrieb <gautamramk@gmail.com>:
>
> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>
> This patch adds support for the JPEG2000 PPT marker. This patch hence,
> fixes #4610. The patch was tested on the samples posted along with
> ticket #4610.

Your patch adds a compile-time warning here, please fix that.

@Michael: Both samples from ticket #4610 are decoded visually correct,
the sample from the testsrc (p1_02.j2k) is not lossless afaict (jasper and
openjpeg produce different output), the samma sample is lossless but
since it is yuv, it will take a little time to test for bit-exactness.

Carl Eugen
Carl Eugen Hoyos March 27, 2020, 2:32 p.m. UTC | #2
Am Fr., 27. März 2020 um 15:29 Uhr schrieb Carl Eugen Hoyos
<ceffmpeg@gmail.com>:
>
> Am Fr., 27. März 2020 um 14:47 Uhr schrieb <gautamramk@gmail.com>:
> >
> > From: Gautam Ramakrishnan <gautamramk@gmail.com>
> >
> > This patch adds support for the JPEG2000 PPT marker. This patch hence,
> > fixes #4610. The patch was tested on the samples posted along with
> > ticket #4610.
>
> Your patch adds a compile-time warning here, please fix that.
>
> @Michael: Both samples from ticket #4610 are decoded visually correct,
> the sample from the testsrc (p1_02.j2k) is not lossless afaict (jasper and
> openjpeg produce different output), the samma sample is lossless but
> since it is yuv, it will take a little time to test for bit-exactness.

Not so difficult: The samma sample produces bit-exact output with the patch.

Carl Eugen
Gautam Ramakrishnan March 27, 2020, 2:57 p.m. UTC | #3
Hi,

I was a bit apprehensive about removing the jpeg2000_decode_packet()
function. I had basically split that function into 2 separate ones.
Should I keep it till we add more changes to the decoder or do I
remove it now itself?

On Fri, Mar 27, 2020 at 8:03 PM Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>
> Am Fr., 27. März 2020 um 15:29 Uhr schrieb Carl Eugen Hoyos
> <ceffmpeg@gmail.com>:
> >
> > Am Fr., 27. März 2020 um 14:47 Uhr schrieb <gautamramk@gmail.com>:
> > >
> > > From: Gautam Ramakrishnan <gautamramk@gmail.com>
> > >
> > > This patch adds support for the JPEG2000 PPT marker. This patch hence,
> > > fixes #4610. The patch was tested on the samples posted along with
> > > ticket #4610.
> >
> > Your patch adds a compile-time warning here, please fix that.
> >
> > @Michael: Both samples from ticket #4610 are decoded visually correct,
> > the sample from the testsrc (p1_02.j2k) is not lossless afaict (jasper and
> > openjpeg produce different output), the samma sample is lossless but
> > since it is yuv, it will take a little time to test for bit-exactness.
>
> Not so difficult: The samma sample produces bit-exact output with the patch.
>
> Carl Eugen
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
Carl Eugen Hoyos March 27, 2020, 2:57 p.m. UTC | #4
Am Fr., 27. März 2020 um 14:47 Uhr schrieb <gautamramk@gmail.com>:
>
> From: Gautam Ramakrishnan <gautamramk@gmail.com>
>
> This patch adds support for the JPEG2000 PPT marker. This patch hence,
> fixes #4610. The patch was tested on the samples posted along with
> ticket #4610.

Some frames in this sample are now decoded correctly, some still fail:
http://samples.ffmpeg.org/jpeg2000/jpeg2000_mxf_first_10mb.mxf
(Also a SAMMA sample)

Carl Eugen
Carl Eugen Hoyos March 27, 2020, 3 p.m. UTC | #5
Am Fr., 27. März 2020 um 15:58 Uhr schrieb Gautam Ramakrishnan
<gautamramk@gmail.com>:

> I was a bit apprehensive about removing the jpeg2000_decode_packet()
> function. I had basically split that function into 2 separate ones.
> Should I keep it till we add more changes to the decoder or do I
> remove it now itself?

Not sure what the right solution is but there should be no (new)
compilation warning.

If you have split a function, then this should be a separate patch
that makes the reviews easier.
(First patch: Splitting a function without functional improvement,
second patch: Functional improvement)

Please find out what top-posting means and avoid it here.

Carl Eugen
Gautam Ramakrishnan March 27, 2020, 3:06 p.m. UTC | #6
On Fri, Mar 27, 2020 at 8:30 PM Carl Eugen Hoyos <ceffmpeg@gmail.com> wrote:
>
> Am Fr., 27. März 2020 um 15:58 Uhr schrieb Gautam Ramakrishnan
> <gautamramk@gmail.com>:
>
> > I was a bit apprehensive about removing the jpeg2000_decode_packet()
> > function. I had basically split that function into 2 separate ones.
> > Should I keep it till we add more changes to the decoder or do I
> > remove it now itself?
>
> Not sure what the right solution is but there should be no (new)
> compilation warning.
>
> If you have split a function, then this should be a separate patch
> that makes the reviews easier.
I'll make a patch series and send a v2
> (First patch: Splitting a function without functional improvement,
> second patch: Functional improvement)
>
> Please find out what top-posting means and avoid it here.
>
Got it
> Carl Eugen
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox series

Patch

diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c
index 019dc81f56..ee003bebf2 100644
--- a/libavcodec/jpeg2000dec.c
+++ b/libavcodec/jpeg2000dec.c
@@ -83,8 +83,12 @@  typedef struct Jpeg2000Tile {
     Jpeg2000QuantStyle  qntsty[4];
     Jpeg2000POC         poc;
     Jpeg2000TilePart    tile_part[32];
-    uint16_t tp_idx;                    // Tile-part index
-    int coord[2][2];                    // border coordinates {{x0, x1}, {y0, y1}}
+    uint8_t             has_ppt;                // whether this tile has a ppt marker
+    uint8_t             *packed_headers;        // contains packed headers. Used only along with PPT marker
+    int                 packed_headers_size;    // size in bytes of the packed headers
+    GetByteContext      packed_headers_stream;  // byte context corresponding to packed headers
+    uint16_t tp_idx;                            // Tile-part index
+    int coord[2][2];                            // border coordinates {{x0, x1}, {y0, y1}}
 } Jpeg2000Tile;
 
 typedef struct Jpeg2000DecoderContext {
@@ -851,6 +855,38 @@  static int get_plt(Jpeg2000DecoderContext *s, int n)
     return 0;
 }
 
+static int get_ppt(Jpeg2000DecoderContext *s, int n)
+{
+    Jpeg2000Tile *tile;
+
+    tile = &s->tile[s->curtileno];
+
+    av_assert1(s->curtileno >= 0);
+    if (tile->tp_idx != 0) {
+        av_log(s->avctx, AV_LOG_ERROR,
+               "PPT marker can occur only on first tile part of a tile.\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    tile->has_ppt = 1;  // this tile has a ppt marker
+/*    Zppt = */ bytestream2_get_byte(&s->g);
+    if (!tile->packed_headers) {
+        tile->packed_headers = av_malloc_array(n - 3, sizeof(uint8_t));
+        memcpy(tile->packed_headers, s->g.buffer, sizeof(uint8_t)*(n - 3));
+        tile->packed_headers_size = n - 3;
+    } else {
+        tile->packed_headers = av_realloc_array(tile->packed_headers,
+                                                tile->packed_headers_size + n - 3,
+                                                sizeof(uint8_t));
+        memcpy(tile->packed_headers + tile->packed_headers_size,
+               s->g.buffer, sizeof(uint8_t)*(n - 3));
+        tile->packed_headers_size += n - 3;
+    }
+    bytestream2_skip(&s->g, n - 3);
+
+    return 0;
+}
+
 static int init_tile(Jpeg2000DecoderContext *s, int tileno)
 {
     int compno;
@@ -923,6 +959,216 @@  static int getlblockinc(Jpeg2000DecoderContext *s)
     return res;
 }
 
+static int jpeg2000_decode_packet_header(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
+                                         int *tp_index,
+                                         Jpeg2000CodingStyle *codsty,
+                                         Jpeg2000ResLevel *rlevel, int precno,
+                                         int layno, uint8_t *expn, int numgbits,
+                                         int *process_data)
+{
+    int bandno, cblkno, ret, nb_code_blocks;
+
+    if (layno < rlevel->band[0].prec[precno].decoded_layers) {
+        *process_data = 0;
+        return 0;
+    }
+    rlevel->band[0].prec[precno].decoded_layers = layno + 1;
+
+    if (tile->has_ppt) {
+        s->g = tile->packed_headers_stream;
+    } else {
+            s->g = tile->tile_part[*tp_index].tpg;
+            if (bytestream2_get_bytes_left(&s->g) == 0 && s->bit_index == 8) {
+                if (*tp_index < FF_ARRAY_ELEMS(tile->tile_part) - 1) {
+                    s->g = tile->tile_part[++(*tp_index)].tpg;
+            }
+        }
+        if (bytestream2_peek_be32(&s->g) == JPEG2000_SOP_FIXED_BYTES)
+            bytestream2_skip(&s->g, JPEG2000_SOP_BYTE_LENGTH);
+    }
+
+    if (!(ret = get_bits(s, 1))) {
+        jpeg2000_flush(s);
+        *process_data = 0;
+        goto end;
+    } else if (ret < 0)
+        return ret;
+
+    for (bandno = 0; bandno < rlevel->nbands; bandno++) {
+        Jpeg2000Band *band = rlevel->band + bandno;
+        Jpeg2000Prec *prec = band->prec + precno;
+
+        if (band->coord[0][0] == band->coord[0][1] ||
+            band->coord[1][0] == band->coord[1][1])
+            continue;
+        nb_code_blocks =  prec->nb_codeblocks_height *
+                          prec->nb_codeblocks_width;
+        for (cblkno = 0; cblkno < nb_code_blocks; cblkno++) {
+            Jpeg2000Cblk *cblk = prec->cblk + cblkno;
+            int incl, newpasses, llen;
+            void *tmp;
+
+            if (cblk->npasses)
+                incl = get_bits(s, 1);
+            else
+                incl = tag_tree_decode(s, prec->cblkincl + cblkno, layno + 1) == layno;
+            if (!incl)
+                continue;
+            else if (incl < 0)
+                return incl;
+
+            if (!cblk->npasses) {
+                int v = expn[bandno] + numgbits - 1 -
+                        tag_tree_decode(s, prec->zerobits + cblkno, 100);
+                if (v < 0 || v > 30) {
+                    av_log(s->avctx, AV_LOG_ERROR,
+                           "nonzerobits %d invalid or unsupported\n", v);
+                    return AVERROR_INVALIDDATA;
+                }
+                cblk->nonzerobits = v;
+            }
+            if ((newpasses = getnpasses(s)) < 0)
+                return newpasses;
+            av_assert2(newpasses > 0);
+            if (cblk->npasses + newpasses >= JPEG2000_MAX_PASSES) {
+                avpriv_request_sample(s->avctx, "Too many passes");
+                return AVERROR_PATCHWELCOME;
+            }
+            if ((llen = getlblockinc(s)) < 0)
+                return llen;
+            if (cblk->lblock + llen + av_log2(newpasses) > 16) {
+                avpriv_request_sample(s->avctx,
+                                      "Block with length beyond 16 bits");
+                return AVERROR_PATCHWELCOME;
+            }
+
+            cblk->lblock += llen;
+
+            cblk->nb_lengthinc = 0;
+            cblk->nb_terminationsinc = 0;
+            av_free(cblk->lengthinc);
+            cblk->lengthinc  = av_mallocz_array(newpasses    , sizeof(*cblk->lengthinc));
+            if (!cblk->lengthinc)
+                return AVERROR(ENOMEM);
+            tmp = av_realloc_array(cblk->data_start, cblk->nb_terminations + newpasses + 1, sizeof(*cblk->data_start));
+            if (!tmp)
+                return AVERROR(ENOMEM);
+            cblk->data_start = tmp;
+            do {
+                int newpasses1 = 0;
+
+                while (newpasses1 < newpasses) {
+                    newpasses1 ++;
+                    if (needs_termination(codsty->cblk_style, cblk->npasses + newpasses1 - 1)) {
+                        cblk->nb_terminationsinc ++;
+                        break;
+                    }
+                }
+
+                if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0)
+                    return ret;
+                if (ret > cblk->data_allocated) {
+                    size_t new_size = FFMAX(2*cblk->data_allocated, ret);
+                    void *new = av_realloc(cblk->data, new_size);
+                    if (new) {
+                        cblk->data = new;
+                        cblk->data_allocated = new_size;
+                    }
+                }
+                if (ret > cblk->data_allocated) {
+                    avpriv_request_sample(s->avctx,
+                                        "Block with lengthinc greater than %"SIZE_SPECIFIER"",
+                                        cblk->data_allocated);
+                    return AVERROR_PATCHWELCOME;
+                }
+                cblk->lengthinc[cblk->nb_lengthinc++] = ret;
+                cblk->npasses  += newpasses1;
+                newpasses -= newpasses1;
+            } while(newpasses);
+        }
+    }
+    jpeg2000_flush(s);
+
+    if (codsty->csty & JPEG2000_CSTY_EPH) {
+        if (bytestream2_peek_be16(&s->g) == JPEG2000_EPH)
+            bytestream2_skip(&s->g, 2);
+        else
+            av_log(s->avctx, AV_LOG_ERROR, "EPH marker not found. instead %X\n", bytestream2_peek_be32(&s->g));
+    }
+end:
+    if (tile->has_ppt)
+        tile->packed_headers_stream = s->g;
+    else
+        tile->tile_part[*tp_index].tpg = s->g;
+    return 0;
+}
+
+static int jpeg2000_decode_packet_data(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
+                                       int *tp_index,
+                                       Jpeg2000CodingStyle *codsty,
+                                       Jpeg2000ResLevel *rlevel, int precno,
+                                       int layno, uint8_t *expn, int numgbits)
+{
+    int bandno, cblkno, nb_code_blocks;
+    int cwsno;
+
+    s->g = tile->tile_part[*tp_index].tpg;
+    if (tile->has_ppt) {
+        if (bytestream2_get_bytes_left(&s->g) == 0 && s->bit_index == 8) {
+            if (*tp_index < FF_ARRAY_ELEMS(tile->tile_part) - 1) {
+                s->g = tile->tile_part[++(*tp_index)].tpg;
+            }
+        }
+        if (bytestream2_peek_be32(&s->g) == JPEG2000_SOP_FIXED_BYTES)
+            bytestream2_skip(&s->g, JPEG2000_SOP_BYTE_LENGTH);
+    }
+
+
+    for (bandno = 0; bandno < rlevel->nbands; bandno++) {
+        Jpeg2000Band *band = rlevel->band + bandno;
+        Jpeg2000Prec *prec = band->prec + precno;
+
+        nb_code_blocks = prec->nb_codeblocks_height * prec->nb_codeblocks_width;
+        for (cblkno = 0; cblkno < nb_code_blocks; cblkno++) {
+            Jpeg2000Cblk *cblk = prec->cblk + cblkno;
+            if (!cblk->nb_terminationsinc && !cblk->lengthinc)
+                continue;
+            for (cwsno = 0; cwsno < cblk->nb_lengthinc; cwsno ++) {
+                if (cblk->data_allocated < cblk->length + cblk->lengthinc[cwsno] + 4) {
+                    size_t new_size = FFMAX(2*cblk->data_allocated, cblk->length + cblk->lengthinc[cwsno] + 4);
+                    void *new = av_realloc(cblk->data, new_size);
+                    if (new) {
+                        cblk->data = new;
+                        cblk->data_allocated = new_size;
+                    }
+                }
+                if (   bytestream2_get_bytes_left(&s->g) < cblk->lengthinc[cwsno]
+                    || cblk->data_allocated < cblk->length + cblk->lengthinc[cwsno] + 4
+                ) {
+                    av_log(s->avctx, AV_LOG_ERROR,
+                        "Block length %"PRIu16" or lengthinc %d is too large, left %d\n",
+                        cblk->length, cblk->lengthinc[cwsno], bytestream2_get_bytes_left(&s->g));
+                    return AVERROR_INVALIDDATA;
+                }
+
+                bytestream2_get_bufferu(&s->g, cblk->data + cblk->length, cblk->lengthinc[cwsno]);
+                cblk->length   += cblk->lengthinc[cwsno];
+                cblk->lengthinc[cwsno] = 0;
+                if (cblk->nb_terminationsinc) {
+                    cblk->nb_terminationsinc--;
+                    cblk->nb_terminations++;
+                    cblk->data[cblk->length++] = 0xFF;
+                    cblk->data[cblk->length++] = 0xFF;
+                    cblk->data_start[cblk->nb_terminations] = cblk->length;
+                }
+            }
+            av_freep(&cblk->lengthinc);
+        }
+    }
+    tile->tile_part[*tp_index].tpg = s->g;
+    return 0;
+}
+
 static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile, int *tp_index,
                                   Jpeg2000CodingStyle *codsty,
                                   Jpeg2000ResLevel *rlevel, int precno,
@@ -1120,13 +1366,25 @@  static int jpeg2000_decode_packets_po_iteration(Jpeg2000DecoderContext *s, Jpeg2
                         Jpeg2000ResLevel *rlevel = tile->comp[compno].reslevel +
                                                 reslevelno;
                         ok_reslevel = 1;
-                        for (precno = 0; precno < rlevel->num_precincts_x * rlevel->num_precincts_y; precno++)
-                            if ((ret = jpeg2000_decode_packet(s, tile, tp_index,
+                        for (precno = 0; precno < rlevel->num_precincts_x * rlevel->num_precincts_y; precno++) {
+                            int process_data = 1;
+                            if ((ret = jpeg2000_decode_packet_header(s, tile, tp_index,
+                                                              codsty, rlevel,
+                                                              precno, layno,
+                                                              qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
+                                                              qntsty->nguardbits, &process_data)) < 0)
+                                return ret;
+
+                            if (!process_data)
+                                continue;
+
+                            if ((ret = jpeg2000_decode_packet_data(s, tile, tp_index,
                                                               codsty, rlevel,
                                                               precno, layno,
                                                               qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
                                                               qntsty->nguardbits)) < 0)
                                 return ret;
+                        }
                     }
                 }
             }
@@ -1146,13 +1404,25 @@  static int jpeg2000_decode_packets_po_iteration(Jpeg2000DecoderContext *s, Jpeg2
                         Jpeg2000ResLevel *rlevel = tile->comp[compno].reslevel +
                                                 reslevelno;
                         ok_reslevel = 1;
-                        for (precno = 0; precno < rlevel->num_precincts_x * rlevel->num_precincts_y; precno++)
-                            if ((ret = jpeg2000_decode_packet(s, tile, tp_index,
+                        for (precno = 0; precno < rlevel->num_precincts_x * rlevel->num_precincts_y; precno++) {
+                            int process_data = 1;
+                            if ((ret = jpeg2000_decode_packet_header(s, tile, tp_index,
+                                                              codsty, rlevel,
+                                                              precno, layno,
+                                                              qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
+                                                              qntsty->nguardbits, &process_data)) < 0)
+                                return ret;
+
+                            if (!process_data)
+                                continue;
+
+                            if ((ret = jpeg2000_decode_packet_data(s, tile, tp_index,
                                                               codsty, rlevel,
                                                               precno, layno,
                                                               qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
                                                               qntsty->nguardbits)) < 0)
                                 return ret;
+                        }
                     }
                 }
             }
@@ -1214,7 +1484,19 @@  static int jpeg2000_decode_packets_po_iteration(Jpeg2000DecoderContext *s, Jpeg2
                         }
 
                         for (layno = 0; layno < LYEpoc; layno++) {
-                            if ((ret = jpeg2000_decode_packet(s, tile, tp_index, codsty, rlevel,
+                            int process_data = 1;
+                            if ((ret = jpeg2000_decode_packet_header(s, tile, tp_index,
+                                                              codsty, rlevel,
+                                                              precno, layno,
+                                                              qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
+                                                              qntsty->nguardbits, &process_data)) < 0)
+                                return ret;
+
+                            if (!process_data)
+                                continue;
+
+                            if ((ret = jpeg2000_decode_packet_data(s, tile, tp_index,
+                                                              codsty, rlevel,
                                                               precno, layno,
                                                               qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
                                                               qntsty->nguardbits)) < 0)
@@ -1284,14 +1566,25 @@  static int jpeg2000_decode_packets_po_iteration(Jpeg2000DecoderContext *s, Jpeg2
                             continue;
                         }
 
-                            for (layno = 0; layno < LYEpoc; layno++) {
-                                if ((ret = jpeg2000_decode_packet(s, tile, tp_index,
-                                                                codsty, rlevel,
-                                                                precno, layno,
-                                                                qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
-                                                                qntsty->nguardbits)) < 0)
-                                    return ret;
-                            }
+                        for (layno = 0; layno < LYEpoc; layno++) {
+                            int process_data = 1;
+                            if ((ret = jpeg2000_decode_packet_header(s, tile, tp_index,
+                                                              codsty, rlevel,
+                                                              precno, layno,
+                                                              qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
+                                                              qntsty->nguardbits, &process_data)) < 0)
+                                return ret;
+
+                            if (!process_data)
+                                continue;
+
+                            if ((ret = jpeg2000_decode_packet_data(s, tile, tp_index,
+                                                              codsty, rlevel,
+                                                              precno, layno,
+                                                              qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
+                                                              qntsty->nguardbits)) < 0)
+                                return ret;
+                        }
                     }
                 }
             }
@@ -1355,7 +1648,19 @@  static int jpeg2000_decode_packets_po_iteration(Jpeg2000DecoderContext *s, Jpeg2
                         }
 
                         for (layno = 0; layno < LYEpoc; layno++) {
-                            if ((ret = jpeg2000_decode_packet(s, tile, tp_index, codsty, rlevel,
+                            int process_data = 1;
+                            if ((ret = jpeg2000_decode_packet_header(s, tile, tp_index,
+                                                              codsty, rlevel,
+                                                              precno, layno,
+                                                              qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
+                                                              qntsty->nguardbits, &process_data)) < 0)
+                                return ret;
+
+                            if (!process_data)
+                                continue;
+
+                            if ((ret = jpeg2000_decode_packet_data(s, tile, tp_index,
+                                                              codsty, rlevel,
                                                               precno, layno,
                                                               qntsty->expn + (reslevelno ? 3 * (reslevelno - 1) + 1 : 0),
                                                               qntsty->nguardbits)) < 0)
@@ -1381,6 +1686,7 @@  static int jpeg2000_decode_packets(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile
     int tp_index = 0;
 
     s->bit_index = 8;
+
     if (tile->poc.nb_poc) {
         for (i=0; i<tile->poc.nb_poc; i++) {
             Jpeg2000POCEntry *e = &tile->poc.poc[i];
@@ -1925,6 +2231,11 @@  static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s)
                 av_log(s->avctx, AV_LOG_ERROR, "Invalid tpend\n");
                 return AVERROR_INVALIDDATA;
             }
+
+            if (tile->has_ppt && tile->tp_idx == 0) {
+                bytestream2_init(&tile->packed_headers_stream, tile->packed_headers, tile->packed_headers_size);
+            }
+
             bytestream2_init(&tp->tpg, s->g.buffer, tp->tp_end - s->g.buffer);
             bytestream2_skip(&s->g, tp->tp_end - s->g.buffer);
 
@@ -1987,6 +2298,10 @@  static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s)
             // Packet length, tile-part header
             ret = get_plt(s, len);
             break;
+        case JPEG2000_PPT:
+            // Packed headers, tile-part header
+            ret = get_ppt(s, len);
+            break;
         default:
             av_log(s->avctx, AV_LOG_ERROR,
                    "unsupported marker 0x%.4"PRIX16" at pos 0x%X\n",
@@ -2016,7 +2331,6 @@  static int jpeg2000_read_bitstream_packets(Jpeg2000DecoderContext *s)
         if ((ret = init_tile(s, tileno)) < 0)
             return ret;
 
-        s->g = tile->tile_part[0].tpg;
         if ((ret = jpeg2000_decode_packets(s, tile)) < 0)
             return ret;
     }