diff mbox series

[FFmpeg-devel] JPEG-LS Decoder: Update

Message ID 000001d72e05$c334ca90$499e5fb0$@alma-tech.com
State New
Headers show
Series [FFmpeg-devel] JPEG-LS Decoder: Update
Related show

Checks

Context Check Description
andriy/configure warning Failed to apply patch
andriy/configure warning Failed to apply patch

Commit Message

Spyros Theoharis April 10, 2021, 12:33 p.m. UTC
- 8 up to 16 bits
- 444/422/420/411
- fixed restart mechanism support
- RGB/YUV 444 images (via -rgb444 input option)
---
 libavcodec/jpegls.c    |   5 +
 libavcodec/jpegls.h    |   2 +-
 libavcodec/jpeglsdec.c | 268
+++++++++++++++++++++++++++++++++----------------
 libavcodec/mjpegdec.c  |  77 ++++++++++----
 4 files changed, 244 insertions(+), 108 deletions(-)

     if (len != 8 + 3 * nb_components) {
@@ -399,7 +398,7 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
         && s->component_id[3] == 'K' - 1)
         s->adobe_transform = 0;
 
-    if (s->ls && (s->h_max > 1 || s->v_max > 1)) {
+    if (s->ls && (s->h_max > 4 || s->v_max > 2)) {
         avpriv_report_missing_feature(s->avctx, "Subsampling in JPEG-LS");
         return AVERROR_PATCHWELCOME;
     }
@@ -469,9 +468,9 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
             return AVERROR_INVALIDDATA;
         }
     } else {
-        if (s->v_max == 1 && s->h_max == 1 && s->lossless==1 &&
(nb_components==3 || nb_components==4))
-            s->rgb = 1;
-        else if (!s->lossless)
+        if (s->v_max == 1 && s->h_max == 1 && s->lossless==1 &&
(nb_components==3 || nb_components==4)) {
+            if (!s->ls) s->rgb = 1;
+        } else if (!s->lossless)
             s->rgb = 0;
         /* XXX: not complete test ! */
         pix_fmt_id = ((unsigned)s->h_count[0] << 28) | (s->v_count[0] <<
24) |
@@ -677,16 +676,51 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
             memset(s->upscale_h, 0, sizeof(s->upscale_h));
             memset(s->upscale_v, 0, sizeof(s->upscale_v));
             if (s->nb_components == 3) {
-                s->avctx->pix_fmt = AV_PIX_FMT_RGB24;
+                switch (pix_fmt_id) {
+                    case 0x11111100: /* 444 */
+                               if (s->bits <=  8)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV444P;
+                        else   if (s->bits <= 10)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV444P10;
+                        else   if (s->bits <= 12)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV444P12;
+                        else   if (s->bits <= 14)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV444P14;
+                        else /*if (s->bits <= 16)*/ s->avctx->pix_fmt =
AV_PIX_FMT_YUV444P16;
+
+                        if (s->rgb) s->avctx->pix_fmt = s->bits <= 8 ?
AV_PIX_FMT_RGB24 : AV_PIX_FMT_RGB48;
+
+                        break;
+                    case 0x21111100: /* 422 */
+                               if (s->bits <=  8)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV422P;
+                        else   if (s->bits <= 10)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV422P10;
+                        else   if (s->bits <= 12)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV422P12;
+                        else   if (s->bits <= 14)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV422P14;
+                        else /*if (s->bits <= 16)*/ s->avctx->pix_fmt =
AV_PIX_FMT_YUV422P16;
+                        break;
+                    case 0x41111100: /* 411 */
+                               if (s->bits <=  8)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV411P;
+                        else goto unk_pixfmt;
+                        break;
+                    case 0x22111100: /* 420 */
+                               if (s->bits <=  8)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV420P;
+                        else   if (s->bits <= 10)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV420P10;
+                        else   if (s->bits <= 12)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV420P12;
+                        else   if (s->bits <= 14)   s->avctx->pix_fmt =
AV_PIX_FMT_YUV420P14;
+                        else /*if (s->bits <= 16)*/ s->avctx->pix_fmt =
AV_PIX_FMT_YUV420P16;
+                        break;
+                    default:
+                        goto unk_pixfmt;
+                        break;
+                }
             } else if (s->nb_components != 1) {
                 av_log(s->avctx, AV_LOG_ERROR, "Unsupported number of
components %d\n", s->nb_components);
                 return AVERROR_PATCHWELCOME;
             } else if (s->palette_index && s->bits <= 8)
                 s->avctx->pix_fmt = AV_PIX_FMT_PAL8;
-            else if (s->bits <= 8)
-                s->avctx->pix_fmt = AV_PIX_FMT_GRAY8;
-            else
-                s->avctx->pix_fmt = AV_PIX_FMT_GRAY16;
+            else { /* Grayscale */
+                       if (s->bits <=  8)   s->avctx->pix_fmt =
AV_PIX_FMT_GRAY8;
+                else   if (s->bits <= 10)   s->avctx->pix_fmt =
AV_PIX_FMT_GRAY10;
+                else   if (s->bits <= 12)   s->avctx->pix_fmt =
AV_PIX_FMT_GRAY12;
+                else   if (s->bits <= 14)   s->avctx->pix_fmt =
AV_PIX_FMT_GRAY14;
+                else /*if (s->bits <= 16)*/ s->avctx->pix_fmt =
AV_PIX_FMT_GRAY16;
+            }
         }
 
         s->pix_desc = av_pix_fmt_desc_get(s->avctx->pix_fmt);
@@ -740,7 +774,6 @@ int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
     }
 
     if ((s->rgb && !s->lossless && !s->ls) ||
-        (!s->rgb && s->ls && s->nb_components > 1) ||
         (s->avctx->pix_fmt == AV_PIX_FMT_PAL8 && !s->ls)
     ) {
         av_log(s->avctx, AV_LOG_ERROR, "Unsupported coding and pixel format
combination\n");
@@ -2275,7 +2308,7 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s,
             if (x == 0xff) {
                 while ((src + t < buf_end) && x == 0xff)
                     x = src[t++];
-                if (x & 0x80) {
+                if (x == SOS || x == EOI) {
                     t -= FFMIN(2, t);
                     break;
                 }
@@ -2288,14 +2321,18 @@ int ff_mjpeg_find_marker(MJpegDecodeContext *s,
         while (b < t) {
             uint8_t x = src[b++];
             put_bits(&pb, 8, x);
-            if (x == 0xFF && b < t) {
+            if (x == 0xff && b < t) {
                 x = src[b++];
-                if (x & 0x80) {
-                    av_log(s->avctx, AV_LOG_WARNING, "Invalid escape
sequence\n");
-                    x &= 0x7f;
+                if (x >= RST0 && x <= RST7) {
+                   put_bits(&pb, 8, x);
+                } else {
+                   if (x & 0x80) {
+                       av_log(s->avctx, AV_LOG_WARNING, "Invalid escape
sequence\n");
+                       x &= 0x7f;
+                   }
+                   put_bits(&pb, 7, x);
+                   bit_count--;
                 }
-                put_bits(&pb, 7, x);
-                bit_count--;
             }
         }
         flush_put_bits(&pb);

Comments

Carl Eugen Hoyos April 10, 2021, 3:01 p.m. UTC | #1
Am Sa., 10. Apr. 2021 um 14:35 Uhr schrieb Spyros Theoharis
<s.theoharis@alma-tech.com>:
>
> - 8 up to 16 bits
> - 444/422/420/411
> - fixed restart mechanism support
> - RGB/YUV 444 images (via -rgb444 input option)

This typically indicates that you should send four patches
instead of one.

How can we create samples / please provide samples.

[...]

> + * Skip bitstream for the next restart marker
> + */

Was this completely missing so far or what is the
reason for this change?

> +static int exp_mk = 0;

You cannot use a static variable, add it to the context instead.

> +static inline int ls_decode_skip_restart_marker(MJpegDecodeContext *s, int
> init)
> +{
> +    int mk = 0, t = 0;
> +
> +    if (init) exp_mk = 0;
> +
> +    while (mk != 0xff && t++ < 16) { /* skip 0xFF */
> +        mk = (mk<<1) + get_bits1(&s->gb);
> +    }
> +    mk  &= 0xff;
> +    mk <<= 8;
> +    mk  += get_bits(&s->gb, 8); /* skip RSTn */
> +

> +    if (mk != 0xffd0 + (exp_mk % 8)) {

Use "&" instead of "%".

> +        av_log(s->avctx, AV_LOG_WARNING, "ERROR: Invalid restart marker
> 0x%.4X, expected is 0x%.4X\n", mk, 0xffd0 + (exp_mk % 8));

This line does not make sense...

Please split your patch and resend, Carl Eugen
diff mbox series

Patch

diff --git a/libavcodec/jpegls.c b/libavcodec/jpegls.c
index 7f9fa8d..2b4f601 100644
--- a/libavcodec/jpegls.c
+++ b/libavcodec/jpegls.c
@@ -45,7 +45,12 @@  void ff_jpegls_init_state(JLSState *state)
     for (i = 0; i < 367; i++) {
         state->A[i] = FFMAX(state->range + 32 >> 6, 2);
         state->N[i] = 1;
+        state->B[i] =
+        state->C[i] = 0;
     }
+    for (i = 0; i < 4; i++) {
+        state->run_index[i] = 0;
+   }
 }
 
 /**
diff --git a/libavcodec/jpegls.h b/libavcodec/jpegls.h
index aac67bb..e9ca664 100644
--- a/libavcodec/jpegls.h
+++ b/libavcodec/jpegls.h
@@ -36,7 +36,7 @@ 
 
 typedef struct JLSState {
     int T1, T2, T3;
-    int A[367], B[367], C[365], N[367];
+    int A[367], B[367], C[367], N[367];
     int limit, reset, bpp, qbpp, maxval, range;
     int near, twonear;
     int run_index[4];
diff --git a/libavcodec/jpeglsdec.c b/libavcodec/jpeglsdec.c
index 69980ea..6d4b53e 100644
--- a/libavcodec/jpeglsdec.c
+++ b/libavcodec/jpeglsdec.c
@@ -25,6 +25,7 @@ 
  * JPEG-LS decoder.
  */
 
+#include "libavutil/opt.h"
 #include "avcodec.h"
 #include "get_bits.h"
 #include "golomb.h"
@@ -220,6 +221,33 @@  static inline int ls_get_code_runterm(GetBitContext
*gb, JLSState *state,
 }
 
 /**
+ * Skip bitstream for the next restart marker
+ */
+static int exp_mk = 0;
+static inline int ls_decode_skip_restart_marker(MJpegDecodeContext *s, int
init)
+{
+    int mk = 0, t = 0;
+
+    if (init) exp_mk = 0;
+
+    while (mk != 0xff && t++ < 16) { /* skip 0xFF */
+        mk = (mk<<1) + get_bits1(&s->gb);
+    }
+    mk  &= 0xff;
+    mk <<= 8;
+    mk  += get_bits(&s->gb, 8); /* skip RSTn */
+
+    if (mk != 0xffd0 + (exp_mk % 8)) {
+        av_log(s->avctx, AV_LOG_WARNING, "ERROR: Invalid restart marker
0x%.4X, expected is 0x%.4X\n", mk, 0xffd0 + (exp_mk % 8));
+        return -1;
+    }
+
+    exp_mk++;
+
+    return mk;
+}
+
+/**
  * Decode one line of image
  */
 static inline int ls_decode_line(JLSState *state, MJpegDecodeContext *s,
@@ -348,17 +376,18 @@  static inline int ls_decode_line(JLSState *state,
MJpegDecodeContext *s,
 int ff_jpegls_decode_picture(MJpegDecodeContext *s, int near,
                              int point_transform, int ilv)
 {
-    int i, t = 0;
-    uint8_t *zero, *last, *cur;
+    int i, m, cmp = 0;
+    int Rc[3] = { 0, 0, 0 };
+    int x, w, is_last_cmp;
+
+    uint8_t *zero, *last[3], *cur[3];
     JLSState *state;
-    int off = 0, stride = 1, width, shift, ret = 0;
+    int shift, ret = 0;
     int decoded_height = 0;
 
     zero = av_mallocz(s->picture_ptr->linesize[0]);
     if (!zero)
         return AVERROR(ENOMEM);
-    last = zero;
-    cur  = s->picture_ptr->data[0];
 
     state = av_mallocz(sizeof(JLSState));
     if (!state) {
@@ -376,12 +405,9 @@  int ff_jpegls_decode_picture(MJpegDecodeContext *s, int
near,
     ff_jpegls_reset_coding_parameters(state, 0);
     ff_jpegls_init_state(state);
 
-    if (s->bits <= 8)
-        shift = point_transform + (8 - s->bits);
-    else
-        shift = point_transform + (16 - s->bits);
+    shift = point_transform;
 
-    if (shift >= 16) {
+    if (shift > s->bits) {
         ret = AVERROR_INVALIDDATA;
         goto end;
     }
@@ -400,59 +426,106 @@  int ff_jpegls_decode_picture(MJpegDecodeContext *s,
int near,
         ret = AVERROR_INVALIDDATA;
         goto end;
     }
+
+    s->restart_count = s->restart_interval;
+
     if (ilv == 0) { /* separate planes */
+        int init_RSTn = 1;
         if (s->cur_scan > s->nb_components) {
             ret = AVERROR_INVALIDDATA;
             goto end;
         }
-        stride = (s->nb_components > 1) ? 3 : 1;
-        off    = av_clip(s->cur_scan - 1, 0, stride - 1);
-        width  = s->width * stride;
-        cur   += off;
+        cmp       = av_clip(s->cur_scan - 1, 0, s->nb_components - 1);
+        cur[cmp]  = s->picture_ptr->data[cmp];
+        last[cmp] = zero;
         for (i = 0; i < s->height; i++) {
-            int ret;
-            if (s->bits <= 8) {
-                ret = ls_decode_line(state, s, last, cur, t, width, stride,
off, 8);
-                t = last[0];
-            } else {
-                ret = ls_decode_line(state, s, last, cur, t, width, stride,
off, 16);
-                t = *((uint16_t *)last);
-            }
+            ret     = ls_decode_line(state, s, last[cmp], cur[cmp],
Rc[cmp], s->width, 1, cmp, s->bits);
+            Rc[cmp] = (s->bits <= 8 ? last[cmp][0] : ((uint16_t
*)(last[cmp]))[0]);
             if (ret < 0)
                 break;
-            last = cur;
-            cur += s->picture_ptr->linesize[0];
+            last[cmp] = cur[cmp];
+            cur[cmp] += s->picture_ptr->linesize[cmp];
 
-            if (s->restart_interval && !--s->restart_count) {
-                align_get_bits(&s->gb);
-                skip_bits(&s->gb, 16); /* skip RSTn */
+            if (s->restart_interval && !--s->restart_count && i !=
s->height - 1) {
+                ret = ls_decode_skip_restart_marker(s, init_RSTn);
+                if (ret < 0)
+                    break;
+
+                /* Reset decoder, skip RSTn */
+                s->restart_count = s->restart_interval;
+                ff_jpegls_init_state(state);
+                init_RSTn = 0;
+                last[cmp] = zero;
+                Rc[cmp]   = 0;
             }
         }
         decoded_height = i;
     } else if (ilv == 1) { /* line interleaving */
-        int j;
-        int Rc[3] = { 0, 0, 0 };
-        stride = (s->nb_components > 1) ? 3 : 1;
-        memset(cur, 0, s->picture_ptr->linesize[0]);
-        width = s->width * stride;
-        for (i = 0; i < s->height; i++) {
-            int ret;
-            for (j = 0; j < stride; j++) {
-                ret = ls_decode_line(state, s, last + j, cur + j,
-                               Rc[j], width, stride, j, 8);
+        int init_RSTn = 1;
+        if (s->rgb && s->nb_components == 3 && s->v_max == 1 && s->h_max ==
1) /* RGB 444 */
+        {
+            cur[0]  = s->picture_ptr->data[0];
+            last[0] = zero;
+
+            for (i = 0; i < s->height; i++) {
+                for (cmp = 0; cmp < s->nb_components; cmp++) {
+                    ret = ls_decode_line(state, s, last[0] + cmp, cur[0] +
cmp, Rc[cmp], s->width * s->nb_components, s->nb_components, cmp, s->bits);
+                    if (ret < 0)
+                        break;
+                    Rc[cmp] = (s->bits <= 8 ? last[0][cmp] : ((uint16_t
*)(last[0]))[cmp]);
+                }
+                if (ret < 0)
+                    break;
+                last[0] = cur[0];
+                cur[0] += s->picture_ptr->linesize[0];
+
+                if (s->restart_interval && !--s->restart_count && i !=
s->height - 1) {
+                    ret = ls_decode_skip_restart_marker(s, init_RSTn);
+                    if (ret < 0)
+                        break;
+
+                    /* Reset decoder, skip RSTn */
+                    s->restart_count = s->restart_interval;
+                    ff_jpegls_init_state(state);
+                    init_RSTn = 0;
+                    last[0]   = zero;
+                    Rc[0]     = 0;
+                }
+            }
+        } else { /* YUV 444, 422, 420, 411 */
+            for (cmp = 0; cmp < s->nb_components; cmp++) {
+                cur[cmp]  = s->picture_ptr->data[cmp];
+                last[cmp] = zero;
+            }
+            for (i = 0; i < s->height; i += s->v_max) {
+                for (cmp = 0; cmp < s->nb_components; cmp++) {
+                    for (m = 0; m < (cmp ? 1 : s->v_max) ; m++) {
+                        ret   = ls_decode_line(state, s, last[cmp],
cur[cmp], Rc[cmp], s->width / (cmp ? s->h_max : 1), 1, cmp, s->bits);
+                        Rc[cmp] = (s->bits <= 8 ? last[cmp][0] : ((uint16_t
*)(last[cmp]))[0]);
+                        if (ret < 0)
+                            break;
+                        last[cmp] = cur[cmp];
+                        cur[cmp] += s->picture_ptr->linesize[cmp];
+                    }
+                }
                 if (ret < 0)
                     break;
-                Rc[j] = last[j];
 
-                if (s->restart_interval && !--s->restart_count) {
-                    align_get_bits(&s->gb);
-                    skip_bits(&s->gb, 16); /* skip RSTn */
+                if (s->restart_interval && !--s->restart_count && i !=
s->height - s->v_max) {
+                    ret = ls_decode_skip_restart_marker(s, init_RSTn);
+                    if (ret < 0)
+                        break;
+
+                    /* Reset decoder, skip RSTn */
+                    s->restart_count = s->restart_interval;
+                    ff_jpegls_init_state(state);
+                    init_RSTn = 0;
+                    for (cmp = 0; cmp < s->nb_components; cmp++) {
+                        Rc[cmp]   = 0;
+                        last[cmp] = zero;
+                    }
                 }
             }
-            if (ret < 0)
-                break;
-            last = cur;
-            cur += s->picture_ptr->linesize[0];
         }
         decoded_height = i;
     } else if (ilv == 2) { /* sample interleaving */
@@ -465,73 +538,78 @@  int ff_jpegls_decode_picture(MJpegDecodeContext *s,
int near,
         goto end;
     }
 
-    if (s->xfrm && s->nb_components == 3) {
-        int x, w;
-
-        w = s->width * s->nb_components;
+    w   = s->width;
+    cmp = (ilv == 0 ? av_clip(s->cur_scan, 0, s->nb_components) :
s->nb_components); /* end component */
+    is_last_cmp = (cmp == 3 || s->nb_components == 1 ? 1 : 0);
 
+    if (s->xfrm && s->nb_components == 3 && is_last_cmp) {
         if (s->bits <= 8) {
-            uint8_t *src = s->picture_ptr->data[0];
+            uint8_t *src[3];
+            src[0] = s->picture_ptr->data[0];
+            src[1] = s->picture_ptr->data[1];
+            src[2] = s->picture_ptr->data[2];
 
-            for (i = 0; i < s->height; i++) {
+            for (i = 0; i < decoded_height; i++) {
                 switch(s->xfrm) {
                 case 1:
-                    for (x = off; x < w; x += 3) {
-                        src[x  ] += src[x+1] + 128;
-                        src[x+2] += src[x+1] + 128;
+                    for (x = 0; x < w; x++) {
+                        src[0][x] += src[1][x] + 128;
+                        src[2][x] += src[1][x] + 128;
                     }
                     break;
                 case 2:
-                    for (x = off; x < w; x += 3) {
-                        src[x  ] += src[x+1] + 128;
-                        src[x+2] += ((src[x  ] + src[x+1])>>1) + 128;
+                    for (x = 0; x < w; x++) {
+                        src[0][x] += src[1][x] + 128;
+                        src[2][x] += ((src[0][x] + src[1][x])>>1) + 128;
                     }
                     break;
                 case 3:
-                    for (x = off; x < w; x += 3) {
-                        int g = src[x+0] - ((src[x+2]+src[x+1])>>2) + 64;
-                        src[x+0] = src[x+2] + g + 128;
-                        src[x+2] = src[x+1] + g + 128;
-                        src[x+1] = g;
+                    for (x = 0; x < w; x++) {
+                        int g = src[0][x] - ((src[2][x]+src[1][x])>>2) +
64;
+                        src[0][x] = src[2][x] + g + 128;
+                        src[2][x] = src[1][x] + g + 128;
+                        src[1][x] = g;
                     }
                     break;
                 case 4:
-                    for (x = off; x < w; x += 3) {
-                        int r    = src[x+0] - ((                       359
* (src[x+2]-128) + 490) >> 8);
-                        int g    = src[x+0] - (( 88 * (src[x+1]-128) - 183
* (src[x+2]-128) +  30) >> 8);
-                        int b    = src[x+0] + ((454 * (src[x+1]-128)
+ 574) >> 8);
-                        src[x+0] = av_clip_uint8(r);
-                        src[x+1] = av_clip_uint8(g);
-                        src[x+2] = av_clip_uint8(b);
+                    for (x = 0; x < w; x++) {
+                        int r    = src[0][x] - ((
359 * (src[2][x]-128) + 490) >> 8);
+                        int g    = src[0][x] - (( 88 * (src[1][x]-128) -
183 * (src[2][x]-128) +  30) >> 8);
+                        int b    = src[0][x] + ((454 * (src[1][x]-128)
+ 574) >> 8);
+                        src[0][x] = av_clip_uint8(r);
+                        src[1][x] = av_clip_uint8(g);
+                        src[2][x] = av_clip_uint8(b);
                     }
                     break;
                 }
-                src += s->picture_ptr->linesize[0];
+                src[0] += s->picture_ptr->linesize[0];
+                src[1] += s->picture_ptr->linesize[1];
+                src[2] += s->picture_ptr->linesize[2];
             }
-        }else
+        } else
             avpriv_report_missing_feature(s->avctx, "16bit xfrm");
     }
 
-    if (shift) { /* we need to do point transform or normalize samples */
-        int x, w;
+    if (shift && is_last_cmp) { /* we need to do point transform or
normalize samples */
 
-        w = s->width * s->nb_components;
 
-        if (s->bits <= 8) {
-            uint8_t *src = s->picture_ptr->data[0];
+        for (cmp = 0; cmp < s->nb_components; cmp++) {
+            if (s->bits <= 8) {
+                uint8_t *src = s->picture_ptr->data[cmp];
 
-            for (i = 0; i < decoded_height; i++) {
-                for (x = off; x < w; x += stride)
-                    src[x] <<= shift;
-                src += s->picture_ptr->linesize[0];
-            }
-        } else {
-            uint16_t *src = (uint16_t *)s->picture_ptr->data[0];
+                for (i = 0; i < decoded_height / (cmp ? s->v_max : 1); i++)
{
+                    for (x = 0; x < w / (cmp ? s->h_max : 1); x++)
+                        src[x] <<= shift;
+                    src += s->picture_ptr->linesize[cmp];
+                }
+            } else {
+                uint16_t *src = (uint16_t *)s->picture_ptr->data[cmp];
 
-            for (i = 0; i < decoded_height; i++) {
-                for (x = 0; x < w; x++)
-                    src[x] <<= shift;
-                src += s->picture_ptr->linesize[0] / 2;
+                for (i = 0; i < decoded_height / (cmp ? s->v_max : 1); i++)
{
+                    for (x = 0; x < w / (cmp ? s->h_max : 1); x++)
+                        src[x] <<= shift;
+                    src += s->picture_ptr->linesize[cmp] / 2;
+                }
             }
         }
     }
@@ -543,12 +621,28 @@  end:
     return ret;
 }
 
+#define OFFSET(x) offsetof(MJpegDecodeContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    { "rgb444", "Are RGB 444 JPEG-LS input images?",
+      OFFSET(rgb), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, VD },
+    { NULL },
+};
+
+static const AVClass jpeglsdec_class = {
+    .class_name = "JPEG-LS decoder",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
 AVCodec ff_jpegls_decoder = {
     .name           = "jpegls",
     .long_name      = NULL_IF_CONFIG_SMALL("JPEG-LS"),
     .type           = AVMEDIA_TYPE_VIDEO,
     .id             = AV_CODEC_ID_JPEGLS,
     .priv_data_size = sizeof(MJpegDecodeContext),
+    .priv_class     = &jpeglsdec_class,
     .init           = ff_mjpeg_decode_init,
     .close          = ff_mjpeg_decode_end,
     .receive_frame  = ff_mjpeg_receive_frame,
diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c
index f3d9e99..952c5c4 100644
--- a/libavcodec/mjpegdec.c
+++ b/libavcodec/mjpegdec.c
@@ -352,10 +352,9 @@  int ff_mjpeg_decode_sof(MJpegDecodeContext *s)
             return AVERROR_INVALIDDATA;
         }
     }
-    if (s->ls && !(bits <= 8 || nb_components == 1)) {
+    if (s->ls && (bits < 8 || bits > 16)) {
         avpriv_report_missing_feature(s->avctx,
-                                      "JPEG-LS that is not <= 8 "
-                                      "bits/component or 16-bit gray");
+                                      "JPEG-LS that is < 8 or > 16
bits/component");
         return AVERROR_PATCHWELCOME;
     }