#include "bytestream.h"
#include "jpeg2000.h"
#include "libavutil/common.h"
+#include "libavutil/opt.h"
#define NMSEDEC_BITS 7
#define NMSEDEC_FRACBITS (NMSEDEC_BITS-1)
#define WMSEDEC_SHIFT 13 ///< must be >= 13
#define LAMBDA_SCALE (100000000LL << (WMSEDEC_SHIFT - 13))
+#define CODEC_JP2 1
+#define CODEC_J2K 0
+
static int lut_nmsedec_ref [1<<NMSEDEC_BITS],
lut_nmsedec_ref0[1<<NMSEDEC_BITS],
lut_nmsedec_sig [1<<NMSEDEC_BITS],
} Jpeg2000Tile;
typedef struct {
+ AVClass *class;
AVCodecContext *avctx;
const AVFrame *picture;
Jpeg2000QuantStyle qntsty;
Jpeg2000Tile *tile;
+
+ int format;
} Jpeg2000EncoderContext;
bytestream_put_byte(&s->buf, 0); // progression level
bytestream_put_be16(&s->buf, 1); // num of layers
if(s->avctx->pix_fmt == AV_PIX_FMT_YUV444P){
- bytestream_put_byte(&s->buf, 2); // ICT
+ bytestream_put_byte(&s->buf, 0); // unspecified
}else{
bytestream_put_byte(&s->buf, 0); // unspecified
}
for (y0 = 0; y0 < height; y0 += 4)
for (x = 0; x < width; x++)
for (y = y0; y < height && y < y0+4; y++){
- if (!(t1->flags[y+1][x+1] & JPEG2000_T1_SIG) && (t1->flags[y+1][x+1] & JPEG2000_T1_SIG_NB)){
- int ctxno = ff_jpeg2000_getsigctxno(t1->flags[y+1][x+1], bandno),
- bit = t1->data[y][x] & mask ? 1 : 0;
+ if (!(t1->flags[(y+1) * t1->stride + x+1] & JPEG2000_T1_SIG) && (t1->flags[(y+1) * t1->stride + x+1] & JPEG2000_T1_SIG_NB)){
+ int ctxno = ff_jpeg2000_getsigctxno(t1->flags[(y+1) * t1->stride + x+1], bandno),
+ bit = t1->data[(y) * t1->stride + x] & mask ? 1 : 0;
ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, bit);
if (bit){
int xorbit;
- int ctxno = ff_jpeg2000_getsgnctxno(t1->flags[y+1][x+1], &xorbit);
- ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, (t1->flags[y+1][x+1] >> 15) ^ xorbit);
- *nmsedec += getnmsedec_sig(t1->data[y][x], bpno + NMSEDEC_FRACBITS);
- ff_jpeg2000_set_significance(t1, x, y, t1->flags[y+1][x+1] >> 15);
+ int ctxno = ff_jpeg2000_getsgnctxno(t1->flags[(y+1) * t1->stride + x+1], &xorbit);
+ ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, (t1->flags[(y+1) * t1->stride + x+1] >> 15) ^ xorbit);
+ *nmsedec += getnmsedec_sig(t1->data[(y) * t1->stride + x], bpno + NMSEDEC_FRACBITS);
+ ff_jpeg2000_set_significance(t1, x, y, t1->flags[(y+1) * t1->stride + x+1] >> 15);
}
- t1->flags[y+1][x+1] |= JPEG2000_T1_VIS;
+ t1->flags[(y+1) * t1->stride + x+1] |= JPEG2000_T1_VIS;
}
}
}
for (y0 = 0; y0 < height; y0 += 4)
for (x = 0; x < width; x++)
for (y = y0; y < height && y < y0+4; y++)
- if ((t1->flags[y+1][x+1] & (JPEG2000_T1_SIG | JPEG2000_T1_VIS)) == JPEG2000_T1_SIG){
- int ctxno = ff_jpeg2000_getrefctxno(t1->flags[y+1][x+1]);
- *nmsedec += getnmsedec_ref(t1->data[y][x], bpno + NMSEDEC_FRACBITS);
- ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, t1->data[y][x] & mask ? 1:0);
- t1->flags[y+1][x+1] |= JPEG2000_T1_REF;
+ if ((t1->flags[(y+1) * t1->stride + x+1] & (JPEG2000_T1_SIG | JPEG2000_T1_VIS)) == JPEG2000_T1_SIG){
+ int ctxno = ff_jpeg2000_getrefctxno(t1->flags[(y+1) * t1->stride + x+1]);
+ *nmsedec += getnmsedec_ref(t1->data[(y) * t1->stride + x], bpno + NMSEDEC_FRACBITS);
+ ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, t1->data[(y) * t1->stride + x] & mask ? 1:0);
+ t1->flags[(y+1) * t1->stride + x+1] |= JPEG2000_T1_REF;
}
}
for (y0 = 0; y0 < height; y0 += 4)
for (x = 0; x < width; x++){
if (y0 + 3 < height && !(
- (t1->flags[y0+1][x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG)) ||
- (t1->flags[y0+2][x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG)) ||
- (t1->flags[y0+3][x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG)) ||
- (t1->flags[y0+4][x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG))))
+ (t1->flags[(y0+1) * t1->stride + x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG)) ||
+ (t1->flags[(y0+2) * t1->stride + x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG)) ||
+ (t1->flags[(y0+3) * t1->stride + x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG)) ||
+ (t1->flags[(y0+4) * t1->stride + x+1] & (JPEG2000_T1_SIG_NB | JPEG2000_T1_VIS | JPEG2000_T1_SIG))))
{
// aggregation mode
int rlen;
for (rlen = 0; rlen < 4; rlen++)
- if (t1->data[y0+rlen][x] & mask)
+ if (t1->data[(y0+rlen) * t1->stride + x] & mask)
break;
ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + MQC_CX_RL, rlen != 4);
if (rlen == 4)
ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + MQC_CX_UNI, rlen >> 1);
ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + MQC_CX_UNI, rlen & 1);
for (y = y0 + rlen; y < y0 + 4; y++){
- if (!(t1->flags[y+1][x+1] & (JPEG2000_T1_SIG | JPEG2000_T1_VIS))){
- int ctxno = ff_jpeg2000_getsigctxno(t1->flags[y+1][x+1], bandno);
+ if (!(t1->flags[(y+1) * t1->stride + x+1] & (JPEG2000_T1_SIG | JPEG2000_T1_VIS))){
+ int ctxno = ff_jpeg2000_getsigctxno(t1->flags[(y+1) * t1->stride + x+1], bandno);
if (y > y0 + rlen)
- ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, t1->data[y][x] & mask ? 1:0);
- if (t1->data[y][x] & mask){ // newly significant
+ ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, t1->data[(y) * t1->stride + x] & mask ? 1:0);
+ if (t1->data[(y) * t1->stride + x] & mask){ // newly significant
int xorbit;
- int ctxno = ff_jpeg2000_getsgnctxno(t1->flags[y+1][x+1], &xorbit);
- *nmsedec += getnmsedec_sig(t1->data[y][x], bpno + NMSEDEC_FRACBITS);
- ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, (t1->flags[y+1][x+1] >> 15) ^ xorbit);
- ff_jpeg2000_set_significance(t1, x, y, t1->flags[y+1][x+1] >> 15);
+ int ctxno = ff_jpeg2000_getsgnctxno(t1->flags[(y+1) * t1->stride + x+1], &xorbit);
+ *nmsedec += getnmsedec_sig(t1->data[(y) * t1->stride + x], bpno + NMSEDEC_FRACBITS);
+ ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, (t1->flags[(y+1) * t1->stride + x+1] >> 15) ^ xorbit);
+ ff_jpeg2000_set_significance(t1, x, y, t1->flags[(y+1) * t1->stride + x+1] >> 15);
}
}
- t1->flags[y+1][x+1] &= ~JPEG2000_T1_VIS;
+ t1->flags[(y+1) * t1->stride + x+1] &= ~JPEG2000_T1_VIS;
}
} else{
for (y = y0; y < y0 + 4 && y < height; y++){
- if (!(t1->flags[y+1][x+1] & (JPEG2000_T1_SIG | JPEG2000_T1_VIS))){
- int ctxno = ff_jpeg2000_getsigctxno(t1->flags[y+1][x+1], bandno);
- ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, t1->data[y][x] & mask ? 1:0);
- if (t1->data[y][x] & mask){ // newly significant
+ if (!(t1->flags[(y+1) * t1->stride + x+1] & (JPEG2000_T1_SIG | JPEG2000_T1_VIS))){
+ int ctxno = ff_jpeg2000_getsigctxno(t1->flags[(y+1) * t1->stride + x+1], bandno);
+ ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, t1->data[(y) * t1->stride + x] & mask ? 1:0);
+ if (t1->data[(y) * t1->stride + x] & mask){ // newly significant
int xorbit;
- int ctxno = ff_jpeg2000_getsgnctxno(t1->flags[y+1][x+1], &xorbit);
- *nmsedec += getnmsedec_sig(t1->data[y][x], bpno + NMSEDEC_FRACBITS);
- ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, (t1->flags[y+1][x+1] >> 15) ^ xorbit);
- ff_jpeg2000_set_significance(t1, x, y, t1->flags[y+1][x+1] >> 15);
+ int ctxno = ff_jpeg2000_getsgnctxno(t1->flags[(y+1) * t1->stride + x+1], &xorbit);
+ *nmsedec += getnmsedec_sig(t1->data[(y) * t1->stride + x], bpno + NMSEDEC_FRACBITS);
+ ff_mqc_encode(&t1->mqc, t1->mqc.cx_states + ctxno, (t1->flags[(y+1) * t1->stride + x+1] >> 15) ^ xorbit);
+ ff_jpeg2000_set_significance(t1, x, y, t1->flags[(y+1) * t1->stride + x+1] >> 15);
}
}
- t1->flags[y+1][x+1] &= ~JPEG2000_T1_VIS;
+ t1->flags[(y+1) * t1->stride + x+1] &= ~JPEG2000_T1_VIS;
}
}
}
int pass_t = 2, passno, x, y, max=0, nmsedec, bpno;
int64_t wmsedec = 0;
- for (y = 0; y < height+2; y++)
- memset(t1->flags[y], 0, (width+2)*sizeof(int));
+ memset(t1->flags, 0, t1->stride * (height + 2) * sizeof(*t1->flags));
for (y = 0; y < height; y++){
for (x = 0; x < width; x++){
- if (t1->data[y][x] < 0){
- t1->flags[y+1][x+1] |= JPEG2000_T1_SGN;
- t1->data[y][x] = -t1->data[y][x];
+ if (t1->data[(y) * t1->stride + x] < 0){
+ t1->flags[(y+1) * t1->stride + x+1] |= JPEG2000_T1_SGN;
+ t1->data[(y) * t1->stride + x] = -t1->data[(y) * t1->stride + x];
}
- max = FFMAX(max, t1->data[y][x]);
+ max = FFMAX(max, t1->data[(y) * t1->stride + x]);
}
}
break;
}
- cblk->passes[passno].rate = 3 + ff_mqc_length(&t1->mqc);
+ cblk->passes[passno].rate = ff_mqc_flush_to(&t1->mqc, cblk->passes[passno].flushed, &cblk->passes[passno].flushed_len);
wmsedec += (int64_t)nmsedec << (2*bpno);
cblk->passes[passno].disto = wmsedec;
cblk->npasses = passno;
cblk->ninclpasses = passno;
- // TODO: optional flush on each pass
- cblk->passes[passno-1].rate = ff_mqc_flush(&t1->mqc);
+ cblk->passes[passno-1].rate = ff_mqc_flush_to(&t1->mqc, cblk->passes[passno-1].flushed, &cblk->passes[passno-1].flushed_len);
}
/* tier-2 routines: */
if (cblk->ninclpasses){
if (s->buf_end - s->buf < cblk->passes[cblk->ninclpasses-1].rate)
return -1;
- bytestream_put_buffer(&s->buf, cblk->data, cblk->passes[cblk->ninclpasses-1].rate);
+ bytestream_put_buffer(&s->buf, cblk->data, cblk->passes[cblk->ninclpasses-1].rate
+ - cblk->passes[cblk->ninclpasses-1].flushed_len);
+ bytestream_put_buffer(&s->buf, cblk->passes[cblk->ninclpasses-1].flushed,
+ cblk->passes[cblk->ninclpasses-1].flushed_len);
}
}
}
for (compno = 0; compno < s->ncomponents; compno++){
Jpeg2000Component *comp = s->tile[tileno].comp + compno;
+ t1.stride = (1<<codsty->log2_cblk_width) + 2;
+
av_log(s->avctx, AV_LOG_DEBUG,"dwt\n");
if ((ret = ff_dwt_encode(&comp->dwt, comp->i_data)) < 0)
return ret;
int y, x;
if (codsty->transform == FF_DWT53){
for (y = yy0; y < yy1; y++){
- int *ptr = t1.data[y-yy0];
+ int *ptr = t1.data + (y-yy0)*t1.stride;
for (x = xx0; x < xx1; x++){
*ptr++ = comp->i_data[(comp->coord[0][1] - comp->coord[0][0]) * y + x] << NMSEDEC_FRACBITS;
}
}
} else{
for (y = yy0; y < yy1; y++){
- int *ptr = t1.data[y-yy0];
+ int *ptr = t1.data + (y-yy0)*t1.stride;
for (x = xx0; x < xx1; x++){
*ptr = (comp->i_data[(comp->coord[0][1] - comp->coord[0][0]) * y + x]);
*ptr = (int64_t)*ptr * (int64_t)(16384 * 65536 / band->i_stepsize) >> 15 - NMSEDEC_FRACBITS;
}
}
+static void update_size(uint8_t *size, const uint8_t *end)
+{
+ AV_WB32(size, end-size);
+}
+
static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *pict, int *got_packet)
{
int tileno, ret;
Jpeg2000EncoderContext *s = avctx->priv_data;
+ uint8_t *chunkstart, *jp2cstart, *jp2hstart;
if ((ret = ff_alloc_packet2(avctx, pkt, avctx->width*avctx->height*9 + FF_MIN_BUFFER_SIZE)) < 0)
return ret;
copy_frame(s);
reinit(s);
+ if (s->format == CODEC_JP2) {
+ av_assert0(s->buf == pkt->data);
+
+ bytestream_put_be32(&s->buf, 0x0000000C);
+ bytestream_put_be32(&s->buf, 0x6A502020);
+ bytestream_put_be32(&s->buf, 0x0D0A870A);
+
+ chunkstart = s->buf;
+ bytestream_put_be32(&s->buf, 0);
+ bytestream_put_buffer(&s->buf, "ftyp", 4);
+ bytestream_put_buffer(&s->buf, "jp2\040\040", 4);
+ bytestream_put_be32(&s->buf, 0);
+ update_size(chunkstart, s->buf);
+
+ jp2hstart = s->buf;
+ bytestream_put_be32(&s->buf, 0);
+ bytestream_put_buffer(&s->buf, "jp2h", 4);
+
+ chunkstart = s->buf;
+ bytestream_put_be32(&s->buf, 0);
+ bytestream_put_buffer(&s->buf, "ihdr", 4);
+ bytestream_put_be32(&s->buf, avctx->height);
+ bytestream_put_be32(&s->buf, avctx->width);
+ bytestream_put_be16(&s->buf, s->ncomponents);
+ bytestream_put_byte(&s->buf, s->cbps[0]);
+ bytestream_put_byte(&s->buf, 7);
+ bytestream_put_byte(&s->buf, 0);
+ bytestream_put_byte(&s->buf, 0);
+ update_size(chunkstart, s->buf);
+
+ chunkstart = s->buf;
+ bytestream_put_be32(&s->buf, 0);
+ bytestream_put_buffer(&s->buf, "colr", 4);
+ bytestream_put_byte(&s->buf, 1);
+ bytestream_put_byte(&s->buf, 0);
+ bytestream_put_byte(&s->buf, 0);
+ if (s->ncomponents == 1) {
+ bytestream_put_be32(&s->buf, 17);
+ } else if (avctx->pix_fmt == AV_PIX_FMT_RGB24) {
+ bytestream_put_be32(&s->buf, 16);
+ } else {
+ bytestream_put_be32(&s->buf, 18);
+ }
+ update_size(chunkstart, s->buf);
+ update_size(jp2hstart, s->buf);
+
+ jp2cstart = s->buf;
+ bytestream_put_be32(&s->buf, 0);
+ bytestream_put_buffer(&s->buf, "jp2c", 4);
+ }
+
if (s->buf_end - s->buf < 2)
return -1;
bytestream_put_be16(&s->buf, JPEG2000_SOC);
return -1;
bytestream_put_be16(&s->buf, JPEG2000_EOC);
+ if (s->format == CODEC_JP2)
+ update_size(jp2cstart, s->buf);
+
av_log(s->avctx, AV_LOG_DEBUG, "end\n");
pkt->size = s->buf - s->buf_start;
pkt->flags |= AV_PKT_FLAG_KEY;
return 0;
}
+// taken from the libopenjpeg wraper so it matches
+
+#define OFFSET(x) offsetof(Jpeg2000EncoderContext, x)
+#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
+static const AVOption options[] = {
+ { "format", "Codec Format", OFFSET(format), AV_OPT_TYPE_INT, { .i64 = CODEC_JP2 }, CODEC_J2K, CODEC_JP2, VE, "format" },
+ { "j2k", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_J2K }, 0, 0, VE, "format" },
+ { "jp2", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = CODEC_JP2 }, 0, 0, VE, "format" },
+ { NULL }
+};
+
+static const AVClass j2k_class = {
+ .class_name = "jpeg 2000 encoder",
+ .item_name = av_default_item_name,
+ .option = options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVCodec ff_jpeg2000_encoder = {
.name = "jpeg2000",
.long_name = NULL_IF_CONFIG_SMALL("JPEG 2000"),
AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P,
AV_PIX_FMT_YUV410P, AV_PIX_FMT_YUV411P,*/
AV_PIX_FMT_NONE
- }
+ },
+ .priv_class = &j2k_class,
};