1
0
mirror of https://git.ffmpeg.org/ffmpeg.git synced 2025-09-18 11:34:34 +03:00
Files
ffmpeg/libavcodec/jpeglsenc.c
Andreas Rheinhardt 0971fcf0a0 avcodec/codec_internal, all: Use macros to set deprecated AVCodec fields
The aim of this is twofold: a) Clang warns when setting a deprecated
field in a definition and because several of the widely set
AVCodec fields are deprecated, one gets several hundred warnings
from Clang for an ordinary build. Yet fortunately Clang (unlike GCC)
allows to disable deprecation warnings inside a definition, so
that one can create simple macros to set these fields that also suppress
deprecation warnings for Clang. This has already been done in
fdff1b9cbf for AVCodec.channel_layouts.
b) Using macros will allow to easily migrate these fields to internal ones.

Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2025-03-10 00:57:23 +01:00

491 lines
15 KiB
C

/*
* JPEG-LS encoder
* Copyright (c) 2003 Michael Niedermayer
* Copyright (c) 2006 Konstantin Shishkov
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* 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
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* @file
* JPEG-LS encoder.
*/
#define UNCHECKED_BITSTREAM_READER 1
#include "libavutil/mem.h"
#include "libavutil/opt.h"
#include "avcodec.h"
#include "bytestream.h"
#include "codec_internal.h"
#include "encode.h"
#include "get_bits.h"
#include "put_bits.h"
#include "put_golomb.h"
#include "mathops.h"
#include "mjpeg.h"
#include "jpegls.h"
typedef struct JPEGLSContext {
AVClass *class;
int pred;
int comps;
size_t size;
uint8_t *buf;
} JPEGLSContext;
static inline void put_marker_byteu(PutByteContext *pb, enum JpegMarker code)
{
bytestream2_put_byteu(pb, 0xff);
bytestream2_put_byteu(pb, code);
}
/**
* Encode error from regular symbol
*/
static inline void ls_encode_regular(JLSState *state, PutBitContext *pb, int Q,
int err)
{
int k;
int val;
int map;
for (k = 0; (state->N[Q] << k) < state->A[Q]; k++)
;
map = !state->near && !k && (2 * state->B[Q] <= -state->N[Q]);
if (err < 0)
err += state->range;
if (err >= (state->range + 1 >> 1)) {
err -= state->range;
val = 2 * FFABS(err) - 1 - map;
} else
val = 2 * err + map;
set_ur_golomb_jpegls(pb, val, k, state->limit, state->qbpp);
ff_jpegls_update_state_regular(state, Q, err);
}
/**
* Encode error from run termination
*/
static inline void ls_encode_runterm(JLSState *state, PutBitContext *pb,
int RItype, int err, int limit_add)
{
int k;
int val, map;
int Q = 365 + RItype;
int temp;
temp = state->A[Q];
if (RItype)
temp += state->N[Q] >> 1;
for (k = 0; (state->N[Q] << k) < temp; k++)
;
map = 0;
if (!k && err && (2 * state->B[Q] < state->N[Q]))
map = 1;
if (err < 0)
val = -(2 * err) - 1 - RItype + map;
else
val = 2 * err - RItype - map;
set_ur_golomb_jpegls(pb, val, k, state->limit - limit_add - 1, state->qbpp);
if (err < 0)
state->B[Q]++;
state->A[Q] += (val + 1 - RItype) >> 1;
ff_jpegls_downscale_state(state, Q);
}
/**
* Encode run value as specified by JPEG-LS standard
*/
static inline void ls_encode_run(JLSState *state, PutBitContext *pb, int run,
int comp, int trail)
{
while (run >= (1 << ff_log2_run[state->run_index[comp]])) {
put_bits(pb, 1, 1);
run -= 1 << ff_log2_run[state->run_index[comp]];
if (state->run_index[comp] < 31)
state->run_index[comp]++;
}
/* if hit EOL, encode another full run, else encode aborted run */
if (!trail && run) {
put_bits(pb, 1, 1);
} else if (trail) {
put_bits(pb, 1, 0);
if (ff_log2_run[state->run_index[comp]])
put_bits(pb, ff_log2_run[state->run_index[comp]], run);
}
}
/**
* Encode one line of image
*/
static inline void ls_encode_line(JLSState *state, PutBitContext *pb,
void *tmp, const void *in, int last2, int w,
int stride, int comp, int bits)
{
int x = 0;
int Ra = R(tmp, 0), Rb, Rc = last2, Rd;
int D0, D1, D2;
while (x < w) {
int err, pred, sign;
/* compute gradients */
Rb = R(tmp, x);
Rd = (x >= w - stride) ? R(tmp, x) : R(tmp, x + stride);
D0 = Rd - Rb;
D1 = Rb - Rc;
D2 = Rc - Ra;
/* run mode */
if ((FFABS(D0) <= state->near) &&
(FFABS(D1) <= state->near) &&
(FFABS(D2) <= state->near)) {
int RUNval, RItype, run;
run = 0;
RUNval = Ra;
while (x < w && (FFABS(R(in, x) - RUNval) <= state->near)) {
run++;
W(tmp, x, Ra);
x += stride;
}
ls_encode_run(state, pb, run, comp, x < w);
if (x >= w)
return;
Rb = R(tmp, x);
RItype = FFABS(Ra - Rb) <= state->near;
pred = RItype ? Ra : Rb;
err = R(in, x) - pred;
if (!RItype && Ra > Rb)
err = -err;
if (state->near) {
if (err > 0)
err = (state->near + err) / state->twonear;
else
err = -(state->near - err) / state->twonear;
if (RItype || (Rb >= Ra))
Ra = av_clip(pred + err * state->twonear, 0, state->maxval);
else
Ra = av_clip(pred - err * state->twonear, 0, state->maxval);
} else
Ra = R(in, x);
W(tmp, x, Ra);
if (err < 0)
err += state->range;
if (err >= state->range + 1 >> 1)
err -= state->range;
ls_encode_runterm(state, pb, RItype, err,
ff_log2_run[state->run_index[comp]]);
if (state->run_index[comp] > 0)
state->run_index[comp]--;
} else { /* regular mode */
int context;
context = ff_jpegls_quantize(state, D0) * 81 +
ff_jpegls_quantize(state, D1) * 9 +
ff_jpegls_quantize(state, D2);
pred = mid_pred(Ra, Ra + Rb - Rc, Rb);
if (context < 0) {
context = -context;
sign = 1;
pred = av_clip(pred - state->C[context], 0, state->maxval);
err = pred - R(in, x);
} else {
sign = 0;
pred = av_clip(pred + state->C[context], 0, state->maxval);
err = R(in, x) - pred;
}
if (state->near) {
if (err > 0)
err = (state->near + err) / state->twonear;
else
err = -(state->near - err) / state->twonear;
if (!sign)
Ra = av_clip(pred + err * state->twonear, 0, state->maxval);
else
Ra = av_clip(pred - err * state->twonear, 0, state->maxval);
} else
Ra = R(in, x);
W(tmp, x, Ra);
ls_encode_regular(state, pb, context, err);
}
Rc = Rb;
x += stride;
}
}
static void ls_store_lse(JLSState *state, PutByteContext *pb)
{
/* Test if we have default params and don't need to store LSE */
JLSState state2 = { 0 };
state2.bpp = state->bpp;
state2.near = state->near;
ff_jpegls_reset_coding_parameters(&state2, 1);
if (state->T1 == state2.T1 &&
state->T2 == state2.T2 &&
state->T3 == state2.T3 &&
state->reset == state2.reset)
return;
/* store LSE type 1 */
put_marker_byteu(pb, LSE);
bytestream2_put_be16u(pb, 13);
bytestream2_put_byteu(pb, 1);
bytestream2_put_be16u(pb, state->maxval);
bytestream2_put_be16u(pb, state->T1);
bytestream2_put_be16u(pb, state->T2);
bytestream2_put_be16u(pb, state->T3);
bytestream2_put_be16u(pb, state->reset);
}
static int encode_picture_ls(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *pict, int *got_packet)
{
JPEGLSContext *ctx = avctx->priv_data;
const AVFrame *const p = pict;
PutByteContext pb;
PutBitContext pb2;
GetBitContext gb;
const uint8_t *in;
uint8_t *last = NULL;
JLSState state = { 0 };
size_t size;
int i, ret, size_in_bits;
int comps;
last = av_mallocz(FFABS(p->linesize[0]));
if (!last)
return AVERROR(ENOMEM);
init_put_bits(&pb2, ctx->buf, ctx->size);
comps = ctx->comps;
/* initialize JPEG-LS state from JPEG parameters */
state.near = ctx->pred;
state.bpp = (avctx->pix_fmt == AV_PIX_FMT_GRAY16) ? 16 : 8;
ff_jpegls_reset_coding_parameters(&state, 0);
ff_jpegls_init_state(&state);
in = p->data[0];
if (avctx->pix_fmt == AV_PIX_FMT_GRAY8) {
int t = 0;
for (i = 0; i < avctx->height; i++) {
int last0 = last[0];
ls_encode_line(&state, &pb2, last, in, t, avctx->width, 1, 0, 8);
t = last0;
in += p->linesize[0];
}
} else if (avctx->pix_fmt == AV_PIX_FMT_GRAY16) {
int t = 0;
for (i = 0; i < avctx->height; i++) {
int last0 = *((uint16_t *)last);
ls_encode_line(&state, &pb2, last, in, t, avctx->width, 1, 0, 16);
t = last0;
in += p->linesize[0];
}
} else if (avctx->pix_fmt == AV_PIX_FMT_RGB24) {
int j, width;
int Rc[3] = { 0, 0, 0 };
width = avctx->width * 3;
for (i = 0; i < avctx->height; i++) {
for (j = 0; j < 3; j++) {
int last0 = last[j];
ls_encode_line(&state, &pb2, last + j, in + j, Rc[j],
width, 3, j, 8);
Rc[j] = last0;
}
in += p->linesize[0];
}
} else if (avctx->pix_fmt == AV_PIX_FMT_BGR24) {
int j, width;
int Rc[3] = { 0, 0, 0 };
width = avctx->width * 3;
for (i = 0; i < avctx->height; i++) {
for (j = 2; j >= 0; j--) {
int last0 = last[j];
ls_encode_line(&state, &pb2, last + j, in + j, Rc[j],
width, 3, j, 8);
Rc[j] = last0;
}
in += p->linesize[0];
}
}
av_free(last);
/* Now the actual image data has been written, which enables us to estimate
* the needed packet size: For every 15 input bits, an escape bit might be
* added below; and if put_bits_count % 15 is >= 8, then another bit might
* be added.
* Furthermore the specification says that after doing 0xff escaping unused
* bits in the last byte must be set to 0, so just append 7 "optional" zero
* bits to avoid special-casing. This also simplifies the size calculation:
* Properly rounding up is now automatically baked-in. */
put_bits(&pb2, 7, 0);
/* Make sure that the bit count + padding is representable in an int;
necessary for put_bits_count() as well as for using a GetBitContext. */
if (put_bytes_count(&pb2, 0) > INT_MAX / 8 - AV_INPUT_BUFFER_PADDING_SIZE)
return AVERROR(ERANGE);
size_in_bits = put_bits_count(&pb2);
flush_put_bits(&pb2);
size = size_in_bits * 2U / 15;
size += 2 + 2 + 2 + 1 + 2 + 2 + 1 + comps * (1 + 1 + 1) + 2 + 2 + 1
+ comps * (1 + 1) + 1 + 1 + 1; /* Header */
size += 2 + 2 + 1 + 2 + 2 + 2 + 2 + 2; /* LSE */
size += 2; /* EOI */
if ((ret = ff_get_encode_buffer(avctx, pkt, size, 0)) < 0)
return ret;
bytestream2_init_writer(&pb, pkt->data, pkt->size);
/* write our own JPEG header, can't use mjpeg_picture_header */
put_marker_byteu(&pb, SOI);
put_marker_byteu(&pb, SOF48);
bytestream2_put_be16u(&pb, 8 + comps * 3); // header size depends on components
bytestream2_put_byteu(&pb, (avctx->pix_fmt == AV_PIX_FMT_GRAY16) ? 16 : 8); // bpp
bytestream2_put_be16u(&pb, avctx->height);
bytestream2_put_be16u(&pb, avctx->width);
bytestream2_put_byteu(&pb, comps); // components
for (i = 1; i <= comps; i++) {
bytestream2_put_byteu(&pb, i); // component ID
bytestream2_put_byteu(&pb, 0x11); // subsampling: none
bytestream2_put_byteu(&pb, 0); // Tiq, used by JPEG-LS ext
}
put_marker_byteu(&pb, SOS);
bytestream2_put_be16u(&pb, 6 + comps * 2);
bytestream2_put_byteu(&pb, comps);
for (i = 1; i <= comps; i++) {
bytestream2_put_byteu(&pb, i); // component ID
bytestream2_put_byteu(&pb, 0); // mapping index: none
}
bytestream2_put_byteu(&pb, ctx->pred);
bytestream2_put_byteu(&pb, (comps > 1) ? 1 : 0); // interleaving: 0 - plane, 1 - line
bytestream2_put_byteu(&pb, 0); // point transform: none
ls_store_lse(&state, &pb);
/* do escape coding */
init_get_bits(&gb, pb2.buf, size_in_bits);
size_in_bits -= 7;
while (get_bits_count(&gb) < size_in_bits) {
int v;
v = get_bits(&gb, 8);
bytestream2_put_byteu(&pb, v);
if (v == 0xFF) {
v = get_bits(&gb, 7);
bytestream2_put_byteu(&pb, v);
}
}
/* End of image */
put_marker_byteu(&pb, EOI);
av_shrink_packet(pkt, bytestream2_tell_p(&pb));
*got_packet = 1;
return 0;
}
static av_cold int encode_jpegls_init(AVCodecContext *avctx)
{
JPEGLSContext *ctx = avctx->priv_data;
size_t size;
if ((avctx->width | avctx->height) > UINT16_MAX) {
av_log(avctx, AV_LOG_ERROR, "Dimensions exceeding 65535x65535\n");
return AVERROR(EINVAL);
}
if (avctx->pix_fmt == AV_PIX_FMT_GRAY8 ||
avctx->pix_fmt == AV_PIX_FMT_GRAY16)
ctx->comps = 1;
else
ctx->comps = 3;
size = FF_INPUT_BUFFER_MIN_SIZE;
/* INT_MAX due to PutBit-API. */
if (avctx->width * (unsigned)avctx->height > (INT_MAX - size) / 4 / ctx->comps)
return AVERROR(ERANGE);
size += 4 * ctx->comps * avctx->width * avctx->height;
ctx->size = size;
ctx->buf = av_malloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!ctx->buf)
return AVERROR(ENOMEM);
return 0;
}
static av_cold int encode_jpegls_close(AVCodecContext *avctx)
{
JPEGLSContext *ctx = avctx->priv_data;
av_freep(&ctx->buf);
return 0;
}
#define OFFSET(x) offsetof(JPEGLSContext, x)
#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "pred", "Prediction method", OFFSET(pred), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 2, VE, .unit = "pred" },
{ "left", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, VE, .unit = "pred" },
{ "plane", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, VE, .unit = "pred" },
{ "median", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, INT_MIN, INT_MAX, VE, .unit = "pred" },
{ NULL},
};
static const AVClass jpegls_class = {
.class_name = "jpegls",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
const FFCodec ff_jpegls_encoder = {
.p.name = "jpegls",
CODEC_LONG_NAME("JPEG-LS"),
.p.type = AVMEDIA_TYPE_VIDEO,
.p.id = AV_CODEC_ID_JPEGLS,
.p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS |
AV_CODEC_CAP_ENCODER_REORDERED_OPAQUE,
.priv_data_size = sizeof(JPEGLSContext),
.p.priv_class = &jpegls_class,
.init = encode_jpegls_init,
FF_CODEC_ENCODE_CB(encode_picture_ls),
.close = encode_jpegls_close,
CODEC_PIXFMTS(AV_PIX_FMT_BGR24, AV_PIX_FMT_RGB24,
AV_PIX_FMT_GRAY8, AV_PIX_FMT_GRAY16),
.caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
};