diff mbox series

[FFmpeg-devel,v3,2/3] avcodec/jpeg2000dec: Add support for placeholder passes

Message ID 20240624061947.1354109-2-owatanab@es.takushoku-u.ac.jp
State New
Headers show
Series [FFmpeg-devel,v3,1/3] avcodec/jpeg2000dec: Add support for CAP and CPF markers | expand

Checks

Context Check Description
yinshiyou/make_loongarch64 fail Make failed
andriy/make_x86 fail Make failed

Commit Message

Osamu Watanabe June 24, 2024, 6:19 a.m. UTC
This commit adds support for placeholder pass parsing

Signed-off-by: Osamu Watanabe <owatanab@es.takushoku-u.ac.jp>
---
 libavcodec/jpeg2000.h      |   2 +
 libavcodec/jpeg2000dec.c   | 292 +++++++++++++++++++++++++++++++------
 libavcodec/jpeg2000htdec.c |  18 +--
 3 files changed, 257 insertions(+), 55 deletions(-)

Comments

Andreas Rheinhardt June 24, 2024, 9:34 a.m. UTC | #1
Osamu Watanabe:
> This commit adds support for placeholder pass parsing
> 

What is a placeholder pass?

> Signed-off-by: Osamu Watanabe <owatanab@es.takushoku-u.ac.jp>
> ---
>  libavcodec/jpeg2000.h      |   2 +
>  libavcodec/jpeg2000dec.c   | 292 +++++++++++++++++++++++++++++++------
>  libavcodec/jpeg2000htdec.c |  18 +--
>  3 files changed, 257 insertions(+), 55 deletions(-)
>
Osamu Watanabe June 24, 2024, 10:11 a.m. UTC | #2
Placeholder pass is a coding pass having zero length. It is necessary to keep pass boundaries of layers for the transcoding from HT to non HT codestream. It is defined in the spec of HTJ2K.

A detaled explanation is available at https://ds.jpeg.org/documents/jpeg2000/wg1n100680-101-COM-Guideline_on_Placeholder_Passes_and_Multiple_HT_Sets_in_HTJ2K_codestreams.zip


