[FFmpeg-devel] [PATCH] JPEG-LS Decoder: Update
Spyros Theoharis
s.theoharis at alma-tech.com
Sat Apr 10 15:33:46 EEST 2021
- 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(-)
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;
}
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);
--
2.7.4
More information about the ffmpeg-devel
mailing list