diff mbox series

[FFmpeg-devel,v2] avcodec/jpeg2000dec: Add support for placeholder passes, CAP, and CPF markers

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

Checks

Context Check Description
yinshiyou/make_loongarch64 success Make finished
yinshiyou/make_fate_loongarch64 success Make fate finished
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished

Commit Message

Osamu Watanabe June 17, 2024, 4 a.m. UTC
This v2
- fixes the regression indicated in Message-ID: <20240615201144.GB4991@pb2>
by counting the number of necessary terminations for non HT code blocks with the existing code.
- drops the commit that was recently merged. 

Signed-off-by: Osamu Watanabe <owatanab@es.takushoku-u.ac.jp>
---
 libavcodec/jpeg2000.h      |  10 +
 libavcodec/jpeg2000dec.c   | 470 ++++++++++++++++++++++++++++++-------
 libavcodec/jpeg2000dec.h   |   7 +
 libavcodec/jpeg2000htdec.c | 225 ++++++++++--------
 libavcodec/jpeg2000htdec.h |   2 +-
 5 files changed, 535 insertions(+), 179 deletions(-)

Comments

Tomas Härdin June 18, 2024, 2:20 p.m. UTC | #1
It seems this patch combines a lot of things that might be better to
split into separate patches for easier review

> @@ -382,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;

Could we handle 9 <= precision <= 16 while we're at it?

> @@ -408,6 +420,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) {

Missing indentation

> +        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;
> +    }
> +    }

Weird indentation

> @@ -802,6 +881,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;
> +}

Don't we already have code for skipping markers we don't care about?