> 2024/06/24 18:34、Andreas Rheinhardt <andreas.rheinhardt@outlook.com>のメール:
> 
> ?Osamu Watanabe:
>> This commit adds support for placeholder pass parsing
>> 
> 
> What is a placeholder pass?
> 
>> Signed-off-by: Osamu Watanabe <owatanab@es.takushoku-u.ac.jp>
>> ---
>> libavcodec/jpeg2000.h      |   2 +
>> libavcodec/jpeg2000dec.c   | 292 +++++++++++++++++++++++++++++++------
>> libavcodec/jpeg2000htdec.c |  18 +--
>> 3 files changed, 257 insertions(+), 55 deletions(-)
>> 
> 
> _______________________________________________
> 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/jpeg2000.h b/libavcodec/jpeg2000.h
index 4bdc38df7c..93221d90ca 100644
--- a/libavcodec/jpeg2000.h
+++ b/libavcodec/jpeg2000.h
@@ -200,6 +200,8 @@  typedef struct Jpeg2000Cblk {
     /* specific to HT code-blocks */
     int zbp;
     int pass_lengths[2];
+    uint8_t modes; // copy of SPcod/SPcoc field to parse HT-MIXED mode
+    uint8_t ht_plhd; // are we looking for HT placeholder passes?
 } Jpeg2000Cblk; // code block
 
 typedef struct Jpeg2000Prec {
diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c
index d1046661c4..60756db05c 100644
--- a/libavcodec/jpeg2000dec.c
+++ b/libavcodec/jpeg2000dec.c
@@ -54,6 +54,15 @@ 
 #define HAD_COC 0x01
 #define HAD_QCC 0x02
 
+// Values of flag for placeholder passes
+enum HT_PLHD_STATUS {
+    HT_PLHD_OFF,
+    HT_PLHD_ON
+};
+
+#define HT_MIXED 0x80 // bit 7 of SPcod/SPcoc
+
+
 /* get_bits functions for JPEG2000 packet bitstream
  * It is a get_bit function with a bit-stuffing routine. If the value of the
  * byte is 0xFF, the next byte includes an extra zero bit stuffed into the MSB.
@@ -1160,30 +1169,44 @@  static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
             int incl, newpasses, llen;
             void *tmp;
 
-            if (cblk->npasses)
-                incl = get_bits(s, 1);
-            else
+            if (!cblk->incl) {
+                incl = 0;
+                cblk->modes = codsty->cblk_style;
+                if (cblk->modes >= JPEG2000_CTSY_HTJ2K_F)
+                    cblk->ht_plhd = HT_PLHD_ON;
+                if (layno > 0)
+                    incl = tag_tree_decode(s, prec->cblkincl + cblkno, 0 + 1) == 0;
                 incl = tag_tree_decode(s, prec->cblkincl + cblkno, layno + 1) == layno;
-            if (!incl)
-                continue;
-            else if (incl < 0)
-                return incl;
 
-            if (!cblk->npasses) {
+                if (incl) {
                 int zbp = tag_tree_decode(s, prec->zerobits + cblkno, 100);
-                int v = expn[bandno] + numgbits - 1 - zbp;
-
+                    int v = expn[bandno] + numgbits - 1 - (zbp - tile->comp->roi_shift);
                 if (v < 0 || v > 30) {
                     av_log(s->avctx, AV_LOG_ERROR,
                            "nonzerobits %d invalid or unsupported\n", v);
                     return AVERROR_INVALIDDATA;
                 }
-                cblk->zbp = zbp;
+                    cblk->incl = 1;
                 cblk->nonzerobits = v;
+                    cblk->zbp = zbp;
+                    cblk->lblock = 3;
+                }
+            } else {
+                incl = get_bits(s, 1);
             }
-            if ((newpasses = getnpasses(s)) < 0)
+
+            if (incl) {
+                uint8_t bypass_term_threshold = 0;
+                uint8_t bits_to_read = 0;
+                uint32_t segment_bytes = 0;
+                int32_t segment_passes = 0;
+                uint8_t next_segment_passes = 0;
+                int32_t href_passes, pass_bound;
+                uint32_t tmp_length = 0;
+                int32_t newpasses_copy, npasses_copy;
+
+                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;
@@ -1195,65 +1218,244 @@  static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
                                       "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_calloc(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));
+                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;
+                cblk->lblock += llen;
+
+                // Count number of necessary terminations for non HT code block
+                newpasses_copy = newpasses;
+                npasses_copy = cblk->npasses;
+                if (!(cblk->modes & JPEG2000_CTSY_HTJ2K_F)) {
             do {
                 int newpasses1 = 0;
 
-                while (newpasses1 < newpasses) {
-                    newpasses1 ++;
-                    if (needs_termination(codsty->cblk_style, cblk->npasses + newpasses1 - 1)) {
-                        cblk->nb_terminationsinc ++;
+                        while (newpasses1 < newpasses_copy) {
+                            newpasses1++;
+                            if (needs_termination(codsty->cblk_style, npasses_copy + newpasses1 - 1)) {
+                                cblk->nb_terminationsinc++;
                         break;
                     }
                 }
+                        npasses_copy += newpasses1;
+                        newpasses_copy -= newpasses1;
+                    } while (newpasses_copy);
+                }
 
-                if (newpasses > 1 && (codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F)) {
-                    // Retrieve pass lengths for each pass
-                    int href_passes =  (cblk->npasses + newpasses - 1) % 3;
-                    int eb = av_log2(newpasses - href_passes);
-                    int extra_bit = newpasses > 2 ? 1 : 0;
-                    if ((ret = get_bits(s, llen + eb + 3)) < 0)
-                        return ret;
-                    cblk->pass_lengths[0] = ret;
-                    if ((ret = get_bits(s, llen + 3 + extra_bit)) < 0)
-                        return ret;
-                    cblk->pass_lengths[1] = ret;
-                    ret = cblk->pass_lengths[0] + cblk->pass_lengths[1];
+                if (cblk->ht_plhd) {
+                    href_passes = (cblk->npasses + newpasses - 1) % 3;
+                    segment_passes = newpasses - href_passes;
+                    pass_bound = 2;
+                    bits_to_read = cblk->lblock;
+                    if (segment_passes < 1) {
+                        // No possible HT Cleanup pass here; may have placeholder passes
+                        // or an original J2K block bit-stream (in MIXED mode).
+                        segment_passes = newpasses;
+                        while (pass_bound <= segment_passes) {
+                            bits_to_read++;
+                            pass_bound += pass_bound;
+                        }
+                        segment_bytes = get_bits(s, bits_to_read);
+                        if (segment_bytes) {
+                            if (cblk->modes & HT_MIXED) {
+                                cblk->ht_plhd = HT_PLHD_OFF;
+                                cblk->modes &= (uint8_t) (~(JPEG2000_CTSY_HTJ2K_F));
+                            }
+                            else {
+                                av_log(s->avctx, AV_LOG_WARNING, "Length information for a HT-codeblock is invalid\n");
+                            }
+                        }
                 } else {
-                    if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0)
-                        return ret;
-                    cblk->pass_lengths[0] = ret;
+                        while (pass_bound <= segment_passes) {
+                            bits_to_read++;
+                            pass_bound += pass_bound;
+                        }
+                        segment_bytes = get_bits(s, bits_to_read);
+                        if (segment_bytes) {
+                            // No more placeholder passes
+                            if (!(cblk->modes & HT_MIXED)) {
+                                // Must be the first HT Cleanup pass
+                                if (segment_bytes < 2)
+                                    av_log(s->avctx, AV_LOG_WARNING, "Length information for a HT-codeblock is invalid\n");
+                                next_segment_passes = 2;
+                                cblk->ht_plhd = HT_PLHD_OFF;
+                                // Write length information for HT CleanUp segment
+                                cblk->pass_lengths[0] = segment_bytes;
+                            } else if (cblk->lblock > 3 && segment_bytes > 1
+                                       && (segment_bytes >> (bits_to_read - 1)) == 0) {
+                                // Must be the first HT Cleanup pass, since length MSB is 0
+                                next_segment_passes = 2;
+                                cblk->ht_plhd = HT_PLHD_OFF;
+                                // Write length information for HT CleanUp segment
+                                cblk->pass_lengths[0] = segment_bytes;
+                            } else {
+                                // Must have an original (non-HT) block coding pass
+                                cblk->modes &= (uint8_t) (~(JPEG2000_CTSY_HTJ2K_F));
+                                cblk->ht_plhd = HT_PLHD_OFF;
+                                segment_passes = newpasses;
+                                while (pass_bound <= segment_passes) {
+                                    bits_to_read++;
+                                    pass_bound += pass_bound;
+                                    segment_bytes <<= 1;
+                                    segment_bytes += get_bits(s, 1);
+                                }
+                            }
+                        } else {
+                            // Probably parsing placeholder passes, but we need to read an
+                            // extra length bit to verify this, since prior to the first
+                            // HT Cleanup pass, the number of length bits read for a
+                            // contributing code-block is dependent on the number of passes
+                            // being included, as if it were a non-HT code-block.
+                            segment_passes = newpasses;
+                            if (pass_bound <= segment_passes) {
+                                while (1) {
+                                    bits_to_read++;
+                                    pass_bound += pass_bound;
+                                    segment_bytes <<= 1;
+                                    segment_bytes += get_bits(s, 1);
+                                    if (pass_bound > segment_passes)
+                                        break;
+                                }
+                                if (segment_bytes) {
+                                    if (cblk->modes & HT_MIXED) {
+                                        cblk->modes &= (uint8_t) (~(JPEG2000_CTSY_HTJ2K_F));
+                                        cblk->ht_plhd = HT_PLHD_OFF;
+                                    } else {
+                                        av_log(s->avctx, AV_LOG_WARNING, "Length information for a HT-codeblock is invalid\n");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } else if (cblk->modes & JPEG2000_CTSY_HTJ2K_F) {
+                    // Quality layer commences with a non-initial HT coding pass
+                    if(bits_to_read != 0)
+                        av_log(s->avctx, AV_LOG_WARNING, "Length information for a HT-codeblock is invalid\n");
+                    segment_passes = cblk->npasses % 3;
+                    if (segment_passes == 0) {
+                        // newpasses is a HT Cleanup pass; next segment has refinement passes
+                        segment_passes = 1;
+                        next_segment_passes = 2;
+                        if (segment_bytes == 1)
+                            av_log(s->avctx, AV_LOG_WARNING, "Length information for a HT-codeblock is invalid\n");
+                    } else {
+                        // newpasses = 1 means npasses is HT SigProp; 2 means newpasses is
+                        // HT MagRef pass
+                        segment_passes = newpasses > 1 ? 3 - segment_passes : 1;
+                        next_segment_passes = 1;
+                        bits_to_read = av_log2(segment_passes);
+                    }
+                    bits_to_read = (uint8_t) (bits_to_read + cblk->lblock);
+                    segment_bytes = get_bits(s, bits_to_read);
+                    // Write length information for HT Refinment segment
+                    cblk->pass_lengths[1] += segment_bytes;
+                } else if (!(cblk->modes & (JPEG2000_CBLK_TERMALL | JPEG2000_CBLK_BYPASS))) {
+                    // Common case for non-HT code-blocks; we have only one segment
+                    bits_to_read = (uint8_t) cblk->lblock + av_log2((uint8_t) newpasses);
+                    segment_bytes = get_bits(s, bits_to_read);
+                    segment_passes = newpasses;
+                } else if (cblk->modes & JPEG2000_CBLK_TERMALL) {
+                    // RESTART MODE
+                    bits_to_read = cblk->lblock;
+                    segment_bytes = get_bits(s, bits_to_read);
+                    segment_passes = 1;
+                    next_segment_passes = 1;
+                } else {
+                    // BYPASS MODE
+                    bypass_term_threshold = 10;
+                    if(bits_to_read != 0)
+                        av_log(s->avctx, AV_LOG_WARNING, "Length information for a codeblock is invalid\n");
+                    if (cblk->npasses < bypass_term_threshold) {
+                        // May have from 1 to 10 uninterrupted passes before 1st RAW SigProp
+                        segment_passes = bypass_term_threshold - cblk->npasses;
+                        if (segment_passes > newpasses)
+                            segment_passes = newpasses;
+                        while ((2 << bits_to_read) <= segment_passes)
+                            bits_to_read++;
+                        next_segment_passes = 2;
+                    } else if ((cblk->npasses - bypass_term_threshold) % 3 < 2) {
+                        // 0 means newpasses is a RAW SigProp; 1 means newpasses is a RAW MagRef pass
+                        segment_passes = newpasses > 1 ? 2 - (cblk->npasses - bypass_term_threshold) % 3 : 1;
+                        bits_to_read = av_log2(segment_passes);
+                        next_segment_passes = 1;
+                    } else {
+                        // newpasses is an isolated Cleanup pass that precedes a RAW SigProp pass
+                        segment_passes = 1;
+                        next_segment_passes = 2;
+                    }
+                    bits_to_read = (uint8_t) (bits_to_read + cblk->lblock);
+                    segment_bytes = get_bits(s, bits_to_read);
                 }
-                if (ret > cblk->data_allocated) {
-                    size_t new_size = FFMAX(2*cblk->data_allocated, ret);
+                // Update cblk->npasses and write length information
+                cblk->npasses = (uint8_t) (cblk->npasses + segment_passes);
+                cblk->lengthinc[cblk->nb_lengthinc++] = segment_bytes;
+
+                if ((cblk->modes & JPEG2000_CTSY_HTJ2K_F) && cblk->ht_plhd == HT_PLHD_OFF) {
+                    newpasses -= (uint8_t) segment_passes;
+                    while (newpasses > 0) {
+                        segment_passes = newpasses > 1 ? next_segment_passes : 1;
+                        next_segment_passes = (uint8_t) (3 - next_segment_passes);
+                        bits_to_read = (uint8_t) (cblk->lblock + av_log2(segment_passes));
+                        segment_bytes = get_bits(s, bits_to_read);
+                        newpasses -= (uint8_t) (segment_passes);
+                        // This is a FAST Refinement pass
+                        // Write length information for HT Refinement segment
+                        cblk->pass_lengths[1] += segment_bytes;
+                        // Update cblk->npasses and write length information
+                        cblk->npasses = (uint8_t) (cblk->npasses + segment_passes);
+                        cblk->lengthinc[cblk->nb_lengthinc++] = segment_bytes;
+                    }
+                } else {
+                    newpasses -= (uint8_t) (segment_passes);
+                    while (newpasses > 0) {
+                        if (bypass_term_threshold != 0) {
+                            segment_passes = newpasses > 1 ? next_segment_passes : 1;
+                            next_segment_passes = (uint8_t) (3 - next_segment_passes);
+                            bits_to_read = (uint8_t) (cblk->lblock + av_log2(segment_passes));
+                        } else {
+                            if ((cblk->modes & JPEG2000_CBLK_TERMALL) == 0)
+                                av_log(s->avctx, AV_LOG_WARNING, "Corrupted packet header is found.\n");
+                            segment_passes = 1;
+                            bits_to_read = cblk->lblock;
+                        }
+                        segment_bytes = get_bits(s, bits_to_read);
+                        newpasses -= (uint8_t) (segment_passes);
+
+                        // Update cblk->npasses and write length information
+                        cblk->npasses = (uint8_t) (cblk->npasses + segment_passes);
+                        cblk->lengthinc[cblk->nb_lengthinc++] = segment_bytes;
+                    }
+                }
+
+                for (int i = 0; i < cblk->nb_lengthinc; ++i)
+                    tmp_length = (tmp_length < cblk->lengthinc[i]) ? cblk->lengthinc[i] : tmp_length;
+
+                if (tmp_length > cblk->data_allocated) {
+                    size_t new_size = FFMAX(2 * cblk->data_allocated, tmp_length);
                     void *new = av_realloc(cblk->data, new_size);
                     if (new) {
                         cblk->data = new;
                         cblk->data_allocated = new_size;
                     }
                 }
-                if (ret > cblk->data_allocated) {
+                if (tmp_length > 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);
+            } else {
+                // This codeblock has no contribution to the current packet
+                continue;
+            }
         }
     }
     jpeg2000_flush(s);
@@ -1788,7 +1990,7 @@  static int decode_cblk(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *cod
                        Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk,
                        int width, int height, int bandpos, uint8_t roi_shift)
 {
-    int passno = cblk->npasses, pass_t = 2, bpno = cblk->nonzerobits - 1 + roi_shift;
+    int passno = cblk->npasses, pass_t = 2, bpno = cblk->nonzerobits - 1;
     int pass_cnt = 0;
     int vert_causal_ctx_csty_symbol = codsty->cblk_style & JPEG2000_CBLK_VSC;
     int term_cnt = 0;
@@ -2028,7 +2230,7 @@  static inline int tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile
 
                         Jpeg2000Cblk *cblk = prec->cblk + cblkno;
 
-                        if (codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F)
+                        if (cblk->modes & JPEG2000_CTSY_HTJ2K_F)
                             ret = ff_jpeg2000_decode_htj2k(s, codsty, &t1, cblk,
                                                            cblk->coord[0][1] - cblk->coord[0][0],
                                                            cblk->coord[1][1] - cblk->coord[1][0],
diff --git a/libavcodec/jpeg2000htdec.c b/libavcodec/jpeg2000htdec.c
index eba0936089..d5bebe127b 100644
--- a/libavcodec/jpeg2000htdec.c
+++ b/libavcodec/jpeg2000htdec.c
@@ -1160,7 +1160,7 @@  int
 ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk,
                          int width, int height, int magp, uint8_t roi_shift)
 {
-    uint8_t p0 = 0;             // Number of placeholder passes
+    uint8_t p0 = 0;             // 3 * p0 = Number of placeholder passes
     uint32_t Lcup;              // Length of HT cleanup segment
     uint32_t Lref;              // Length of Refinement segment
     uint32_t Scup;              // HT cleanup segment suffix length
@@ -1174,7 +1174,7 @@  ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
 
     int z_blk;                  // Number of ht coding pass
 
-    uint8_t empty_passes;
+    uint8_t num_plhd_passes;    // Number of placeholder passes
 
     StateVars mag_sgn;          // Magnitude and Sign
     StateVars mel;              // Adaptive run-length coding
@@ -1191,7 +1191,7 @@  ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
 
     int32_t n, val;             // Post-processing
 
-    int32_t M_b = magp;
+    uint8_t num_rempass;
 
     const int quad_buf_width = width + 4;
     const int quad_buf_height = height + 4;
@@ -1210,13 +1210,11 @@  ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
     if (cblk->npasses == 0)
         return 0;
 
-    if (cblk->npasses > 3)
-        p0 = 0;
-    else if (cblk->length == 0)
-        p0 = 1;
-
-    empty_passes = p0 * 3;
-    z_blk = cblk->npasses - empty_passes;
+    num_rempass = cblk->npasses % 3;  // Number of remainder passes
+    num_plhd_passes = num_rempass ? cblk->npasses - num_rempass : cblk->npasses - 3;
+    av_assert0(num_plhd_passes % 3 == 0);
+    p0 = num_plhd_passes / 3;
+    z_blk = cblk->npasses - num_plhd_passes;
 
     if (z_blk <= 0)
         return 0; // No passes within this set, continue