A couple of modifications: 1. Added support for Vertically causal decoding in HT mode. 2. HT block decoder should not read from outside a codeblock lenght -- this required modification of data reading algorithm, which improved them. 3. Improve messaging around unreasonable or illegal conditions that may occur during block decoding; two of these conditions are better moved to t2.c.

This commit is contained in:
Aous Naman 2021-09-20 22:51:14 +10:00
parent 7c754ed468
commit f4436c74f1
2 changed files with 175 additions and 142 deletions

View File

@ -55,24 +55,11 @@
#define OPJ_COMPILER_GNUC
#endif
//************************************************************************/
/** @brief Displays the error message when 32 bits are not sufficient to
* decode any passes
*/
static OPJ_BOOL cannot_decode_due_to_insufficient_precision = OPJ_FALSE;
//************************************************************************/
/** @brief Displays the error message when we do not have enough precision
* to decode the cleanup pass and set the bin center to 1. The code can
* be modified to support this case.
*/
static OPJ_BOOL modify_code_to_support_this_precision = OPJ_FALSE;
//************************************************************************/
/** @brief Displays the error message for disabling the decoding of SPP and
* MRP passes
*/
static OPJ_BOOL cannot_decode_spp_mrp_msg = OPJ_FALSE;
static OPJ_BOOL only_cleanup_pass_is_decoded = OPJ_FALSE;
//************************************************************************/
/** @brief Generates population count (i.e., the number of set bits)
@ -167,10 +154,29 @@ void mel_read(dec_mel_t *melp)
if (melp->bits > 32) { //there are enough bits in the tmp variable
return; // return without reading new data
}
val = 0xFFFFFFFF;
//the next line (the if statement) needs to be tested first
//if (melp->size > 0) // if there is data in the MEL segment
val = *(OPJ_UINT32*)melp->data; // read 32 bits from MEL data
val = 0xFFFFFFFF; // feed in 0xFF if buffer is exhausted
if (melp->size > 4) { // if there is more than 4 bytes the MEL segment
val = *(OPJ_UINT32*)melp->data; // read 32 bits from MEL data
melp->data += 4; // advance pointer
melp->size -= 4; // reduce counter
} else if (melp->size > 0) { // 4 or less
OPJ_UINT32 m, v;
int i = 0;
while (melp->size > 1) {
OPJ_UINT32 v = *melp->data++; // read one byte at a time
OPJ_UINT32 m = ~(0xFFu << i); // mask of location
val = (val & m) | (v << i); // put byte in its correct location
--melp->size;
i += 8;
}
// size equal to 1
v = *melp->data++; // the one before the last is different
v |= 0xF; // MEL and VLC segments can overlap
m = ~(0xFFu << i);
val = (val & m) | (v << i);
--melp->size;
}
// next we unstuff them before adding them to the buffer
bits = 32 - melp->unstuff; // number of bits in val, subtract 1 if
@ -179,46 +185,23 @@ void mel_read(dec_mel_t *melp)
// data is unstuffed and accumulated in t
// bits has the number of bits in t
t = (melp->size > 0) ? (val & 0xFF) : 0xFF; // feed 0xFF if the
// MEL bitstream has been exhausted
if (melp->size == 1) {
t |= 0xF; // if this is 1 byte before the last
}
// in MEL+VLC segments (remember they
// can overlap)
melp->data += melp->size-- > 0; // advance data by 1 byte if we have not
// reached the end of the MEL segment
t = val & 0xFF;
unstuff = ((val & 0xFF) == 0xFF); // true if the byte needs unstuffing
bits -= unstuff; // there is one less bit in t if unstuffing is needed
t = t << (8 - unstuff); // move up to make room for the next byte
//this is a repeat of the above
t |= (melp->size > 0) ? ((val >> 8) & 0xFF) : 0xFF;
if (melp->size == 1) {
t |= 0xF;
}
melp->data += melp->size-- > 0;
t |= (val >> 8) & 0xFF;
unstuff = (((val >> 8) & 0xFF) == 0xFF);
bits -= unstuff;
t = t << (8 - unstuff);
t |= (melp->size > 0) ? ((val >> 16) & 0xFF) : 0xFF;
if (melp->size == 1) {
t |= 0xF;
}
melp->data += melp->size-- > 0;
t |= (val >> 16) & 0xFF;
unstuff = (((val >> 16) & 0xFF) == 0xFF);
bits -= unstuff;
t = t << (8 - unstuff);
t |= (melp->size > 0) ? ((val >> 24) & 0xFF) : 0xFF;
if (melp->size == 1) {
t |= 0xF;
}
melp->data += melp->size-- > 0;
t |= (val >> 24) & 0xFF;
melp->unstuff = (((val >> 24) & 0xFF) == 0xFF);
// move t to tmp, and push the result all the way up, so we read from
@ -401,12 +384,19 @@ void rev_read(rev_struct_t *vlcp)
}
val = 0;
//the next line (the if statement) needs to be tested first
if (vlcp->size > 0) { // if there are bytes left in the VLC segment
// We pad the data by 8 bytes at the beginning of the code stream
// buffer
val = *(OPJ_UINT32*)vlcp->data; // then read 32 bits
if (vlcp->size > 3) { // if there are more than 3 bytes left in VLC
// (vlcp->data - 3) move pointer back to read 32 bits at once
val = *(OPJ_UINT32*)(vlcp->data - 3); // then read 32 bits
vlcp->data -= 4; // move data pointer back by 4
vlcp->size -= 4; // reduce available byte by 4
} else if (vlcp->size > 0) { // 4 or less
int i = 24;
while (vlcp->size > 0) {
OPJ_UINT32 v = *vlcp->data--; // read one byte at a time
val |= (v << i); // put byte in its correct location
--vlcp->size;
i -= 8;
}
}
//accumulate in tmp, number of bits in tmp are stored in bits
@ -468,7 +458,8 @@ void rev_init(rev_struct_t *vlcp, OPJ_UINT8* data, int lcup, int scup)
//This code is designed for an architecture that read address should
// align to the read size (address multiple of 4 if read size is 4)
//These few lines take care of the case where data is not at a multiple
// of 4 boundary. It reads 1,2,3 up to 4 bytes from the VLC bitstream
// of 4 boundary. It reads 1,2,3 up to 4 bytes from the VLC bitstream.
// To read 32 bits, read from (vlcp->data - 3)
num = 1 + (int)((intptr_t)(vlcp->data) & 0x3);
tnum = num < vlcp->size ? num : vlcp->size;
for (i = 0; i < tnum; ++i) {
@ -482,7 +473,6 @@ void rev_init(rev_struct_t *vlcp, OPJ_UINT8* data, int lcup, int scup)
vlcp->unstuff = d > 0x8F; // for next byte
}
vlcp->size -= tnum;
vlcp->data -= 3; // make ready to read 32 bits (address multiple of 4)
rev_read(vlcp); // read another 32 buts
}
@ -544,32 +534,39 @@ void rev_read_mrp(rev_struct_t *mrp)
return;
}
val = 0;
//the next line (the if statement) needs to be tested first
//notice that second line can be simplified to mrp->data -= 4
// if (mrp->size > 0)
{
val = *(OPJ_UINT32*)mrp->data; // read 32 bits
mrp->data -= mrp->size > 0 ? 4 : 0; // move back read pointer only if
// there is data
if (mrp->size > 3) { // If there are 3 byte or more
// (mrp->data - 3) move pointer back to read 32 bits at once
val = *(OPJ_UINT32*)(mrp->data - 3); // read 32 bits
mrp->data -= 4; // move back pointer
mrp->size -= 4; // reduce count
} else if (mrp->size > 0) {
int i = 24;
while (mrp->size > 0) {
OPJ_UINT32 v = *mrp->data--; // read one byte at a time
val |= (v << i); // put byte in its correct location
--mrp->size;
i -= 8;
}
}
//accumulate in tmp, and keep count in bits
tmp = (mrp->size-- > 0) ? (val >> 24) : 0; // fill zeros if all
tmp = val >> 24;
//test if the last byte > 0x8F (unstuff must be true) and this is 0x7F
bits = 8u - ((mrp->unstuff && (((val >> 24) & 0x7F) == 0x7F)) ? 1u : 0u);
unstuff = (val >> 24) > 0x8F;
//process the next byte
tmp |= (mrp->size-- > 0) ? (((val >> 16) & 0xFF) << bits) : 0;
tmp |= ((val >> 16) & 0xFF) << bits;
bits += 8u - ((unstuff && (((val >> 16) & 0x7F) == 0x7F)) ? 1u : 0u);
unstuff = ((val >> 16) & 0xFF) > 0x8F;
tmp |= (mrp->size-- > 0) ? (((val >> 8) & 0xFF) << bits) : 0;
tmp |= ((val >> 8) & 0xFF) << bits;
bits += 8u - ((unstuff && (((val >> 8) & 0x7F) == 0x7F)) ? 1u : 0u);
unstuff = ((val >> 8) & 0xFF) > 0x8F;
tmp |= (mrp->size-- > 0) ? ((val & 0xFF) << bits) : 0;
tmp |= (val & 0xFF) << bits;
bits += 8u - ((unstuff && ((val & 0x7F) == 0x7F)) ? 1u : 0u);
unstuff = (val & 0xFF) > 0x8F;
@ -621,7 +618,6 @@ void rev_init_mrp(rev_struct_t *mrp, OPJ_UINT8* data, int lcup, int len2)
mrp->bits += d_bits;
mrp->unstuff = d > 0x8F; // for next byte
}
mrp->data -= 3; //make ready to read a 32 bits
rev_read_mrp(mrp);
}
@ -889,25 +885,39 @@ void frwd_read(frwd_struct_t *msp)
assert(msp->bits <= 32); // assert that there is a space for 32 bits
val = *(OPJ_UINT32*)msp->data; // read 32 bits
msp->data += msp->size > 0 ? 4 : 0; // move pointer if data is not
// exhausted
val = 0u;
if (msp->size > 3) {
val = *(OPJ_UINT32*)msp->data; // read 32 bits
msp->data += 4; // increment pointer
msp->size -= 4; // reduce size
} else if (msp->size > 0) {
int i = 0;
val = msp->X != 0 ? 0xFFFFFFFFu : 0;
while (msp->size > 0) {
OPJ_UINT32 v = *msp->data++; // read one byte at a time
OPJ_UINT32 m = ~(0xFFu << i); // mask of location
val = (val & m) | (v << i); // put one byte in its correct location
--msp->size;
i += 8;
}
} else {
val = msp->X != 0 ? 0xFFFFFFFFu : 0;
}
// we accumulate in t and keep a count of the number of bits in bits
bits = 8u - (msp->unstuff ? 1u : 0u); // if previous byte was 0xFF
// get next byte, if bitstream is exhausted, replace it with X
t = msp->size-- > 0 ? (val & 0xFF) : msp->X;
bits = 8u - (msp->unstuff ? 1u : 0u);
t = val & 0xFF;
unstuff = ((val & 0xFF) == 0xFF); // Do we need unstuffing next?
t |= (msp->size-- > 0 ? ((val >> 8) & 0xFF) : msp->X) << bits;
t |= ((val >> 8) & 0xFF) << bits;
bits += 8u - (unstuff ? 1u : 0u);
unstuff = (((val >> 8) & 0xFF) == 0xFF);
t |= (msp->size-- > 0 ? ((val >> 16) & 0xFF) : msp->X) << bits;
t |= ((val >> 16) & 0xFF) << bits;
bits += 8u - (unstuff ? 1u : 0u);
unstuff = (((val >> 16) & 0xFF) == 0xFF);
t |= (msp->size-- > 0 ? ((val >> 24) & 0xFF) : msp->X) << bits;
t |= ((val >> 24) & 0xFF) << bits;
bits += 8u - (unstuff ? 1u : 0u);
msp->unstuff = (((val >> 24) & 0xFF) == 0xFF); // for next byte
@ -1077,7 +1087,7 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
OPJ_BYTE* cblkdata = NULL;
OPJ_UINT8* coded_data;
OPJ_UINT32* decoded_data;
OPJ_UINT32 missing_msbs;
OPJ_UINT32 zero_bplanes;
OPJ_UINT32 num_passes;
OPJ_UINT32 lengths1;
OPJ_UINT32 lengths2;
@ -1086,7 +1096,7 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
OPJ_INT32 stride;
OPJ_UINT32 *pflags, *sigma1, *sigma2, *mbr1, *mbr2, *sip, sip_shift;
OPJ_UINT32 p;
OPJ_UINT32 zero_planes_p1;
OPJ_UINT32 zero_bplanes_p1;
int lcup, scup;
dec_mel_t mel;
rev_struct_t vlc;
@ -1100,13 +1110,13 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
OPJ_UINT32 c_q;
OPJ_UINT32* sp;
OPJ_INT32 x, y; // loop indices
OPJ_BOOL stripe_causal = (cblksty & J2K_CCP_CBLKSTY_VSC) != 0;
(void)(orient); // stops unused parameter message
(void)(check_pterm); // stops unused parameter message
// We ignor orient, because the same decoder is used for all subbands
// We also ignore check_pterm, because I am not sure how it applies
assert(cblksty == J2K_CCP_CBLKSTY_HT); // that is the only support mode
if (roishift != 0) {
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
@ -1130,8 +1140,8 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
return OPJ_TRUE;
}
/* Mb = Kmax, numbps = Kmax + 1 - missing_msbs */
missing_msbs = (cblk->Mb + 1) - cblk->numbps;
/* numbps = Mb + 1 - zero_bplanes, Mb = Kmax, zero_bplanes = missing_msbs */
zero_bplanes = (cblk->Mb + 1) - cblk->numbps;
/* Even if we have a single chunk, in multi-threaded decoding */
/* the insertion of our synthetic marker might potentially override */
@ -1223,7 +1233,7 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
}
opj_event_msg(p_manager, EVT_WARNING, "A malformed codeblock that has "
"more than one coding pass, but zero length for "
"2nd and potential 3rd pass.\n");
"2nd and potentially the 3rd pass in an HT codeblock.\n");
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
@ -1233,77 +1243,103 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
opj_event_msg(p_manager, EVT_WARNING, "We do not support more than 3 "
"coding passes; This codeblocks has %d passes.\n",
num_passes);
opj_event_msg(p_manager, EVT_ERROR, "We do not support more than 3 "
"coding passes in an HT codeblock; This codeblocks has "
"%d passes.\n", num_passes);
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
return OPJ_FALSE;
}
if (missing_msbs > 30) {
/* We do not have enough precision to decode any passes */
if (cannot_decode_due_to_insufficient_precision == OPJ_FALSE) {
if (cblk->Mb > 30) {
/* This check is better moved to opj_t2_read_packet_header() in t2.c
We do not have enough precision to decode any passes
The design of openjpeg assumes that the bits of a 32-bit integer are
assigned as follows:
bit 31 is for sign
bits 30-1 are for magnitude
bit 0 is for the center of the quantization bin
Therefore we can only do values of cblk->Mb <= 30
*/
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
opj_event_msg(p_manager, EVT_ERROR, "32 bits are not enough to "
"decode this codeblock, since the number of "
"bitplane, %d, is larger than 30.\n", cblk->Mb);
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
return OPJ_FALSE;
}
if (zero_bplanes > cblk->Mb) {
/* This check is better moved to opj_t2_read_packet_header() in t2.c,
in the line "l_cblk->numbps = (OPJ_UINT32)l_band->numbps + 1 - i;"
where i is the zero bitplanes, and should be no larger than cblk->Mb
We cannot have more zero bitplanes than there are planes. */
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
"Decoding this codeblock is stopped. There are "
"%d zero bitplanes in %d bitplanes.\n",
zero_bplanes, cblk->Mb);
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
return OPJ_FALSE;
} else if (zero_bplanes == cblk->Mb && num_passes > 1) {
/* When the number of zero bitplanes is equal to the number of bitplanes,
only the cleanup pass makes sense*/
if (only_cleanup_pass_is_decoded == OPJ_FALSE) {
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
cannot_decode_due_to_insufficient_precision = OPJ_TRUE;
opj_event_msg(p_manager, EVT_ERROR, "32 bits are not enough to "
"decode this codeblock. This message "
"will not be displayed again.\n");
/* We have a second check to prevent the possibility of an overrun condition,
in the very unlikely event of a second thread discovering that
only_cleanup_pass_is_decoded is false before the first thread changing
the condition. */
if (only_cleanup_pass_is_decoded == OPJ_FALSE) {
only_cleanup_pass_is_decoded = OPJ_TRUE;
opj_event_msg(p_manager, EVT_WARNING, "Malformed HT codeblock. "
"When the number of zero planes bitplanes is "
"equal to the number of bitplanes, only the cleanup "
"pass makes sense, but we have %d passes in this "
"codeblock. Therefore, only the cleanup pass will be "
"decoded. This message will not be displayed again.\n",
num_passes);
}
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
}
return OPJ_FALSE;
} else if (missing_msbs == 30) {
/* We do not have enough precision to decode the CUP pass with the
center of bin bit set. The code can be modified to support this
case, where we do not set the center of the bin. */
if (modify_code_to_support_this_precision == OPJ_FALSE) {
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
modify_code_to_support_this_precision = OPJ_TRUE;
opj_event_msg(p_manager, EVT_ERROR, "Not enough precision to decode "
"the cleanup pass. The code can be modified to "
"support this case. This message will not be "
"displayed again.\n");
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
}
return OPJ_FALSE;
} else if (missing_msbs == 29) { /* if p is 1, then num_passes must be 1 */
if (num_passes > 1) {
num_passes = 1;
if (cannot_decode_spp_mrp_msg == OPJ_FALSE) {
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
cannot_decode_spp_mrp_msg = OPJ_TRUE;
opj_event_msg(p_manager, EVT_WARNING, "Not enough precision to decode "
"the SgnProp nor MagRef passes, which will be skipped. "
"This message will not be displayed again.\n");
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
}
}
num_passes = 1;
}
/* OPJ_UINT32 */
p = 30 - missing_msbs;
p = cblk->numbps;
// OPJ_UINT32 zero planes plus 1
zero_planes_p1 = missing_msbs + 1;
zero_bplanes_p1 = zero_bplanes + 1;
// read scup and fix the bytes there
lcup = (int)lengths1; // length of CUP
//scup is the length of MEL + VLC
scup = (((int)coded_data[lcup - 1]) << 4) + (coded_data[lcup - 2] & 0xF);
if (scup < 2 || scup > lcup || scup > 4079) { //something is wrong
/* The standard stipulates 2 <= Scup <= min(Lcup, 4079) */
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
"One of the following condition is not met: "
"2 <= Scup <= min(Lcup, 4079)\n");
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
return OPJ_FALSE;
}
@ -1460,12 +1496,13 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
}
//decode uvlc_mode to get u for both quads
consumed_bits = decode_init_uvlc(vlc_val, uvlc_mode, U_q);
if (U_q[0] > zero_planes_p1 || U_q[1] > zero_planes_p1) {
if (U_q[0] > zero_bplanes_p1 || U_q[1] > zero_bplanes_p1) {
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. Decoding "
"this codeblock is stopped.\n");
"this codeblock is stopped. U_q is larger than zero "
"bitplanes + 1 \n");
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
@ -1747,12 +1784,13 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
U_q[1] += E > 2 ? E - 2 : 0;
}
if (U_q[0] > zero_planes_p1 || U_q[1] > zero_planes_p1) {
if (U_q[0] > zero_bplanes_p1 || U_q[1] > zero_bplanes_p1) {
if (p_manager_mutex) {
opj_mutex_lock(p_manager_mutex);
}
opj_event_msg(p_manager, EVT_ERROR, "Malformed HT codeblock. "
"Decoding this codeblock is stopped.\n");
"Decoding this codeblock is stopped. U_q is"
"larger than bitplanes + 1 \n");
if (p_manager_mutex) {
opj_mutex_unlock(p_manager_mutex);
}
@ -2046,7 +2084,9 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
t |= nxt_sig[1] << 28; //for last column, right neighbors
prev = nxt_sig[0]; // for next group of columns
cur_mbr[0] |= (t & 0x11111111u) << 3; //propagate up to cur_mbr
if (!stripe_causal) {
cur_mbr[0] |= (t & 0x11111111u) << 3; //propagate up to cur_mbr
}
cur_mbr[0] &= ~cur_sig[0]; //remove already significance samples
}
@ -2355,7 +2395,9 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
t |= nxt_sig[1] << 28; //for last column, right neighbors
prev = nxt_sig[0];
cur_mbr[0] |= (t & 0x11111111) << 3;
if (!stripe_causal) {
cur_mbr[0] |= (t & 0x11111111u) << 3;
}
//remove already significance samples
cur_mbr[0] &= ~cur_sig[0];
}
@ -2528,11 +2570,10 @@ OPJ_BOOL opj_t1_ht_decode_cblk(opj_t1_t *t1,
{
OPJ_INT32 x, y;
OPJ_UINT32 shift = 29u - cblk->Mb;
for (y = 0; y < height; ++y) {
OPJ_INT32* sp = (OPJ_INT32*)decoded_data + y * stride;
for (x = 0; x < width; ++x, ++sp) {
OPJ_INT32 val = (*sp & 0x7FFFFFFF) >> shift;
OPJ_INT32 val = (*sp & 0x7FFFFFFF);
*sp = ((OPJ_UINT32) * sp & 0x80000000) ? -val : val;
}
}

View File

@ -10700,20 +10700,12 @@ static OPJ_BOOL opj_j2k_read_SPCod_SPCoc(opj_j2k_t *p_j2k,
opj_read_bytes(l_current_ptr, &l_tccp->cblksty, 1);
++l_current_ptr;
if ((l_tccp->cblksty & J2K_CCP_CBLKSTY_HTMIXED) != 0) {
/* We do not support HT mixed mode yet*/
/* We do not support HT mixed mode yet. For conformance, it should be supported.*/
opj_event_msg(p_manager, EVT_ERROR,
"Error reading SPCod SPCoc element. Unsupported Mixed HT code-block style found\n");
return OPJ_FALSE;
}
if ((l_tccp->cblksty & (J2K_CCP_CBLKSTY_HT | J2K_CCP_CBLKSTY_VSC)) ==
(J2K_CCP_CBLKSTY_HT | J2K_CCP_CBLKSTY_VSC)) {
/* For HT, we do not support vertically causal mode yet. */
opj_event_msg(p_manager, EVT_ERROR,
"Error reading SPCod SPCoc element. Unsupported HT mode with vertically causal mode. \n");
return OPJ_FALSE;
}
/* SPcod (H) / SPcoc (E) */
opj_read_bytes(l_current_ptr, &l_tccp->qmfbid, 1);
++l_current_ptr;