> +
>  /* 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 +1053,10 @@ 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_WARNING, "Transformation = 0
> (lossy DWT) is found in HTREV HT set\n");
> +        if (s->isHT && s->Ccap15_b14_15 != (codsty->cblk_style >> 6)
> && s->Ccap15_b14_15 != HTJ2K_HTONLY)
> +            av_log(s->avctx, AV_LOG_WARNING, "SPcod/SPcoc value does
> not match bit 14-15 values of Ccap15\n");

Do you have samples demonstrating the need to accept such broken files?
If not then we should probably error out

> @@ -1704,7 +1989,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;

Won't this break files with ROI? I see there's some ROI stuff further
down so maybe not

> @@ -2187,22 +2472,42 @@ 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_WARNING, "CAP marker segment
> shall come after SIZ\n");

SHALL -> we should be able to safely reject. Similarly with the other
errors. Unless we know of an encoder that produces broken files then
there's no reason to be lenient. And if such a broken encoder exists we
could try to get it fixed

> @@ -112,6 +112,13 @@ typedef struct Jpeg2000DecoderContext {
>      Jpeg2000Tile    *tile;
>      Jpeg2000DSPContext dsp;
>  
> +    uint8_t         isHT; // HTJ2K?

Isn't it possible to mix Part 1 and HT in the same file? I know HTONLY
exists also

> @@ -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)

Aren't there conformance files to catch these kinds of errors? I
remember looked at J2K a while back, and I think we should add such
files to FATE

/Tomas
Osamu Watanabe June 19, 2024, 5:51 a.m. UTC | #2
First of all, I appreciate your kind review.
I'm writing to discuss the changes and would like to hear your feedback on these.


> On Jun 18, 2024, at 23:20, Tomas Hardin <git@haerdin.se> wrote:
> 
> 
> It seems this patch combines a lot of things that might be better to
> split into separate patches for easier review

Agree. I will split this patch into several patches.
For example, the set of patches includes changes:
- only for HTJ2K (JPEG 2000 Part 15)
- only for J2K (JPEG 2000 Part 1)
- for both J2K and HTJ2K.

Do you think it makes sense?


> 
>> @@ -382,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;
> 
> Could we handle 9 <= precision <= 16 while we're at it?
> 

Yes. The native J2K decoder for both lossless and lossy J2K/HTJ2K
codestreams can handle bpp from 9 to 16. This change is required to
produce the decoded images for the ISO test codestreams defined in
Part 4 (Conformance testing).


>> @@ -408,6 +420,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) {
> 
> Missing indentation
> 
>> +        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;
>> +    }
>> +    }
> 
> Weird indentation
> 

Thank you for catching these. I will fix them in the patch set.


>> @@ -802,6 +881,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;
>> +}
> 
> Don't we already have code for skipping markers we don't care about?
> 

The `read_cpf()` function was added for consistency with the `read_crg()` function.
We already have `bytestream2_skip(GetByteContext *g, unsigned int size)` that skips `size`
bytes from the compressed data. 
Do you think it is better to replace those functions (= `read_cpf()` and `read_crg()`)
with `bytestream2_skip()`?


>> +
>>  /* 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 +1053,10 @@ 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_WARNING, "Transformation = 0
>> (lossy DWT) is found in HTREV HT set\n");
>> +        if (s->isHT && s->Ccap15_b14_15 != (codsty->cblk_style >> 6)
>> && s->Ccap15_b14_15 != HTJ2K_HTONLY)
>> +            av_log(s->avctx, AV_LOG_WARNING, "SPcod/SPcoc value does
>> not match bit 14-15 values of Ccap15\n");
> 
> Do you have samples demonstrating the need to accept such broken files?
> If not then we should probably error out

Does `error out` mean that
- Should we exit decoding here?
- or should we replace AV_LOG_WARNING with AV_LOG_ERROR?


> 
>> @@ -1704,7 +1989,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;
> 
> Won't this break files with ROI? I see there's some ROI stuff further
> down so maybe not

It won't break ROI decoding, and this change is mandatory
when handling codestreams with placeholder passes.

> 
>> @@ -2187,22 +2472,42 @@ 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_WARNING, "CAP marker segment
>> shall come after SIZ\n");
> 
> SHALL -> we should be able to safely reject. Similarly with the other
> errors. Unless we know of an encoder that produces broken files then
> there's no reason to be lenient. And if such a broken encoder exists we
> could try to get it fixed

Does `safely reject` mean that we should replace AV_LOG_WARNING with
AV_LOG_ERROR? or we should stop decoding here?


> 
>> @@ -112,6 +112,13 @@ typedef struct Jpeg2000DecoderContext {
>>      Jpeg2000Tile    *tile;
>>      Jpeg2000DSPContext dsp;
>>  
>> +    uint8_t         isHT; // HTJ2K?
> 
> Isn't it possible to mix Part 1 and HT in the same file? I know HTONLY
> exists also

It is possible to mix Part 1 and HT in the same tile-component.
This mode is defined as MIXED in the spec of Part 15.
There are three types of HT codestreams:
- HTONLY
- HTDECLARED
- MIXED

The spec says - 
"The HTONLY set is the set of HTJ2K codestreams where all code-blocks
are HT code-blocks. The HTDECLARED set is the set of HTJ2K codestreams
where all code-blocks within a given tile-component are either
a) HT code-blocks, or b) code-blocks as specified in 
Rec. ITU-T T.800 | ISO/IEC 15444-1. The MIXED set is the set of all
HTJ2K codestreams that are not in the HTDECLARED set."


> 
>> @@ -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)
> 
> Aren't there conformance files to catch these kinds of errors? I
> remember looked at J2K a while back, and I think we should add such
> files to FATE
> 

Do you mean "errors" are the difference in pixel values between
uncompressed and lossy compressed images?

There are no specific conformance files to catch the difference
in reconstruction parameter values. 

The value of the reconstruction parameter r is not limited to 1/2.
We can use other values.
However, the spec of Part 4 says the use of reconstruction parameter
r = 1/2 will typically increase the ease of passing.
 
Looking forward to seeing your thoughts.

Best,
Osamu

> /Tomas
> _______________________________________________
> 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".
Tomas Härdin June 19, 2024, 8:27 a.m. UTC | #3
ons 2024-06-19 klockan 05:51 +0000 skrev WATANABE Osamu:
> First of all, I appreciate your kind review.
> I'm writing to discuss the changes and would like to hear your
> feedback on these.
> 
> 
> > On Jun 18, 2024, at 23:20, Tomas Hardin <git@haerdin.se> wrote:
> > 
> > 
> > It seems this patch combines a lot of things that might be better
> > to
> > split into separate patches for easier review
> 
> Agree. I will split this patch into several patches.
> For example, the set of patches includes changes:
> - only for HTJ2K (JPEG 2000 Part 15)
> - only for J2K (JPEG 2000 Part 1)
> - for both J2K and HTJ2K.
> 
> Do you think it makes sense?

Maybe. Going by the commit message, separate support for placeholder
passes from CAP from CFP handling perhaps?

> > > @@ -382,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;
> > 
> > Could we handle 9 <= precision <= 16 while we're at it?
> > 
> 
> Yes. The native J2K decoder for both lossless and lossy J2K/HTJ2K
> codestreams can handle bpp from 9 to 16. This change is required to
> produce the decoded images for the ISO test codestreams defined in
> Part 4 (Conformance testing).

Are there any test files for the other precisions?


> > > @@ -802,6 +881,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;
> > > +}
> > 
> > Don't we already have code for skipping markers we don't care
> > about?
> > 
> 
> The `read_cpf()` function was added for consistency with the
> `read_crg()` function.
> We already have `bytestream2_skip(GetByteContext *g, unsigned int
> size)` that skips `size`
> bytes from the compressed data. 
> Do you think it is better to replace those functions (= `read_cpf()`
> and `read_crg()`)
> with `bytestream2_skip()`?

read_crg() performs a sanity check on ncomponents so it's not quite the
same. On the other hand this always checks the length of the marker
unlike the main parsing loop which only does so if
strict_std_compliance >= FF_COMPLIANCE_STRICT. I guess keeping
read_cpf() in the patch is fine and is useful for the future

> > >  /* 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 +1053,10 @@ 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_WARNING, "Transformation = 0
> > > (lossy DWT) is found in HTREV HT set\n");
> > > +        if (s->isHT && s->Ccap15_b14_15 != (codsty->cblk_style
> > > >> 6)
> > > && s->Ccap15_b14_15 != HTJ2K_HTONLY)
> > > +            av_log(s->avctx, AV_LOG_WARNING, "SPcod/SPcoc value
> > > does
> > > not match bit 14-15 values of Ccap15\n");
> > 
> > Do you have samples demonstrating the need to accept such broken
> > files?
> > If not then we should probably error out
> 
> Does `error out` mean that
> - Should we exit decoding here?
> - or should we replace AV_LOG_WARNING with AV_LOG_ERROR?

Returning with AVERROR_INVALIDDATA

> > > @@ -2187,22 +2472,42 @@ 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_WARNING, "CAP marker
> > > segment
> > > shall come after SIZ\n");
> > 
> > SHALL -> we should be able to safely reject. Similarly with the
> > other
> > errors. Unless we know of an encoder that produces broken files
> > then
> > there's no reason to be lenient. And if such a broken encoder
> > exists we
> > could try to get it fixed
> 
> Does `safely reject` mean that we should replace AV_LOG_WARNING with
> AV_LOG_ERROR? or we should stop decoding here?

Returning AVERROR_INVALIDDATA since the input is invalid per the spec.
Else we invite having to deal with incredibly broken encoders.

> > > @@ -112,6 +112,13 @@ typedef struct Jpeg2000DecoderContext {
> > >      Jpeg2000Tile    *tile;
> > >      Jpeg2000DSPContext dsp;
> > >  
> > > +    uint8_t         isHT; // HTJ2K?
> > 
> > Isn't it possible to mix Part 1 and HT in the same file? I know
> > HTONLY
> > exists also
> 
> It is possible to mix Part 1 and HT in the same tile-component.
> This mode is defined as MIXED in the spec of Part 15.
> There are three types of HT codestreams:
> - HTONLY
> - HTDECLARED
> - MIXED
> 
> The spec says - 
> "The HTONLY set is the set of HTJ2K codestreams where all code-blocks
> are HT code-blocks. The HTDECLARED set is the set of HTJ2K
> codestreams
> where all code-blocks within a given tile-component are either
> a) HT code-blocks, or b) code-blocks as specified in 
> Rec. ITU-T T.800 | ISO/IEC 15444-1. The MIXED set is the set of all
> HTJ2K codestreams that are not in the HTDECLARED set."

So HTDECLARED allows encoding for example luma as HT and chroma as Part
1?

> > > @@ -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)
> > 
> > Aren't there conformance files to catch these kinds of errors? I
> > remember looked at J2K a while back, and I think we should add such
> > files to FATE
> > 
> 
> Do you mean "errors" are the difference in pixel values between
> uncompressed and lossy compressed images?

Yes

> There are no specific conformance files to catch the difference
> in reconstruction parameter values. 

Bummer

> The value of the reconstruction parameter r is not limited to 1/2.
> We can use other values.
> However, the spec of Part 4 says the use of reconstruction parameter
> r = 1/2 will typically increase the ease of passing.

I see. Does this only applies to lossy J2K? I can't imagine "lossless"
and "different reconstruction" are compatible

/Tomas
Osamu Watanabe June 21, 2024, 12:22 a.m. UTC | #4
> On Jun 19, 2024, at 17:27, Tomas Hardin <git@haerdin.se> wrote:
> 
> ons 2024-06-19 klockan 05:51 +0000 skrev WATANABE Osamu:
>> First of all, I appreciate your kind review.
>> I'm writing to discuss the changes and would like to hear your
>> feedback on these.
>> 
>> 
>>> On Jun 18, 2024, at 23:20, Tomas Hardin <git@haerdin.se> wrote:
>>> 
>>> 
>>> It seems this patch combines a lot of things that might be better
>>> to
>>> split into separate patches for easier review
>> 
>> Agree. I will split this patch into several patches.
>> For example, the set of patches includes changes:
>> - only for HTJ2K (JPEG 2000 Part 15)
>> - only for J2K (JPEG 2000 Part 1)
>> - for both J2K and HTJ2K.
>> 
>> Do you think it makes sense?
> 
> Maybe. Going by the commit message, separate support for placeholder
> passes from CAP from CFP handling perhaps?

Support for placeholder passes is related to both J2K and HTJ2K.
The handling of CAP and CPF is related to only HTJ2K.
I will separate the commit into the above three types.


> 
>>>> @@ -382,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;
>>> 
>>> Could we handle 9 <= precision <= 16 while we're at it?
>>> 
>> 
>> Yes. The native J2K decoder for both lossless and lossy J2K/HTJ2K
>> codestreams can handle bpp from 9 to 16. This change is required to
>> produce the decoded images for the ISO test codestreams defined in
>> Part 4 (Conformance testing).
> 
> Are there any test files for the other precisions?
> 

In ISO test files, the maximum precision is 12 bits.
I think it would be good to add such a file to FATE (in another patch.)


> 
>>>> @@ -802,6 +881,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;
>>>> +}
>>> 
>>> Don't we already have code for skipping markers we don't care
>>> about?
>>> 
>> 
>> The `read_cpf()` function was added for consistency with the
>> `read_crg()` function.
>> We already have `bytestream2_skip(GetByteContext *g, unsigned int
>> size)` that skips `size`
>> bytes from the compressed data. 
>> Do you think it is better to replace those functions (= `read_cpf()`
>> and `read_crg()`)
>> with `bytestream2_skip()`?
> 
> read_crg() performs a sanity check on ncomponents so it's not quite the
> same. On the other hand this always checks the length of the marker
> unlike the main parsing loop which only does so if
> strict_std_compliance >= FF_COMPLIANCE_STRICT. I guess keeping
> read_cpf() in the patch is fine and is useful for the future

I agree. I will keep `read_cpf()` as is. 


> 
>>>>  /* 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 +1053,10 @@ 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_WARNING, "Transformation = 0
>>>> (lossy DWT) is found in HTREV HT set\n");
>>>> +        if (s->isHT && s->Ccap15_b14_15 != (codsty->cblk_style
>>>>>> 6)
>>>> && s->Ccap15_b14_15 != HTJ2K_HTONLY)
>>>> +            av_log(s->avctx, AV_LOG_WARNING, "SPcod/SPcoc value
>>>> does
>>>> not match bit 14-15 values of Ccap15\n");
>>> 
>>> Do you have samples demonstrating the need to accept such broken
>>> files?
>>> If not then we should probably error out
>> 
>> Does `error out` mean that
>> - Should we exit decoding here?
>> - or should we replace AV_LOG_WARNING with AV_LOG_ERROR?
> 
> Returning with AVERROR_INVALIDDATA

OK. I will change those AV_LOG_WARNING into AV_LOG_ERROR.


> 
>>>> @@ -2187,22 +2472,42 @@ 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_WARNING, "CAP marker
>>>> segment
>>>> shall come after SIZ\n");
>>> 
>>> SHALL -> we should be able to safely reject. Similarly with the
>>> other
>>> errors. Unless we know of an encoder that produces broken files
>>> then
>>> there's no reason to be lenient. And if such a broken encoder
>>> exists we
>>> could try to get it fixed
>> 
>> Does `safely reject` mean that we should replace AV_LOG_WARNING with
>> AV_LOG_ERROR? or we should stop decoding here?
> 
> Returning AVERROR_INVALIDDATA since the input is invalid per the spec.
> Else we invite having to deal with incredibly broken encoders.

I agree. Will do.


> 
>>>> @@ -112,6 +112,13 @@ typedef struct Jpeg2000DecoderContext {
>>>>      Jpeg2000Tile    *tile;
>>>>      Jpeg2000DSPContext dsp;
>>>>  
>>>> +    uint8_t         isHT; // HTJ2K?
>>> 
>>> Isn't it possible to mix Part 1 and HT in the same file? I know
>>> HTONLY
>>> exists also
>> 
>> It is possible to mix Part 1 and HT in the same tile-component.
>> This mode is defined as MIXED in the spec of Part 15.
>> There are three types of HT codestreams:
>> - HTONLY
>> - HTDECLARED
>> - MIXED
>> 
>> The spec says - 
>> "The HTONLY set is the set of HTJ2K codestreams where all code-blocks
>> are HT code-blocks. The HTDECLARED set is the set of HTJ2K
>> codestreams
>> where all code-blocks within a given tile-component are either
>> a) HT code-blocks, or b) code-blocks as specified in 
>> Rec. ITU-T T.800 | ISO/IEC 15444-1. The MIXED set is the set of all
>> HTJ2K codestreams that are not in the HTDECLARED set."
> 
> So HTDECLARED allows encoding for example luma as HT and chroma as Part
> 1?

Yes. MIXED is a more relaxed condition that allows the mixing of HT
and non-HT code blocks in the same component.

> 
>>>> @@ -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)
>>> 
>>> Aren't there conformance files to catch these kinds of errors? I
>>> remember looked at J2K a while back, and I think we should add such
>>> files to FATE
>>> 
>> 
>> Do you mean "errors" are the difference in pixel values between
>> uncompressed and lossy compressed images?
> 
> Yes
> 
>> There are no specific conformance files to catch the difference
>> in reconstruction parameter values.
> 
> Bummer
> 
>> The value of the reconstruction parameter r is not limited to 1/2.
>> We can use other values.
>> However, the spec of Part 4 says the use of reconstruction parameter
>> r = 1/2 will typically increase the ease of passing.
> 
> I see. Does this only applies to lossy J2K? I can't imagine "lossless"
> and "different reconstruction" are compatible
> 

No. In J2K Part 1, truncation of coding passes in the losslessly encoded
codestream may happen. In this case, this also applies to lossless J2K.
The current implementation is taking care of this case.

Best,
Osamu

> /Tomas
> _______________________________________________
> 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 d004c08f10..93221d90ca 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
 
@@ -192,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 091931b1ff..2d03107690 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.
@@ -382,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;
         }
     }
 
@@ -408,6 +420,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 +881,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 +1053,10 @@  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_WARNING, "Transformation = 0 (lossy DWT) is found in HTREV HT set\n");
+        if (s->isHT && s->Ccap15_b14_15 != (codsty->cblk_style >> 6) && s->Ccap15_b14_15 != HTJ2K_HTONLY)
+            av_log(s->avctx, AV_LOG_WARNING, "SPcod/SPcoc value does not match bit 14-15 values of Ccap15\n");
         if (ret = ff_jpeg2000_init_component(comp, codsty, qntsty,
                                              s->cbps[compno], s->cdx[compno],
                                              s->cdy[compno], s->avctx))
@@ -1076,100 +1168,293 @@  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) {
-                int zbp = tag_tree_decode(s, prec->zerobits + cblkno, 100);
-                int v = expn[bandno] + numgbits - 1 - zbp;
-
-                if (v < 0 || v > 30) {
-                    av_log(s->avctx, AV_LOG_ERROR,
-                           "nonzerobits %d invalid or unsupported\n", v);
-                    return AVERROR_INVALIDDATA;
+                if (incl) {
+                    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;
+                    }
+                    cblk->incl = 1;
+                    cblk->nonzerobits = v;
+                    cblk->zbp = zbp;
+                    cblk->lblock = 3;
                 }
-                cblk->zbp = zbp;
-                cblk->nonzerobits = v;
-            }
-            if ((newpasses = getnpasses(s)) < 0)
-                return newpasses;
-            av_assert2(newpasses > 0);
-            if (cblk->npasses + newpasses >= JPEG2000_MAX_PASSES) {
-                avpriv_request_sample(s->avctx, "Too many passes");
-                return AVERROR_PATCHWELCOME;
-            }
-            if ((llen = getlblockinc(s)) < 0)
-                return llen;
-            if (cblk->lblock + llen + av_log2(newpasses) > 16) {
-                avpriv_request_sample(s->avctx,
-                                      "Block with length beyond 16 bits");
-                return AVERROR_PATCHWELCOME;
+            } else {
+                incl = get_bits(s, 1);
             }
 
-            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));
-            if (!tmp)
-                return AVERROR(ENOMEM);
-            cblk->data_start = tmp;
-            do {
-                int newpasses1 = 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;
+                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;
+                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_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);
+                }
 
-                while (newpasses1 < newpasses) {
-                    newpasses1 ++;
-                    if (needs_termination(codsty->cblk_style, cblk->npasses + newpasses1 - 1)) {
-                        cblk->nb_terminationsinc ++;
-                        break;
+                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 {
+                        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 (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];
+                // 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 {
-                    if ((ret = get_bits(s, av_log2(newpasses1) + cblk->lblock)) < 0)
-                        return ret;
-                    cblk->pass_lengths[0] = ret;
+                    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;
+                    }
                 }
-                if (ret > cblk->data_allocated) {
-                    size_t new_size = FFMAX(2*cblk->data_allocated, ret);
+
+                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);
@@ -1704,7 +1989,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;
@@ -1918,7 +2203,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);
 
@@ -1926,8 +2211,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;
                 }
 
@@ -1944,11 +2229,11 @@  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],
-                                                           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],
@@ -2187,22 +2472,42 @@  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_WARNING, "CAP marker segment shall come after SIZ\n");
+            }
+            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 +2557,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;
diff --git a/libavcodec/jpeg2000htdec.c b/libavcodec/jpeg2000htdec.c
index eba0936089..beb4a386b0 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,21 +1172,24 @@  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;             // 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 +1203,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
@@ -1190,8 +1219,8 @@  ff_jpeg2000_decode_htj2k(const Jpeg2000DecoderContext *s, Jpeg2000CodingStyle *c
     uint8_t *block_states = NULL;
 
     int32_t n, val;             // Post-processing
-
-    int32_t M_b = magp;
+    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;
     const int quad_buf_height = height + 4;
@@ -1201,22 +1230,17 @@  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));
 
     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
@@ -1231,7 +1255,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);
@@ -1277,31 +1305,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 */