blob: c582ab02f33bce59f8782807589ad1fa7f5469b8 [file] [log] [blame]
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include "aml_mjpeg_parser.h"
#include "../utils/get_bits.h"
#include "../utils/put_bits.h"
#include "../utils/golomb.h"
#include "../utils/common.h"
#include "utils.h"
/* return the 8 bit start code value and update the search
state. Return -1 if no start code found */
static int find_marker(const u8 **pbuf_ptr, const u8 *buf_end)
{
const u8 *buf_ptr;
u32 v, v2;
int val;
int skipped = 0;
buf_ptr = *pbuf_ptr;
while (buf_end - buf_ptr > 1) {
v = *buf_ptr++;
v2 = *buf_ptr;
if ((v == 0xff) && (v2 >= 0xc0) && (v2 <= 0xfe) && buf_ptr < buf_end) {
val = *buf_ptr++;
goto found;
}
skipped++;
}
buf_ptr = buf_end;
val = -1;
found:
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "find_marker skipped %d bytes\n", skipped);
*pbuf_ptr = buf_ptr;
return val;
}
int ff_mjpeg_find_marker(struct MJpegDecodeContext *s,
const u8 **buf_ptr, const u8 *buf_end,
const u8 **unescaped_buf_ptr,
int *unescaped_buf_size)
{
int start_code;
start_code = find_marker(buf_ptr, buf_end);
/* unescape buffer of SOS, use special treatment for JPEG-LS */
if (start_code == SOS && !s->ls) {
const u8 *src = *buf_ptr;
const u8 *ptr = src;
u8 *dst = s->buffer;
#define copy_data_segment(skip) do { \
int length = (ptr - src) - (skip); \
if (length > 0) { \
memcpy(dst, src, length); \
dst += length; \
src = ptr; \
} \
} while (0)
while (ptr < buf_end) {
u8 x = *(ptr++);
if (x == 0xff) {
int skip = 0;
while (ptr < buf_end && x == 0xff) {
x = *(ptr++);
skip++;
}
/* 0xFF, 0xFF, ... */
if (skip > 1) {
copy_data_segment(skip);
/* decrement src as it is equal to ptr after the
* copy_data_segment macro and we might want to
* copy the current value of x later on */
src--;
}
if (x < 0xd0 || x > 0xd7) {
copy_data_segment(1);
if (x)
break;
}
}
if (src < ptr)
copy_data_segment(0);
}
#undef copy_data_segment
*unescaped_buf_ptr = s->buffer;
*unescaped_buf_size = dst - s->buffer;
memset(s->buffer + *unescaped_buf_size, 0,
AV_INPUT_BUFFER_PADDING_SIZE);
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "escaping removed %d bytes\n",
(int)((buf_end - *buf_ptr) - (dst - s->buffer)));
} else if (start_code == SOS && s->ls) {
const u8 *src = *buf_ptr;
u8 *dst = s->buffer;
int bit_count = 0;
int t = 0, b = 0;
struct put_bits_context pb;
/* find marker */
while (src + t < buf_end) {
u8 x = src[t++];
if (x == 0xff) {
while ((src + t < buf_end) && x == 0xff)
x = src[t++];
if (x & 0x80) {
t -= FFMIN(2, t);
break;
}
}
}
bit_count = t * 8;
init_put_bits(&pb, dst, t);
/* unescape bitstream */
while (b < t) {
u8 x = src[b++];
put_bits(&pb, 8, x);
if (x == 0xFF && b < t) {
x = src[b++];
if (x & 0x80) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid escape sequence\n");
x &= 0x7f;
}
put_bits(&pb, 7, x);
bit_count--;
}
}
flush_put_bits(&pb);
*unescaped_buf_ptr = dst;
*unescaped_buf_size = (bit_count + 7) >> 3;
memset(s->buffer + *unescaped_buf_size, 0,
AV_INPUT_BUFFER_PADDING_SIZE);
} else {
*unescaped_buf_ptr = *buf_ptr;
*unescaped_buf_size = buf_end - *buf_ptr;
}
return start_code;
}
int ff_mjpeg_decode_sof(struct MJpegDecodeContext *s)
{
int len, nb_components, i, width, height, bits, size_change;
int h_count[MAX_COMPONENTS] = { 0 };
int v_count[MAX_COMPONENTS] = { 0 };
s->cur_scan = 0;
memset(s->upscale_h, 0, sizeof(s->upscale_h));
memset(s->upscale_v, 0, sizeof(s->upscale_v));
/* XXX: verify len field validity */
len = get_bits(&s->gb, 16);
bits = get_bits(&s->gb, 8);
if (bits > 16 || bits < 1) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "bits %d is invalid\n", bits);
return -1;
}
height = get_bits(&s->gb, 16);
width = get_bits(&s->gb, 16);
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "sof0: picture: %dx%d\n", width, height);
nb_components = get_bits(&s->gb, 8);
if (nb_components <= 0 ||
nb_components > MAX_COMPONENTS)
return -1;
s->nb_components = nb_components;
s->h_max = 1;
s->v_max = 1;
for (i = 0; i < nb_components; i++) {
/* component id */
s->component_id[i] = get_bits(&s->gb, 8) - 1;
h_count[i] = get_bits(&s->gb, 4);
v_count[i] = get_bits(&s->gb, 4);
/* compute hmax and vmax (only used in interleaved case) */
if (h_count[i] > s->h_max)
s->h_max = h_count[i];
if (v_count[i] > s->v_max)
s->v_max = v_count[i];
s->quant_index[i] = get_bits(&s->gb, 8);
if (s->quant_index[i] >= 4) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "quant_index is invalid\n");
return -1;
}
if (!h_count[i] || !v_count[i]) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "Invalid sampling factor in component %d %d:%d\n",
i, h_count[i], v_count[i]);
return -1;
}
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "component %d %d:%d id: %d quant:%d\n",
i, h_count[i], v_count[i],
s->component_id[i], s->quant_index[i]);
}
if (nb_components == 4
&& s->component_id[0] == 'C' - 1
&& s->component_id[1] == 'M' - 1
&& s->component_id[2] == 'Y' - 1
&& s->component_id[3] == 'K' - 1)
s->adobe_transform = 0;
/* if different size, realloc/alloc picture */
if (width != s->width || height != s->height || bits != s->bits ||
memcmp(s->h_count, h_count, sizeof(h_count)) ||
memcmp(s->v_count, v_count, sizeof(v_count))) {
size_change = 1;
s->width = width;
s->height = height;
s->bits = bits;
memcpy(s->h_count, h_count, sizeof(h_count));
memcpy(s->v_count, v_count, sizeof(v_count));
s->interlaced = 0;
s->got_picture = 0;
} else {
size_change = 0;
}
return 0;
}
static int ff_mjpeg_decode_frame(u8 *buf, int buf_size, struct MJpegDecodeContext *s)
{
const u8 *buf_end, *buf_ptr;
const u8 *unescaped_buf_ptr;
int unescaped_buf_size;
int start_code;
int ret = 0;
buf_ptr = buf;
buf_end = buf + buf_size;
while (buf_ptr < buf_end) {
/* find start next marker */
start_code = ff_mjpeg_find_marker(s, &buf_ptr, buf_end,
&unescaped_buf_ptr,
&unescaped_buf_size);
/* EOF */
if (start_code < 0) {
break;
} else if (unescaped_buf_size > INT_MAX / 8) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "MJPEG packet 0x%x too big (%d/%d), corrupt data?\n",
start_code, unescaped_buf_size, buf_size);
return -1;
}
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "marker=%x avail_size_in_buf=%d\n",
start_code, (int)(buf_end - buf_ptr));
ret = init_get_bits8(&s->gb, unescaped_buf_ptr, unescaped_buf_size);
if (ret < 0) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "invalid buffer\n");
goto fail;
}
s->start_code = start_code;
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "startcode: %X\n", start_code);
switch (start_code) {
case SOF0:
case SOF1:
case SOF2:
case SOF3:
case SOF48:
case SOI:
case SOS:
case EOI:
break;
default:
goto skip;
}
switch (start_code) {
case SOI:
s->restart_interval = 0;
s->restart_count = 0;
s->raw_image_buffer = buf_ptr;
s->raw_image_buffer_size = buf_end - buf_ptr;
/* nothing to do on SOI */
break;
case SOF0:
case SOF1:
if (start_code == SOF0)
s->profile = FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT;
else
s->profile = FF_PROFILE_MJPEG_HUFFMAN_EXTENDED_SEQUENTIAL_DCT;
s->lossless = 0;
s->ls = 0;
s->progressive = 0;
if ((ret = ff_mjpeg_decode_sof(s)) < 0)
goto fail;
break;
case SOF2:
s->profile = FF_PROFILE_MJPEG_HUFFMAN_PROGRESSIVE_DCT;
s->lossless = 0;
s->ls = 0;
s->progressive = 1;
if ((ret = ff_mjpeg_decode_sof(s)) < 0)
goto fail;
break;
case SOF3:
s->profile = FF_PROFILE_MJPEG_HUFFMAN_LOSSLESS;
s->properties |= FF_CODEC_PROPERTY_LOSSLESS;
s->lossless = 1;
s->ls = 0;
s->progressive = 0;
if ((ret = ff_mjpeg_decode_sof(s)) < 0)
goto fail;
break;
case SOF48:
s->profile = FF_PROFILE_MJPEG_JPEG_LS;
s->properties |= FF_CODEC_PROPERTY_LOSSLESS;
s->lossless = 1;
s->ls = 1;
s->progressive = 0;
if ((ret = ff_mjpeg_decode_sof(s)) < 0)
goto fail;
break;
case EOI:
goto the_end;
case DHT:
case LSE:
case SOS:
case DRI:
case SOF5:
case SOF6:
case SOF7:
case SOF9:
case SOF10:
case SOF11:
case SOF13:
case SOF14:
case SOF15:
case JPG:
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "mjpeg: unsupported coding type (%x)\n", start_code);
break;
}
skip:
/* eof process start code */
buf_ptr += (get_bits_count(&s->gb) + 7) / 8;
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "marker parser used %d bytes (%d bits)\n",
(get_bits_count(&s->gb) + 7) / 8, get_bits_count(&s->gb));
}
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "No JPEG data found in image\n");
return -1;
fail:
s->got_picture = 0;
return ret;
the_end:
v4l_dbg(0, V4L_DEBUG_CODEC_PARSER, "decode frame unused %d bytes\n", (int)(buf_end - buf_ptr));
return 0;
}
int mjpeg_decode_extradata_ps(u8 *buf, int size, struct mjpeg_param_sets *ps)
{
int ret;
ps->head_parsed = false;
ps->dec_ps.buf_size = size;
ps->dec_ps.buffer = vzalloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!ps->dec_ps.buffer)
return -1;
ret = ff_mjpeg_decode_frame(buf, size, &ps->dec_ps);
if (ret) {
v4l_dbg(0, V4L_DEBUG_CODEC_ERROR, "parse extra data failed. err: %d\n", ret);
vfree(ps->dec_ps.buffer);
return ret;
}
if (ps->dec_ps.width && ps->dec_ps.height)
ps->head_parsed = true;
vfree(ps->dec_ps.buffer);
return 0;
}