* [FFmpeg-devel] [PATCH v3 1/3] avcodec/jpeg2000dec: Add support for CAP and CPF markers
@ 2024-06-24 6:19 Osamu Watanabe
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes Osamu Watanabe
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 3/3] avcodec/jpeg2000dec: Fix HT decoding Osamu Watanabe
0 siblings, 2 replies; 5+ messages in thread
From: Osamu Watanabe @ 2024-06-24 6:19 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Osamu Watanabe, pal
This commit adds suuport for CAP and CPF markers
Signed-off-by: Osamu Watanabe <owatanab@es.takushoku-u.ac.jp>
---
libavcodec/jpeg2000.h | 8 +++
libavcodec/jpeg2000dec.c | 112 ++++++++++++++++++++++++++++++++++++++-
libavcodec/jpeg2000dec.h | 7 +++
3 files changed, 126 insertions(+), 1 deletion(-)
diff --git a/libavcodec/jpeg2000.h b/libavcodec/jpeg2000.h
index d004c08f10..4bdc38df7c 100644
--- a/libavcodec/jpeg2000.h
+++ b/libavcodec/jpeg2000.h
@@ -37,12 +37,14 @@
enum Jpeg2000Markers {
JPEG2000_SOC = 0xff4f, // start of codestream
+ JPEG2000_CAP = 0xff50, // extended capabilities
JPEG2000_SIZ = 0xff51, // image and tile size
JPEG2000_COD, // coding style default
JPEG2000_COC, // coding style component
JPEG2000_TLM = 0xff55, // tile-part length, main header
JPEG2000_PLM = 0xff57, // packet length, main header
JPEG2000_PLT, // packet length, tile-part header
+ JPEG2000_CPF, // corresponding profile
JPEG2000_QCD = 0xff5c, // quantization default
JPEG2000_QCC, // quantization component
JPEG2000_RGN, // region of interest
@@ -58,6 +60,12 @@ enum Jpeg2000Markers {
JPEG2000_EOC = 0xffd9, // end of codestream
};
+enum JPEG2000_Ccap15_b14_15_params {
+ HTJ2K_HTONLY = 0, // HTONLY, bit 14 and 15 are 0
+ HTJ2K_HTDECLARED, // HTDECLARED, bit 14 = 1 and bit 15 = 0
+ HTJ2K_MIXED = 3, // MIXED, bit 14 and 15 are 1
+};
+
#define JPEG2000_SOP_FIXED_BYTES 0xFF910004
#define JPEG2000_SOP_BYTE_LENGTH 6
diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c
index 091931b1ff..d1046661c4 100644
--- a/libavcodec/jpeg2000dec.c
+++ b/libavcodec/jpeg2000dec.c
@@ -408,6 +408,73 @@ static int get_siz(Jpeg2000DecoderContext *s)
s->avctx->bits_per_raw_sample = s->precision;
return 0;
}
+/* get extended capabilities (CAP) marker segment */
+static int get_cap(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c)
+{
+ uint32_t Pcap;
+ uint16_t Ccap_i[32] = { 0 };
+ uint16_t Ccap_15;
+ uint8_t P;
+
+ if (bytestream2_get_bytes_left(&s->g) < 6) {
+ av_log(s->avctx, AV_LOG_ERROR, "Insufficient space for CAP\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ Pcap = bytestream2_get_be32u(&s->g);
+ s->isHT = (Pcap >> (31 - (15 - 1))) & 1;
+ for (int i = 0; i < 32; i++) {
+ if ((Pcap >> (31 - i)) & 1)
+ Ccap_i[i] = bytestream2_get_be16u(&s->g);
+ }
+ Ccap_15 = Ccap_i[14];
+ if (s->isHT == 1) {
+ av_log(s->avctx, AV_LOG_INFO, "This is an HTJ2K codestream.\n");
+ // Bits 14-15
+ switch ((Ccap_15 >> 14) & 0x3) {
+ case 0x3:
+ s->Ccap15_b14_15 = HTJ2K_MIXED;
+ break;
+ case 0x1:
+ s->Ccap15_b14_15 = HTJ2K_HTDECLARED;
+ break;
+ case 0x0:
+ s->Ccap15_b14_15 = HTJ2K_HTONLY;
+ break;
+ default:
+ av_log(s->avctx, AV_LOG_ERROR, "Unknown CCap value.\n");
+ return AVERROR(EINVAL);
+ break;
+ }
+ // Bit 13
+ if ((Ccap_15 >> 13) & 1) {
+ av_log(s->avctx, AV_LOG_ERROR, "MULTIHT set is not supported.\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ // Bit 12
+ s->Ccap15_b12 = (Ccap_15 >> 12) & 1;
+ // Bit 11
+ s->Ccap15_b11 = (Ccap_15 >> 11) & 1;
+ // Bit 5
+ s->Ccap15_b05 = (Ccap_15 >> 5) & 1;
+ // Bit 0-4
+ P = Ccap_15 & 0x1F;
+ if (!P)
+ s->HT_MAGB = 8;
+ else if (P < 20)
+ s->HT_MAGB = P + 8;
+ else if (P < 31)
+ s->HT_MAGB = 4 * (P - 19) + 27;
+ else
+ s->HT_MAGB = 74;
+
+ if (s->HT_MAGB > 31) {
+ av_log(s->avctx, AV_LOG_ERROR, "Available internal precision is exceeded (MAGB> 31).\n");
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+ return 0;
+}
/* get common part for COD and COC segments */
static int get_cox(Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c)
@@ -802,6 +869,15 @@ static int read_crg(Jpeg2000DecoderContext *s, int n)
bytestream2_skip(&s->g, n - 2);
return 0;
}
+
+static int read_cpf(Jpeg2000DecoderContext *s, int n)
+{
+ if (bytestream2_get_bytes_left(&s->g) < (n - 2))
+ return AVERROR_INVALIDDATA;
+ bytestream2_skip(&s->g, n - 2);
+ return 0;
+}
+
/* Tile-part lengths: see ISO 15444-1:2002, section A.7.1
* Used to know the number of tile parts and lengths.
* There may be multiple TLMs in the header.
@@ -965,6 +1041,14 @@ static int init_tile(Jpeg2000DecoderContext *s, int tileno)
comp->roi_shift = s->roi_shift[compno];
if (!codsty->init)
return AVERROR_INVALIDDATA;
+ if (s->isHT && (!s->Ccap15_b05) && (!codsty->transform)) {
+ av_log(s->avctx, AV_LOG_ERROR, "Transformation = 0 (lossy DWT) is found in HTREV HT set\n");
+ return AVERROR_INVALIDDATA;
+ }
+ if (s->isHT && s->Ccap15_b14_15 != (codsty->cblk_style >> 6) && s->Ccap15_b14_15 != HTJ2K_HTONLY) {
+ av_log(s->avctx, AV_LOG_ERROR, "SPcod/SPcoc value does not match bit 14-15 values of Ccap15\n");
+ return AVERROR_INVALIDDATA;
+ }
if (ret = ff_jpeg2000_init_component(comp, codsty, qntsty,
s->cbps[compno], s->cdx[compno],
s->cdy[compno], s->avctx))
@@ -2187,22 +2271,43 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s)
if (!s->tile)
s->numXtiles = s->numYtiles = 0;
break;
+ case JPEG2000_CAP:
+ if (!s->ncomponents) {
+ av_log(s->avctx, AV_LOG_ERROR, "CAP marker segment shall come after SIZ\n");
+ return AVERROR_INVALIDDATA;
+ }
+ ret = get_cap(s, codsty);
+ break;
case JPEG2000_COC:
+ if (s->in_tile_headers == 1 && s->isHT && (!s->Ccap15_b11))
+ av_log(s->avctx, AV_LOG_WARNING, "COC marker is found in HOMOGENEOUS HT set\n");
ret = get_coc(s, codsty, properties);
break;
case JPEG2000_COD:
+ if (s->in_tile_headers == 1 && s->isHT && (!s->Ccap15_b11))
+ av_log(s->avctx, AV_LOG_WARNING, "COD marker is found in HOMOGENEOUS HT set\n");
ret = get_cod(s, codsty, properties);
break;
case JPEG2000_RGN:
+ if (s->in_tile_headers == 1 && s->isHT && (!s->Ccap15_b11))
+ av_log(s->avctx, AV_LOG_WARNING, "RGB marker is found in HOMOGENEOUS HT set\n");
ret = get_rgn(s, len);
+ if ((!s->Ccap15_b12) && s->isHT)
+ av_log(s->avctx, AV_LOG_WARNING, "RGN marker is found in RGNFREE HT set\n");
break;
case JPEG2000_QCC:
+ if (s->in_tile_headers == 1 && s->isHT && (!s->Ccap15_b11))
+ av_log(s->avctx, AV_LOG_WARNING, "QCC marker is found in HOMOGENEOUS HT set\n");
ret = get_qcc(s, len, qntsty, properties);
break;
case JPEG2000_QCD:
+ if (s->in_tile_headers == 1 && s->isHT && (!s->Ccap15_b11))
+ av_log(s->avctx, AV_LOG_WARNING, "QCD marker is found in HOMOGENEOUS HT set\n");
ret = get_qcd(s, len, qntsty, properties);
break;
case JPEG2000_POC:
+ if (s->in_tile_headers == 1 && s->isHT && (!s->Ccap15_b11))
+ av_log(s->avctx, AV_LOG_WARNING, "POC marker is found in HOMOGENEOUS HT set\n");
ret = get_poc(s, len, poc);
break;
case JPEG2000_SOT:
@@ -2252,9 +2357,14 @@ static int jpeg2000_read_main_headers(Jpeg2000DecoderContext *s)
"Cannot have both PPT and PPM marker.\n");
return AVERROR_INVALIDDATA;
}
-
+ if ((!s->Ccap15_b11) && s->isHT)
+ av_log(s->avctx, AV_LOG_WARNING, "PPT marker is found in HOMOGENEOUS HT set\n");
ret = get_ppt(s, len);
break;
+ case JPEG2000_CPF:
+ // Corresponding profile marker
+ ret = read_cpf(s, len);
+ break;
default:
av_log(s->avctx, AV_LOG_ERROR,
"unsupported marker 0x%.4"PRIX16" at pos 0x%X\n",
diff --git a/libavcodec/jpeg2000dec.h b/libavcodec/jpeg2000dec.h
index d0ca6e7a79..326a572722 100644
--- a/libavcodec/jpeg2000dec.h
+++ b/libavcodec/jpeg2000dec.h
@@ -112,6 +112,13 @@ typedef struct Jpeg2000DecoderContext {
Jpeg2000Tile *tile;
Jpeg2000DSPContext dsp;
+ uint8_t isHT; // HTJ2K?
+ uint8_t Ccap15_b14_15; // HTONLY(= 0) or HTDECLARED(= 1) or MIXED(= 3) ?
+ uint8_t Ccap15_b12; // RGNFREE(= 0) or RGN(= 1)?
+ uint8_t Ccap15_b11; // HOMOGENEOUS(= 0) or HETEROGENEOUS(= 1) ?
+ uint8_t Ccap15_b05; // HTREV(= 0) or HTIRV(= 1) ?
+ uint8_t HT_MAGB; // MAGB value
+
/*options parameters*/
int reduction_factor;
} Jpeg2000DecoderContext;
--
2.34.1
_______________________________________________
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".
^ permalink raw reply [flat|nested] 5+ messages in thread
* [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes
2024-06-24 6:19 [FFmpeg-devel] [PATCH v3 1/3] avcodec/jpeg2000dec: Add support for CAP and CPF markers Osamu Watanabe
@ 2024-06-24 6:19 ` Osamu Watanabe
2024-06-24 9:34 ` Andreas Rheinhardt
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 3/3] avcodec/jpeg2000dec: Fix HT decoding Osamu Watanabe
1 sibling, 1 reply; 5+ messages in thread
From: Osamu Watanabe @ 2024-06-24 6:19 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Osamu Watanabe, pal
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(-)
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
--
2.34.1
_______________________________________________
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".
^ permalink raw reply [flat|nested] 5+ messages in thread
* [FFmpeg-devel] [PATCH v3 3/3] avcodec/jpeg2000dec: Fix HT decoding
2024-06-24 6:19 [FFmpeg-devel] [PATCH v3 1/3] avcodec/jpeg2000dec: Add support for CAP and CPF markers Osamu Watanabe
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes Osamu Watanabe
@ 2024-06-24 6:19 ` Osamu Watanabe
1 sibling, 0 replies; 5+ messages in thread
From: Osamu Watanabe @ 2024-06-24 6:19 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: Osamu Watanabe, pal
This commit fixes wrong treatment of MAGBP value in Ccap15 and bugs in
HT block decoding.
Signed-off-by: Osamu Watanabe <owatanab@es.takushoku-u.ac.jp>
---
libavcodec/jpeg2000dec.c | 79 +++++++-------
libavcodec/jpeg2000htdec.c | 208 +++++++++++++++++++++----------------
libavcodec/jpeg2000htdec.h | 2 +-
3 files changed, 162 insertions(+), 127 deletions(-)
diff --git a/libavcodec/jpeg2000dec.c b/libavcodec/jpeg2000dec.c
index 60756db05c..83cd5dbc7c 100644
--- a/libavcodec/jpeg2000dec.c
+++ b/libavcodec/jpeg2000dec.c
@@ -391,6 +391,9 @@ static int get_siz(Jpeg2000DecoderContext *s)
} else if (ncomponents == 1 && s->precision == 8) {
s->avctx->pix_fmt = AV_PIX_FMT_GRAY8;
i = 0;
+ } else if (ncomponents == 1 && s->precision == 12) {
+ s->avctx->pix_fmt = AV_PIX_FMT_GRAY16LE;
+ i = 0;
}
}
@@ -1179,15 +1182,15 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
incl = tag_tree_decode(s, prec->cblkincl + cblkno, layno + 1) == layno;
if (incl) {
- int zbp = tag_tree_decode(s, prec->zerobits + cblkno, 100);
+ int zbp = tag_tree_decode(s, prec->zerobits + cblkno, 100);
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;
- }
+ if (v < 0 || v > 30) {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "nonzerobits %d invalid or unsupported\n", v);
+ return AVERROR_INVALIDDATA;
+ }
cblk->incl = 1;
- cblk->nonzerobits = v;
+ cblk->nonzerobits = v;
cblk->zbp = zbp;
cblk->lblock = 3;
}
@@ -1206,45 +1209,45 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
int32_t newpasses_copy, npasses_copy;
if ((newpasses = getnpasses(s)) <= 0)
- return newpasses;
- 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->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);
+ return newpasses;
+ 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->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));
- if (!tmp)
- return AVERROR(ENOMEM);
- cblk->data_start = tmp;
+ 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;
+ do {
+ int newpasses1 = 0;
while (newpasses1 < newpasses_copy) {
newpasses1++;
if (needs_termination(codsty->cblk_style, npasses_copy + newpasses1 - 1)) {
cblk->nb_terminationsinc++;
- break;
- }
- }
+ break;
+ }
+ }
npasses_copy += newpasses1;
newpasses_copy -= newpasses1;
} while (newpasses_copy);
@@ -1273,7 +1276,7 @@ static int jpeg2000_decode_packet(Jpeg2000DecoderContext *s, Jpeg2000Tile *tile,
av_log(s->avctx, AV_LOG_WARNING, "Length information for a HT-codeblock is invalid\n");
}
}
- } else {
+ } else {
while (pass_bound <= segment_passes) {
bits_to_read++;
pass_bound += pass_bound;
@@ -2204,7 +2207,7 @@ static inline int tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile
Jpeg2000Band *band = rlevel->band + bandno;
int cblkno = 0, bandpos;
/* See Rec. ITU-T T.800, Equation E-2 */
- int magp = quantsty->expn[subbandno] + quantsty->nguardbits - 1;
+ int M_b = quantsty->expn[subbandno] + quantsty->nguardbits - 1;
bandpos = bandno + (reslevelno > 0);
@@ -2212,8 +2215,8 @@ static inline int tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile
band->coord[1][0] == band->coord[1][1])
continue;
- if ((codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F) && magp >= 31) {
- avpriv_request_sample(s->avctx, "JPEG2000_CTSY_HTJ2K_F and magp >= 31");
+ if ((codsty->cblk_style & JPEG2000_CTSY_HTJ2K_F) && M_b >= 31) {
+ avpriv_request_sample(s->avctx, "JPEG2000_CTSY_HTJ2K_F and M_b >= 31");
return AVERROR_PATCHWELCOME;
}
@@ -2234,7 +2237,7 @@ static inline int tile_codeblocks(const Jpeg2000DecoderContext *s, Jpeg2000Tile
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],
- magp, comp->roi_shift);
+ M_b, comp->roi_shift);
else
ret = decode_cblk(s, codsty, &t1, cblk,
cblk->coord[0][1] - cblk->coord[0][0],
diff --git a/libavcodec/jpeg2000htdec.c b/libavcodec/jpeg2000htdec.c
index d5bebe127b..0296792a6a 100644
--- a/libavcodec/jpeg2000htdec.c
+++ b/libavcodec/jpeg2000htdec.c
@@ -122,7 +122,7 @@ static void jpeg2000_init_mel(StateVars *s, uint32_t Pcup)
static void jpeg2000_init_mag_ref(StateVars *s, uint32_t Lref)
{
- s->pos = Lref - 2;
+ s->pos = Lref - 1;
s->bits = 0;
s->last = 0xFF;
s->tmp = 0;
@@ -145,9 +145,10 @@ static void jpeg2000_init_mel_decoder(MelDecoderState *mel_state)
static int jpeg2000_bitbuf_refill_backwards(StateVars *buffer, const uint8_t *array)
{
uint64_t tmp = 0;
- int32_t position = buffer->pos - 4;
uint32_t new_bits = 32;
+ buffer->last = array[buffer->pos + 1];
+
if (buffer->bits_left >= 32)
return 0; // enough data, no need to pull in more bits
@@ -157,9 +158,24 @@ static int jpeg2000_bitbuf_refill_backwards(StateVars *buffer, const uint8_t *ar
* the bottom most bits.
*/
- for(int i = FFMAX(0, position + 1); i <= buffer->pos + 1; i++)
- tmp = 256*tmp + array[i];
-
+ if (buffer->pos >= 3) { // Common case; we have at least 4 bytes available
+ tmp = array[buffer->pos - 3];
+ tmp = (tmp << 8) | array[buffer->pos - 2];
+ tmp = (tmp << 8) | array[buffer->pos - 1];
+ tmp = (tmp << 8) | array[buffer->pos];
+ tmp = (tmp << 8) | buffer->last; // For stuffing bit detection
+ buffer->pos -= 4;
+ } else {
+ if (buffer->pos >= 2)
+ tmp = array[buffer->pos - 2];
+ if (buffer->pos >= 1)
+ tmp = (tmp << 8) | array[buffer->pos - 1];
+ if (buffer->pos >= 0)
+ tmp = (tmp << 8) | array[buffer->pos];
+ buffer->pos = 0;
+ tmp = (tmp << 8) | buffer->last; // For stuffing bit detection
+ }
+ // Now remove any stuffing bits, shifting things down as we go
if ((tmp & 0x7FFF000000) > 0x7F8F000000) {
tmp &= 0x7FFFFFFFFF;
new_bits--;
@@ -176,13 +192,11 @@ static int jpeg2000_bitbuf_refill_backwards(StateVars *buffer, const uint8_t *ar
tmp = (tmp & 0x0000007FFF) + ((tmp & 0xFFFFFF0000) >> 1);
new_bits--;
}
-
- tmp >>= 8; // Remove temporary byte loaded
+ tmp >>= 8; // Shifts away the extra byte we imported
/* Add bits to the MSB of the bit buffer */
buffer->bit_buf |= tmp << buffer->bits_left;
buffer->bits_left += new_bits;
- buffer->pos = FFMAX(0, position);
return 0;
}
@@ -406,6 +420,7 @@ static void recover_mag_sgn(StateVars *mag_sgn, uint8_t pos, uint16_t q, int32_t
E[n] = 32 - ff_clz(v[pos][i] | 1);
mu_n[n] = (v[pos][i] >> 1) + 1;
mu_n[n] <<= pLSB;
+ mu_n[n] |= (1 << (pLSB - 1)); // Add 0.5 (reconstruction parameter = 1/2)
mu_n[n] |= ((uint32_t) (v[pos][i] & 1)) << 31; // sign bit.
}
}
@@ -414,7 +429,7 @@ static void recover_mag_sgn(StateVars *mag_sgn, uint8_t pos, uint16_t q, int32_t
static int jpeg2000_import_bit(StateVars *stream, const uint8_t *array, uint32_t length)
{
int cond = stream->pos < length;
- int pos = FFMIN(stream->pos, length);
+ int pos = FFMIN(stream->pos, length - 1);
if (stream->bits == 0) {
stream->bits = (stream->tmp == 0xFF) ? 7 : 8;
stream->pos += cond;
@@ -426,14 +441,22 @@ static int jpeg2000_import_bit(StateVars *stream, const uint8_t *array, uint32_t
static int jpeg2000_peek_bit(StateVars *stream, const uint8_t *array, uint32_t length)
{
+ uint8_t bit;
+
if (stream->bits == 0) {
- int cond = stream->pos < length;
- int pos = FFMIN(stream->pos, length);
- stream->bits = (stream->tmp == 0xFF) ? 7 : 8;
- stream->pos += cond;
- stream->tmp = cond ? array[pos] : 0xFF;
+ stream->bits = (stream->last == 0xFF) ? 7 : 8;
+ if (stream->pos < length) {
+ stream->tmp = array[stream->pos];
+ stream->pos++;
+ } else {
+ stream->tmp = 0;
+ }
+ stream->last = stream->tmp;
}
- return (stream->tmp >> stream->bits) & 1;
+ bit = stream->tmp & 1;
+ stream->tmp >>= 1;
+ stream->bits--;
+ return bit;
}
static int jpeg2000_decode_mel_sym(MelDecoderState *mel_state,
@@ -994,66 +1017,63 @@ static void jpeg2000_calc_mbr(uint8_t *mbr, const uint16_t i, const uint16_t j,
const uint32_t mbr_info, uint8_t causal_cond,
uint8_t *block_states, int stride)
{
- int local_mbr = 0;
-
- local_mbr |= jpeg2000_get_state(i - 1, j - 1, stride, HT_SHIFT_SIGMA, block_states);
- local_mbr |= jpeg2000_get_state(i - 1, j + 0, stride, HT_SHIFT_SIGMA, block_states);
- local_mbr |= jpeg2000_get_state(i - 1, j + 1, stride, HT_SHIFT_SIGMA, block_states);
-
- local_mbr |= jpeg2000_get_state(i + 0, j - 1, stride, HT_SHIFT_SIGMA, block_states);
- local_mbr |= jpeg2000_get_state(i + 0, j + 1, stride, HT_SHIFT_SIGMA, block_states);
-
- local_mbr |= jpeg2000_get_state(i + 1, j - 1, stride, HT_SHIFT_SIGMA, block_states) * causal_cond;
- local_mbr |= jpeg2000_get_state(i + 1, j + 0, stride, HT_SHIFT_SIGMA, block_states) * causal_cond;
- local_mbr |= jpeg2000_get_state(i + 1, j + 1, stride, HT_SHIFT_SIGMA, block_states) * causal_cond;
-
- local_mbr |= jpeg2000_get_state(i - 1, j - 1, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i - 1, j - 1, stride, HT_SHIFT_SCAN, block_states);
- local_mbr |= jpeg2000_get_state(i - 1, j + 0, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i - 1, j - 1, stride, HT_SHIFT_SCAN, block_states);
- local_mbr |= jpeg2000_get_state(i - 1, j + 1, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i - 1, j + 1, stride, HT_SHIFT_SCAN, block_states);
-
- local_mbr |= jpeg2000_get_state(i + 0, j - 1, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i + 0, j - 1, stride, HT_SHIFT_SCAN, block_states);
- local_mbr |= jpeg2000_get_state(i + 0, j + 1, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i + 0, j + 1, stride, HT_SHIFT_SCAN, block_states);
-
- local_mbr |= jpeg2000_get_state(i + 1, j - 1, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i + 1, j - 1, stride, HT_SHIFT_SCAN, block_states) * causal_cond;
- local_mbr |= jpeg2000_get_state(i + 1, j + 0, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i + 1, j + 0, stride, HT_SHIFT_SCAN, block_states) * causal_cond;
- local_mbr |= jpeg2000_get_state(i + 1, j + 1, stride, HT_SHIFT_REF, block_states) *
- jpeg2000_get_state(i + 1, j + 1, stride, HT_SHIFT_SCAN, block_states) * causal_cond;
-
- *mbr |= local_mbr;
+ uint8_t *state_p0 = block_states + i * stride + j;
+ uint8_t *state_p1 = block_states + (i + 1) * stride + j;
+ uint8_t *state_p2 = block_states + (i + 2) * stride + j;
+
+ uint8_t mbr0 = state_p0[0] | state_p0[1] | state_p0[2];
+ uint8_t mbr1 = state_p1[0] | state_p1[2];
+ uint8_t mbr2 = state_p2[0] | state_p2[1] | state_p2[2];
+ *mbr = mbr0 | mbr1 | (mbr2 & causal_cond);
+ *mbr |= (mbr0 >> HT_SHIFT_REF) & (mbr0 >> HT_SHIFT_SCAN);
+ *mbr |= (mbr1 >> HT_SHIFT_REF) & (mbr1 >> HT_SHIFT_SCAN);
+ *mbr |= (mbr2 >> HT_SHIFT_REF) & (mbr2 >> HT_SHIFT_SCAN) & causal_cond;
+ *mbr &= 1;
}
static void jpeg2000_process_stripes_block(StateVars *sig_prop, int i_s, int j_s,
int width, int height, int stride, int pLSB,
int32_t *sample_buf, uint8_t *block_states,
- uint8_t *magref_segment, uint32_t magref_length)
+ uint8_t *magref_segment, uint32_t magref_length,
+ uint8_t is_causal)
{
for (int j = j_s; j < j_s + width; j++) {
uint32_t mbr_info = 0;
for (int i = i_s; i < i_s + height; i++) {
- int modify_state, cond;
+ int modify_state;
uint8_t bit;
- uint8_t causal_cond = i != (i_s + height - 1);
+ uint8_t causal_cond = (is_causal == 0) || (i != (i_s + height - 1));
int32_t *sp = &sample_buf[j + (i * (stride))];
uint8_t mbr = 0;
- if (jpeg2000_get_state(i, j, stride - 2, HT_SHIFT_SIGMA, block_states) == 0)
+ if (jpeg2000_get_state(i, j, stride, HT_SHIFT_SIGMA, block_states) == 0)
jpeg2000_calc_mbr(&mbr, i, j, mbr_info & 0x1EF, causal_cond, block_states, stride);
mbr_info >>= 3;
- cond = mbr != 0;
- bit = jpeg2000_peek_bit(sig_prop, magref_segment, magref_length);
- *sp |= (bit * cond) << pLSB;
- sig_prop->bits -= cond;
- modify_state = (((1 << HT_SHIFT_REF_IND) | (1 << HT_SHIFT_REF)) * cond) | 1 << HT_SHIFT_SCAN;
+
+ modify_state = block_states[(i + 1) * stride + (j + 1)];
+ modify_state |= 1 << HT_SHIFT_SCAN;
+ if (mbr != 0) {
+ modify_state |= 1 << HT_SHIFT_REF_IND;
+ bit = jpeg2000_peek_bit(sig_prop, magref_segment, magref_length);
+ modify_state |= bit << HT_SHIFT_REF;
+ *sp |= bit << pLSB;
+ *sp |= bit << (pLSB - 1); // Add 0.5 (reconstruction parameter = 1/2)
+ }
jpeg2000_modify_state(i, j, stride, modify_state, block_states);
}
}
+ // decode sign
+ for (int j = j_s; j < j_s + width; j++) {
+ for (int i = i_s; i < i_s + height; i++) {
+ uint8_t bit;
+ int32_t *sp = &sample_buf[j + (i * (stride))];
+ uint8_t *state_p = block_states + (i + 1) * stride + (j + 1);
+ if ((state_p[0] >> HT_SHIFT_REF) & 1) {
+ bit = jpeg2000_peek_bit(sig_prop, magref_segment, magref_length);
+ *sp |= (int32_t)bit << 31;
+ }
+ }
+ }
}
/**
@@ -1074,6 +1094,7 @@ static void jpeg2000_decode_sigprop_segment(Jpeg2000Cblk *cblk, uint16_t width,
int last_width;
uint16_t i = 0, j = 0;
+ uint8_t is_causal = cblk->modes & JPEG2000_CBLK_VSC;
jpeg2000_init_zero(&sp_dec);
@@ -1082,14 +1103,14 @@ static void jpeg2000_decode_sigprop_segment(Jpeg2000Cblk *cblk, uint16_t width,
for (int n2 = 0; n2 < num_h_stripe; n2++) {
jpeg2000_process_stripes_block(&sp_dec, i, j, b_width, b_height, stride,
pLSB, sample_buf, block_states, magref_segment,
- magref_length);
+ magref_length, is_causal);
j += 4;
}
last_width = width % 4;
if (last_width)
jpeg2000_process_stripes_block(&sp_dec, i, j, last_width, b_height, stride,
pLSB, sample_buf, block_states, magref_segment,
- magref_length);
+ magref_length, is_causal);
i += 4;
}
@@ -1099,20 +1120,20 @@ static void jpeg2000_decode_sigprop_segment(Jpeg2000Cblk *cblk, uint16_t width,
for (int n2 = 0; n2 < num_h_stripe; n2++) {
jpeg2000_process_stripes_block(&sp_dec, i, j, b_width, b_height, stride,
pLSB, sample_buf, block_states, magref_segment,
- magref_length);
+ magref_length, is_causal);
j += 4;
}
last_width = width % 4;
if (last_width)
jpeg2000_process_stripes_block(&sp_dec, i, j, last_width, b_height, stride,
pLSB, sample_buf, block_states, magref_segment,
- magref_length);
+ magref_length, is_causal);
}
/**
* See procedure decodeSigPropMag at Rec. ITU-T T.814, 7.5.
*/
-static int
+static void
jpeg2000_decode_magref_segment( uint16_t width, uint16_t block_height, const int stride,
uint8_t *magref_segment,uint32_t magref_length,
uint8_t pLSB, int32_t *sample_buf, uint8_t *block_states)
@@ -1123,7 +1144,8 @@ jpeg2000_decode_magref_segment( uint16_t width, uint16_t block_height, const int
uint16_t height = 4;
uint16_t i_start = 0;
int32_t *sp;
-
+ int32_t bit;
+ int32_t tmp;
jpeg2000_init_mag_ref(&mag_ref, magref_length);
for (int n1 = 0; n1 < num_v_stripe; n1++) {
@@ -1134,9 +1156,13 @@ jpeg2000_decode_magref_segment( uint16_t width, uint16_t block_height, const int
* Rec. ITU-T T.814, Figure 7.
*/
sp = &sample_buf[j + i * stride];
- if (jpeg2000_get_state(i, j, width, HT_SHIFT_SIGMA, block_states) != 0) {
- jpeg2000_modify_state(i, j, width, 1 << HT_SHIFT_REF_IND, block_states);
- *sp |= jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length) << pLSB;
+ if (jpeg2000_get_state(i, j, stride, HT_SHIFT_SIGMA, block_states) != 0) {
+ jpeg2000_modify_state(i, j, stride, 1 << HT_SHIFT_REF_IND, block_states);
+ bit = jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length);
+ tmp = 0xFFFFFFFE | (uint32_t)bit;
+ tmp <<= pLSB;
+ sp[0] &= tmp;
+ sp[0] |= 1 << (pLSB - 1); // Add 0.5 (reconstruction parameter = 1/2)
}
}
}
@@ -1146,19 +1172,22 @@ jpeg2000_decode_magref_segment( uint16_t width, uint16_t block_height, const int
for (int j = 0; j < width; j++) {
for (int i = i_start; i < i_start + height; i++) {
sp = &sample_buf[j + i * stride];
- if (jpeg2000_get_state(i, j, width, HT_SHIFT_SIGMA, block_states) != 0) {
- jpeg2000_modify_state(i, j, width, 1 << HT_SHIFT_REF_IND, block_states);
- *sp |= jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length) << pLSB;
+ if (jpeg2000_get_state(i, j, stride, HT_SHIFT_SIGMA, block_states) != 0) {
+ jpeg2000_modify_state(i, j, stride, 1 << HT_SHIFT_REF_IND, block_states);
+ bit = jpeg2000_import_magref_bit(&mag_ref, magref_segment, magref_length);
+ tmp = 0xFFFFFFFE | (uint32_t)bit;
+ tmp <<= pLSB;
+ sp[0] &= tmp;
+ sp[0] |= 1 << (pLSB - 1); // Add 0.5 (reconstruction parameter = 1/2)
}
}
}
- return 1;
}
int
ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty, Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk,
- int width, int height, int magp, uint8_t roi_shift)
+ int width, int height, int M_b, uint8_t roi_shift)
{
uint8_t p0 = 0; // 3 * p0 = Number of placeholder passes
uint32_t Lcup; // Length of HT cleanup segment
@@ -1190,7 +1219,8 @@ ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
uint8_t *block_states = NULL;
int32_t n, val; // Post-processing
-
+ const uint32_t mask = UINT32_MAX >> (M_b + 1); // bit mask for ROI detection
+
uint8_t num_rempass;
const int quad_buf_width = width + 4;
@@ -1201,9 +1231,6 @@ ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
av_assert0(width * height <= 4096);
av_assert0(width * height > 0);
- if (roi_shift)
- avpriv_report_missing_feature(s->avctx, "ROI shift");
-
memset(t1->data, 0, t1->stride * height * sizeof(*t1->data));
memset(t1->flags, 0, t1->stride * (height + 2) * sizeof(*t1->flags));
@@ -1229,7 +1256,11 @@ ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
}
Dcup = cblk->data;
Dref = cblk->data + Lcup; // Dref comes after the refinement segment
+
+ cblk->data[cblk->length] = 0xFF; // an extra byte for refinement segment (buffer->last)
+
S_blk = p0 + cblk->zbp;
+ cblk->zbp = S_blk - 1;
pLSB = 30 - S_blk;
Scup = (Dcup[Lcup - 1] << 4) + (Dcup[Lcup - 2] & 0x0F);
@@ -1275,31 +1306,32 @@ ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
goto free;
}
- if (cblk->npasses > 1)
+ if (z_blk > 1)
jpeg2000_decode_sigprop_segment(cblk, width, height, quad_buf_width, Dref, Lref,
pLSB - 1, sample_buf, block_states);
- if (cblk->npasses > 2) {
-
- if (Lref < 2){
- av_log(s->avctx,AV_LOG_ERROR,"Invalid magnitude refinement length\n");
- ret = AVERROR_INVALIDDATA;
- goto free;
- }
- if ((ret = jpeg2000_decode_magref_segment(width, height, quad_buf_width, Dref, Lref,
- pLSB - 1, sample_buf, block_states)) < 0)
- goto free;
- }
+ if (z_blk > 2)
+ jpeg2000_decode_magref_segment(width, height, quad_buf_width, Dref, Lref,
+ pLSB - 1, sample_buf, block_states);
pLSB = 31 - M_b;
/* Reconstruct the sample values */
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
+ int32_t sign;
+
n = x + (y * t1->stride);
val = sample_buf[x + (y * quad_buf_width)];
+ sign = val & INT32_MIN;
+ val &= INT32_MAX;
+ /* ROI shift, if necessary */
+ if (roi_shift && (((uint32_t)val & ~mask) == 0))
+ val <<= roi_shift;
/* Convert sign-magnitude to two's complement. */
- val = val >> 31 ? 0x80000000 - val : val;
+ if (sign)
+ val = -val;
+ /* Shift down to 1 bit upper from decimal point for reconstruction value (= 0.5) */
val >>= (pLSB - 1);
t1->data[n] = val;
}
diff --git a/libavcodec/jpeg2000htdec.h b/libavcodec/jpeg2000htdec.h
index 572d095c92..8d6919a0de 100644
--- a/libavcodec/jpeg2000htdec.h
+++ b/libavcodec/jpeg2000htdec.h
@@ -29,6 +29,6 @@
int ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *codsty,
Jpeg2000T1Context *t1, Jpeg2000Cblk *cblk, int width,
- int height, int magp, uint8_t roi_shift);
+ int height, int M_b, uint8_t roi_shift);
#endif /* AVCODEC_JPEG2000HTDEC_H */
--
2.34.1
_______________________________________________
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".
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes Osamu Watanabe
@ 2024-06-24 9:34 ` Andreas Rheinhardt
2024-06-24 10:11 ` WATANABE Osamu
0 siblings, 1 reply; 5+ messages in thread
From: Andreas Rheinhardt @ 2024-06-24 9:34 UTC (permalink / raw)
To: ffmpeg-devel
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".
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes
2024-06-24 9:34 ` Andreas Rheinhardt
@ 2024-06-24 10:11 ` WATANABE Osamu
0 siblings, 0 replies; 5+ messages in thread
From: WATANABE Osamu @ 2024-06-24 10:11 UTC (permalink / raw)
To: FFmpeg development discussions and patches
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".
_______________________________________________
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".
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-06-24 10:11 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-24 6:19 [FFmpeg-devel] [PATCH v3 1/3] avcodec/jpeg2000dec: Add support for CAP and CPF markers Osamu Watanabe
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 2/3] avcodec/jpeg2000dec: Add support for placeholder passes Osamu Watanabe
2024-06-24 9:34 ` Andreas Rheinhardt
2024-06-24 10:11 ` WATANABE Osamu
2024-06-24 6:19 ` [FFmpeg-devel] [PATCH v3 3/3] avcodec/jpeg2000dec: Fix HT decoding Osamu Watanabe
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git