9c7fb608 |
/*
* LOCO codec
* Copyright (c) 2005 Konstantin Shishkov
* |
b78e7197 |
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or |
9c7fb608 |
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either |
b78e7197 |
* version 2.1 of the License, or (at your option) any later version. |
9c7fb608 |
* |
b78e7197 |
* FFmpeg is distributed in the hope that it will be useful, |
9c7fb608 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public |
b78e7197 |
* License along with FFmpeg; if not, write to the Free Software |
5509bffa |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
9c7fb608 |
*/ |
115329f1 |
|
9c7fb608 |
/** |
ba87f080 |
* @file |
9c7fb608 |
* LOCO codec.
*/ |
115329f1 |
|
9c7fb608 |
#include "avcodec.h" |
9106a698 |
#include "get_bits.h" |
9c7fb608 |
#include "golomb.h" |
594d4d5d |
#include "internal.h" |
199436b9 |
#include "mathops.h" |
9c7fb608 |
|
b627c301 |
enum LOCO_MODE {
LOCO_UNKN = 0,
LOCO_CYUY2 = -1,
LOCO_CRGB = -2,
LOCO_CRGBA = -3,
LOCO_CYV12 = -4,
LOCO_YUY2 = 1,
LOCO_UYVY = 2,
LOCO_RGB = 3,
LOCO_RGBA = 4,
LOCO_YV12 = 5,
}; |
9c7fb608 |
|
b627c301 |
typedef struct LOCOContext { |
9c7fb608 |
AVCodecContext *avctx;
int lossy;
int mode;
} LOCOContext;
|
b627c301 |
typedef struct RICEContext { |
9c7fb608 |
GetBitContext gb;
int save, run, run2; /* internal rice decoder state */
int sum, count; /* sum and count for getting rice parameter */ |
42ebf67d |
int lossy; |
b627c301 |
} RICEContext; |
9c7fb608 |
static int loco_get_rice_param(RICEContext *r)
{
int cnt = 0;
int val = r->count; |
115329f1 |
|
b627c301 |
while (r->sum > val && cnt < 9) { |
9c7fb608 |
val <<= 1;
cnt++;
} |
115329f1 |
|
9c7fb608 |
return cnt;
}
static inline void loco_update_rice_param(RICEContext *r, int val)
{
r->sum += val;
r->count++; |
115329f1 |
|
b627c301 |
if (r->count == 16) {
r->sum >>= 1; |
9c7fb608 |
r->count >>= 1;
}
}
static inline int loco_get_rice(RICEContext *r)
{
int v;
if (r->run > 0) { /* we have zero run */
r->run--; |
42ebf67d |
loco_update_rice_param(r, 0); |
9c7fb608 |
return 0;
} |
42ebf67d |
v = get_ur_golomb_jpegls(&r->gb, loco_get_rice_param(r), INT_MAX, 0); |
b627c301 |
loco_update_rice_param(r, (v + 1) >> 1); |
9c7fb608 |
if (!v) {
if (r->save >= 0) { |
42ebf67d |
r->run = get_ur_golomb_jpegls(&r->gb, 2, INT_MAX, 0); |
b627c301 |
if (r->run > 1) |
9c7fb608 |
r->save += r->run + 1;
else
r->save -= 3; |
b627c301 |
} else |
9c7fb608 |
r->run2++; |
42ebf67d |
} else { |
b627c301 |
v = ((v >> 1) + r->lossy) ^ -(v & 1); |
42ebf67d |
if (r->run2 > 0) {
if (r->run2 > 2)
r->save += r->run2;
else
r->save -= 3;
r->run2 = 0;
} |
9c7fb608 |
} |
115329f1 |
|
9c7fb608 |
return v;
}
/* LOCO main predictor - LOCO-I/JPEG-LS predictor */
static inline int loco_predict(uint8_t* data, int stride, int step)
{
int a, b, c; |
115329f1 |
|
9c7fb608 |
a = data[-stride];
b = data[-step];
c = data[-stride - step]; |
115329f1 |
|
42ebf67d |
return mid_pred(a, a + b - c, b); |
9c7fb608 |
}
static int loco_decode_plane(LOCOContext *l, uint8_t *data, int width, int height, |
7993df65 |
int stride, const uint8_t *buf, int buf_size, int step) |
9c7fb608 |
{
RICEContext rc;
int val;
int i, j; |
115329f1 |
|
2bf09826 |
if(buf_size<=0)
return -1;
|
920046ab |
init_get_bits8(&rc.gb, buf, buf_size); |
b627c301 |
rc.save = 0;
rc.run = 0;
rc.run2 = 0; |
115329f1 |
rc.lossy = l->lossy;
|
b627c301 |
rc.sum = 8; |
9c7fb608 |
rc.count = 1; |
115329f1 |
|
9c7fb608 |
/* restore top left pixel */ |
b627c301 |
val = loco_get_rice(&rc); |
9c7fb608 |
data[0] = 128 + val;
/* restore top line */
for (i = 1; i < width; i++) {
val = loco_get_rice(&rc);
data[i * step] = data[i * step - step] + val;
}
data += stride;
for (j = 1; j < height; j++) {
/* restore left column */
val = loco_get_rice(&rc);
data[0] = data[-stride] + val;
/* restore all other pixels */
for (i = 1; i < width; i++) {
val = loco_get_rice(&rc);
data[i * step] = loco_predict(&data[i * step], stride, step) + val;
}
data += stride;
} |
115329f1 |
|
ccd425e7 |
return (get_bits_count(&rc.gb) + 7) >> 3; |
9c7fb608 |
}
|
115329f1 |
static int decode_frame(AVCodecContext *avctx, |
df9b9567 |
void *data, int *got_frame, |
7a00bbad |
AVPacket *avpkt) |
9c7fb608 |
{
LOCOContext * const l = avctx->priv_data; |
b627c301 |
const uint8_t *buf = avpkt->data;
int buf_size = avpkt->size; |
759001c5 |
AVFrame * const p = data; |
1a31dff9 |
int decoded, ret; |
9c7fb608 |
|
1ec94b0f |
if ((ret = ff_get_buffer(avctx, p, 0)) < 0) |
1a31dff9 |
return ret; |
9c7fb608 |
p->key_frame = 1;
|
91ec1c6c |
#define ADVANCE_BY_DECODED do { \ |
714ff448 |
if (decoded < 0 || decoded >= buf_size) goto buf_too_small; \ |
91ec1c6c |
buf += decoded; buf_size -= decoded; \
} while(0) |
9c7fb608 |
switch(l->mode) {
case LOCO_CYUY2: case LOCO_YUY2: case LOCO_UYVY:
decoded = loco_decode_plane(l, p->data[0], avctx->width, avctx->height,
p->linesize[0], buf, buf_size, 1); |
91ec1c6c |
ADVANCE_BY_DECODED; |
9c7fb608 |
decoded = loco_decode_plane(l, p->data[1], avctx->width / 2, avctx->height,
p->linesize[1], buf, buf_size, 1); |
91ec1c6c |
ADVANCE_BY_DECODED; |
9c7fb608 |
decoded = loco_decode_plane(l, p->data[2], avctx->width / 2, avctx->height,
p->linesize[2], buf, buf_size, 1);
break;
case LOCO_CYV12: case LOCO_YV12:
decoded = loco_decode_plane(l, p->data[0], avctx->width, avctx->height,
p->linesize[0], buf, buf_size, 1); |
91ec1c6c |
ADVANCE_BY_DECODED; |
9c7fb608 |
decoded = loco_decode_plane(l, p->data[2], avctx->width / 2, avctx->height / 2,
p->linesize[2], buf, buf_size, 1); |
91ec1c6c |
ADVANCE_BY_DECODED; |
76d6abc1 |
decoded = loco_decode_plane(l, p->data[1], avctx->width / 2, avctx->height / 2,
p->linesize[1], buf, buf_size, 1); |
9c7fb608 |
break;
case LOCO_CRGB: case LOCO_RGB: |
a18ba908 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1), avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 3); |
91ec1c6c |
ADVANCE_BY_DECODED; |
a18ba908 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1) + 1, avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 3); |
91ec1c6c |
ADVANCE_BY_DECODED; |
a18ba908 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1) + 2, avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 3); |
9c7fb608 |
break; |
714ff448 |
case LOCO_CRGBA: |
9c7fb608 |
case LOCO_RGBA: |
7b5fdd04 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1), avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 4); |
91ec1c6c |
ADVANCE_BY_DECODED; |
7b5fdd04 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1) + 1, avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 4); |
91ec1c6c |
ADVANCE_BY_DECODED; |
7b5fdd04 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1) + 2, avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 4); |
91ec1c6c |
ADVANCE_BY_DECODED; |
7b5fdd04 |
decoded = loco_decode_plane(l, p->data[0] + p->linesize[0]*(avctx->height-1) + 3, avctx->width, avctx->height,
-p->linesize[0], buf, buf_size, 4); |
9c7fb608 |
break; |
c4735eef |
default:
av_assert0(0); |
9c7fb608 |
}
|
33796645 |
if (decoded < 0 || decoded > buf_size)
goto buf_too_small;
buf_size -= decoded;
|
df9b9567 |
*got_frame = 1; |
115329f1 |
|
33796645 |
return avpkt->size - buf_size; |
067432c1 |
buf_too_small:
av_log(avctx, AV_LOG_ERROR, "Input data too small.\n");
return AVERROR(EINVAL); |
9c7fb608 |
}
|
b627c301 |
static av_cold int decode_init(AVCodecContext *avctx)
{ |
9c7fb608 |
LOCOContext * const l = avctx->priv_data;
int version;
l->avctx = avctx;
if (avctx->extradata_size < 12) {
av_log(avctx, AV_LOG_ERROR, "Extradata size must be >= 12 instead of %i\n",
avctx->extradata_size); |
1a31dff9 |
return AVERROR_INVALIDDATA; |
9c7fb608 |
} |
fead30d4 |
version = AV_RL32(avctx->extradata); |
b627c301 |
switch (version) { |
9c7fb608 |
case 1:
l->lossy = 0;
break;
case 2: |
fead30d4 |
l->lossy = AV_RL32(avctx->extradata + 8); |
9c7fb608 |
break;
default: |
fead30d4 |
l->lossy = AV_RL32(avctx->extradata + 8); |
6d97484d |
avpriv_request_sample(avctx, "LOCO codec version %i", version); |
9c7fb608 |
} |
115329f1 |
|
fead30d4 |
l->mode = AV_RL32(avctx->extradata + 4); |
b627c301 |
switch (l->mode) {
case LOCO_CYUY2:
case LOCO_YUY2:
case LOCO_UYVY: |
716d413c |
avctx->pix_fmt = AV_PIX_FMT_YUV422P; |
9c7fb608 |
break; |
b627c301 |
case LOCO_CRGB:
case LOCO_RGB: |
716d413c |
avctx->pix_fmt = AV_PIX_FMT_BGR24; |
9c7fb608 |
break; |
b627c301 |
case LOCO_CYV12:
case LOCO_YV12: |
716d413c |
avctx->pix_fmt = AV_PIX_FMT_YUV420P; |
9c7fb608 |
break; |
b627c301 |
case LOCO_CRGBA:
case LOCO_RGBA: |
9c50e693 |
avctx->pix_fmt = AV_PIX_FMT_BGRA; |
9c7fb608 |
break;
default:
av_log(avctx, AV_LOG_INFO, "Unknown colorspace, index = %i\n", l->mode); |
1a31dff9 |
return AVERROR_INVALIDDATA; |
9c7fb608 |
} |
b627c301 |
if (avctx->debug & FF_DEBUG_PICT_INFO) |
42ebf67d |
av_log(avctx, AV_LOG_INFO, "lossy:%i, version:%i, mode: %i\n", l->lossy, version, l->mode); |
9c7fb608 |
|
6d924b5a |
return 0;
}
|
e7e2df27 |
AVCodec ff_loco_decoder = { |
ec6402b7 |
.name = "loco", |
b2bed932 |
.long_name = NULL_IF_CONFIG_SMALL("LOCO"), |
ec6402b7 |
.type = AVMEDIA_TYPE_VIDEO, |
36ef5369 |
.id = AV_CODEC_ID_LOCO, |
ec6402b7 |
.priv_data_size = sizeof(LOCOContext),
.init = decode_init,
.decode = decode_frame,
.capabilities = CODEC_CAP_DR1, |
9c7fb608 |
}; |