*
* Copyright (c) 2012 Konstantin Shishkov
*
+ * This encoder appears to be based on Anatoliy Wassermans considering
+ * similarities in the bugs.
+ *
* This file is part of Libav.
*
* Libav is free software; you can redistribute it and/or
PRORES_PROFILE_HQ,
};
+enum {
+ QUANT_MAT_PROXY = 0,
+ QUANT_MAT_LT,
+ QUANT_MAT_STANDARD,
+ QUANT_MAT_HQ,
+ QUANT_MAT_DEFAULT,
+};
+
+static const uint8_t prores_quant_matrices[][64] = {
+ { // proxy
+ 4, 7, 9, 11, 13, 14, 15, 63,
+ 7, 7, 11, 12, 14, 15, 63, 63,
+ 9, 11, 13, 14, 15, 63, 63, 63,
+ 11, 11, 13, 14, 63, 63, 63, 63,
+ 11, 13, 14, 63, 63, 63, 63, 63,
+ 13, 14, 63, 63, 63, 63, 63, 63,
+ 13, 63, 63, 63, 63, 63, 63, 63,
+ 63, 63, 63, 63, 63, 63, 63, 63,
+ },
+ { // LT
+ 4, 5, 6, 7, 9, 11, 13, 15,
+ 5, 5, 7, 8, 11, 13, 15, 17,
+ 6, 7, 9, 11, 13, 15, 15, 17,
+ 7, 7, 9, 11, 13, 15, 17, 19,
+ 7, 9, 11, 13, 14, 16, 19, 23,
+ 9, 11, 13, 14, 16, 19, 23, 29,
+ 9, 11, 13, 15, 17, 21, 28, 35,
+ 11, 13, 16, 17, 21, 28, 35, 41,
+ },
+ { // standard
+ 4, 4, 5, 5, 6, 7, 7, 9,
+ 4, 4, 5, 6, 7, 7, 9, 9,
+ 5, 5, 6, 7, 7, 9, 9, 10,
+ 5, 5, 6, 7, 7, 9, 9, 10,
+ 5, 6, 7, 7, 8, 9, 10, 12,
+ 6, 7, 7, 8, 9, 10, 12, 15,
+ 6, 7, 7, 9, 10, 11, 14, 17,
+ 7, 7, 9, 10, 11, 14, 17, 21,
+ },
+ { // high quality
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 5,
+ 4, 4, 4, 4, 4, 4, 5, 5,
+ 4, 4, 4, 4, 4, 5, 5, 6,
+ 4, 4, 4, 4, 5, 5, 6, 7,
+ 4, 4, 4, 4, 5, 6, 7, 7,
+ },
+ { // codec default
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ },
+};
+
#define NUM_MB_LIMITS 4
static const int prores_mb_limits[NUM_MB_LIMITS] = {
1620, // up to 720x576
int min_quant;
int max_quant;
int br_tab[NUM_MB_LIMITS];
- uint8_t quant[64];
+ int quant;
} prores_profile_info[4] = {
{
.full_name = "proxy",
.min_quant = 4,
.max_quant = 8,
.br_tab = { 300, 242, 220, 194 },
- .quant = {
- 4, 7, 9, 11, 13, 14, 15, 63,
- 7, 7, 11, 12, 14, 15, 63, 63,
- 9, 11, 13, 14, 15, 63, 63, 63,
- 11, 11, 13, 14, 63, 63, 63, 63,
- 11, 13, 14, 63, 63, 63, 63, 63,
- 13, 14, 63, 63, 63, 63, 63, 63,
- 13, 63, 63, 63, 63, 63, 63, 63,
- 63, 63, 63, 63, 63, 63, 63, 63,
- },
+ .quant = QUANT_MAT_PROXY,
},
{
.full_name = "LT",
.min_quant = 1,
.max_quant = 9,
.br_tab = { 720, 560, 490, 440 },
- .quant = {
- 4, 5, 6, 7, 9, 11, 13, 15,
- 5, 5, 7, 8, 11, 13, 15, 17,
- 6, 7, 9, 11, 13, 15, 15, 17,
- 7, 7, 9, 11, 13, 15, 17, 19,
- 7, 9, 11, 13, 14, 16, 19, 23,
- 9, 11, 13, 14, 16, 19, 23, 29,
- 9, 11, 13, 15, 17, 21, 28, 35,
- 11, 13, 16, 17, 21, 28, 35, 41,
- },
+ .quant = QUANT_MAT_LT,
},
{
.full_name = "standard",
.min_quant = 1,
.max_quant = 6,
.br_tab = { 1050, 808, 710, 632 },
- .quant = {
- 4, 4, 5, 5, 6, 7, 7, 9,
- 4, 4, 5, 6, 7, 7, 9, 9,
- 5, 5, 6, 7, 7, 9, 9, 10,
- 5, 5, 6, 7, 7, 9, 9, 10,
- 5, 6, 7, 7, 8, 9, 10, 12,
- 6, 7, 7, 8, 9, 10, 12, 15,
- 6, 7, 7, 9, 10, 11, 14, 17,
- 7, 7, 9, 10, 11, 14, 17, 21,
- },
+ .quant = QUANT_MAT_STANDARD,
},
{
.full_name = "high quality",
.min_quant = 1,
.max_quant = 6,
.br_tab = { 1566, 1216, 1070, 950 },
- .quant = {
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 5,
- 4, 4, 4, 4, 4, 4, 5, 5,
- 4, 4, 4, 4, 4, 5, 5, 6,
- 4, 4, 4, 4, 5, 5, 6, 7,
- 4, 4, 4, 4, 5, 6, 7, 7,
- },
+ .quant = QUANT_MAT_HQ,
}
// for 4444 profile bitrate numbers are { 2350, 1828, 1600, 1425 }
};
#define MAX_STORED_Q 16
+typedef struct ProresThreadData {
+ DECLARE_ALIGNED(16, DCTELEM, blocks)[MAX_PLANES][64 * 4 * MAX_MBS_PER_SLICE];
+ DECLARE_ALIGNED(16, uint16_t, emu_buf)[16 * 16];
+ int16_t custom_q[64];
+ struct TrellisNode *nodes;
+} ProresThreadData;
+
typedef struct ProresContext {
AVClass *class;
DECLARE_ALIGNED(16, DCTELEM, blocks)[MAX_PLANES][64 * 4 * MAX_MBS_PER_SLICE];
DECLARE_ALIGNED(16, uint16_t, emu_buf)[16*16];
int16_t quants[MAX_STORED_Q][64];
int16_t custom_q[64];
+ const uint8_t *quant_mat;
ProresDSPContext dsp;
ScanTable scantable;
int num_slices;
int num_planes;
int bits_per_mb;
+ int force_quant;
+
+ char *vendor;
+ int quant_sel;
int frame_size;
int profile;
const struct prores_profile *profile_info;
- struct TrellisNode *nodes;
int *slice_q;
+
+ ProresThreadData *tdata;
} ProresContext;
static void get_slice_data(ProresContext *ctx, const uint16_t *src,
int linesize, int x, int y, int w, int h,
- DCTELEM *blocks,
- int mbs_per_slice, int blocks_per_mb)
+ DCTELEM *blocks, uint16_t *emu_buf,
+ int mbs_per_slice, int blocks_per_mb, int is_chroma)
{
const uint16_t *esrc;
const int mb_width = 4 * blocks_per_mb;
elinesize = linesize;
} else {
int bw, bh, pix;
- const int estride = 16 / sizeof(*ctx->emu_buf);
- esrc = ctx->emu_buf;
- elinesize = 16;
+ esrc = emu_buf;
+ elinesize = 16 * sizeof(*emu_buf);
bw = FFMIN(w - x, mb_width);
bh = FFMIN(h - y, 16);
for (j = 0; j < bh; j++) {
- memcpy(ctx->emu_buf + j * estride, src + j * linesize,
+ memcpy(emu_buf + j * 16,
+ (const uint8_t*)src + j * linesize,
bw * sizeof(*src));
- pix = ctx->emu_buf[j * estride + bw - 1];
+ pix = emu_buf[j * 16 + bw - 1];
for (k = bw; k < mb_width; k++)
- ctx->emu_buf[j * estride + k] = pix;
+ emu_buf[j * 16 + k] = pix;
}
for (; j < 16; j++)
- memcpy(ctx->emu_buf + j * estride,
- ctx->emu_buf + (bh - 1) * estride,
- mb_width * sizeof(*ctx->emu_buf));
+ memcpy(emu_buf + j * 16,
+ emu_buf + (bh - 1) * 16,
+ mb_width * sizeof(*emu_buf));
}
- ctx->dsp.fdct(esrc, elinesize, blocks);
- blocks += 64;
- if (blocks_per_mb > 2) {
- ctx->dsp.fdct(src + 8, linesize, blocks);
+ if (!is_chroma) {
+ ctx->dsp.fdct(esrc, elinesize, blocks);
blocks += 64;
- }
- ctx->dsp.fdct(src + linesize * 4, linesize, blocks);
- blocks += 64;
- if (blocks_per_mb > 2) {
- ctx->dsp.fdct(src + linesize * 4 + 8, linesize, blocks);
+ if (blocks_per_mb > 2) {
+ ctx->dsp.fdct(src + 8, linesize, blocks);
+ blocks += 64;
+ }
+ ctx->dsp.fdct(src + linesize * 4, linesize, blocks);
+ blocks += 64;
+ if (blocks_per_mb > 2) {
+ ctx->dsp.fdct(src + linesize * 4 + 8, linesize, blocks);
+ blocks += 64;
+ }
+ } else {
+ ctx->dsp.fdct(esrc, elinesize, blocks);
+ blocks += 64;
+ ctx->dsp.fdct(src + linesize * 4, linesize, blocks);
blocks += 64;
+ if (blocks_per_mb > 2) {
+ ctx->dsp.fdct(src + 8, linesize, blocks);
+ blocks += 64;
+ ctx->dsp.fdct(src + linesize * 4 + 8, linesize, blocks);
+ blocks += 64;
+ }
}
x += mb_width;
/**
* Write an unsigned rice/exp golomb codeword.
*/
-static inline void encode_vlc_codeword(PutBitContext *pb, uint8_t codebook, int val)
+static inline void encode_vlc_codeword(PutBitContext *pb, unsigned codebook, int val)
{
unsigned int rice_order, exp_order, switch_bits, switch_val;
int exponent;
exponent = av_log2(val);
put_bits(pb, exponent - exp_order + switch_bits, 0);
- put_bits(pb, 1, 1);
- put_bits(pb, exponent, val);
+ put_bits(pb, exponent + 1, val);
} else {
exponent = val >> rice_order;
int plane_factor, is_chroma;
uint16_t *qmat;
- if (quant < MAX_STORED_Q) {
+ if (ctx->force_quant) {
+ qmat = ctx->quants[0];
+ } else if (quant < MAX_STORED_Q) {
qmat = ctx->quants[quant];
} else {
qmat = ctx->custom_q;
for (i = 0; i < 64; i++)
- qmat[i] = ctx->profile_info->quant[i] * quant;
+ qmat[i] = ctx->quant_mat[i] * quant;
}
for (i = 0; i < ctx->num_planes; i++) {
src = (const uint16_t*)(pic->data[i] + yp * pic->linesize[i]) + xp;
get_slice_data(ctx, src, pic->linesize[i], xp, yp,
- pwidth, avctx->height, ctx->blocks[0],
- mbs_per_slice, num_cblocks);
+ pwidth, avctx->height, ctx->blocks[0], ctx->emu_buf,
+ mbs_per_slice, num_cblocks, is_chroma);
sizes[i] = encode_slice_plane(ctx, pb, src, pic->linesize[i],
mbs_per_slice, ctx->blocks[0],
num_cblocks, plane_factor,
return total_size;
}
-static inline int estimate_vlc(uint8_t codebook, int val)
+static inline int estimate_vlc(unsigned codebook, int val)
{
unsigned int rice_order, exp_order, switch_bits, switch_val;
int exponent;
const uint16_t *src, int linesize,
int mbs_per_slice,
int blocks_per_mb, int plane_size_factor,
- const int16_t *qmat)
+ const int16_t *qmat, ProresThreadData *td)
{
int blocks_per_slice;
int bits;
blocks_per_slice = mbs_per_slice * blocks_per_mb;
- bits = estimate_dcs(error, ctx->blocks[plane], blocks_per_slice, qmat[0]);
- bits += estimate_acs(error, ctx->blocks[plane], blocks_per_slice,
+ bits = estimate_dcs(error, td->blocks[plane], blocks_per_slice, qmat[0]);
+ bits += estimate_acs(error, td->blocks[plane], blocks_per_slice,
plane_size_factor, ctx->scantable.permutated, qmat);
return FFALIGN(bits, 8);
}
static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic,
- int trellis_node, int x, int y, int mbs_per_slice)
+ int trellis_node, int x, int y, int mbs_per_slice,
+ ProresThreadData *td)
{
ProresContext *ctx = avctx->priv_data;
int i, q, pq, xp, yp;
src = (const uint16_t*)(pic->data[i] + yp * pic->linesize[i]) + xp;
get_slice_data(ctx, src, pic->linesize[i], xp, yp,
- pwidth, avctx->height, ctx->blocks[i],
- mbs_per_slice, num_cblocks[i]);
+ pwidth, avctx->height, td->blocks[i], td->emu_buf,
+ mbs_per_slice, num_cblocks[i], is_chroma[i]);
}
for (q = min_quant; q < max_quant + 2; q++) {
- ctx->nodes[trellis_node + q].prev_node = -1;
- ctx->nodes[trellis_node + q].quant = q;
+ td->nodes[trellis_node + q].prev_node = -1;
+ td->nodes[trellis_node + q].quant = q;
}
// todo: maybe perform coarser quantising to fit into frame size when needed
src, pic->linesize[i],
mbs_per_slice,
num_cblocks[i], plane_factor[i],
- ctx->quants[q]);
+ ctx->quants[q], td);
}
if (bits > 65000 * 8) {
error = SCORE_LIMIT;
if (q < MAX_STORED_Q) {
qmat = ctx->quants[q];
} else {
- qmat = ctx->custom_q;
+ qmat = td->custom_q;
for (i = 0; i < 64; i++)
- qmat[i] = ctx->profile_info->quant[i] * q;
+ qmat[i] = ctx->quant_mat[i] * q;
}
for (i = 0; i < ctx->num_planes; i++) {
bits += estimate_slice_plane(ctx, &error, i,
src, pic->linesize[i],
mbs_per_slice,
num_cblocks[i], plane_factor[i],
- qmat);
+ qmat, td);
}
if (bits <= ctx->bits_per_mb * mbs_per_slice)
break;
slice_score[max_quant + 1] = error;
overquant = q;
}
- ctx->nodes[trellis_node + max_quant + 1].quant = overquant;
+ td->nodes[trellis_node + max_quant + 1].quant = overquant;
bits_limit = mbs * ctx->bits_per_mb;
for (pq = min_quant; pq < max_quant + 2; pq++) {
for (q = min_quant; q < max_quant + 2; q++) {
cur = trellis_node + q;
- bits = ctx->nodes[prev].bits + slice_bits[q];
+ bits = td->nodes[prev].bits + slice_bits[q];
error = slice_score[q];
if (bits > bits_limit)
error = SCORE_LIMIT;
- if (ctx->nodes[prev].score < SCORE_LIMIT && error < SCORE_LIMIT)
- new_score = ctx->nodes[prev].score + error;
+ if (td->nodes[prev].score < SCORE_LIMIT && error < SCORE_LIMIT)
+ new_score = td->nodes[prev].score + error;
else
new_score = SCORE_LIMIT;
- if (ctx->nodes[cur].prev_node == -1 ||
- ctx->nodes[cur].score >= new_score) {
+ if (td->nodes[cur].prev_node == -1 ||
+ td->nodes[cur].score >= new_score) {
- ctx->nodes[cur].bits = bits;
- ctx->nodes[cur].score = new_score;
- ctx->nodes[cur].prev_node = prev;
+ td->nodes[cur].bits = bits;
+ td->nodes[cur].score = new_score;
+ td->nodes[cur].prev_node = prev;
}
}
}
- error = ctx->nodes[trellis_node + min_quant].score;
+ error = td->nodes[trellis_node + min_quant].score;
pq = trellis_node + min_quant;
for (q = min_quant + 1; q < max_quant + 2; q++) {
- if (ctx->nodes[trellis_node + q].score <= error) {
- error = ctx->nodes[trellis_node + q].score;
+ if (td->nodes[trellis_node + q].score <= error) {
+ error = td->nodes[trellis_node + q].score;
pq = trellis_node + q;
}
}
return pq;
}
+static int find_quant_thread(AVCodecContext *avctx, void *arg,
+ int jobnr, int threadnr)
+{
+ ProresContext *ctx = avctx->priv_data;
+ ProresThreadData *td = ctx->tdata + threadnr;
+ int mbs_per_slice = ctx->mbs_per_slice;
+ int x, y = jobnr, mb, q = 0;
+
+ for (x = mb = 0; x < ctx->mb_width; x += mbs_per_slice, mb++) {
+ while (ctx->mb_width - x < mbs_per_slice)
+ mbs_per_slice >>= 1;
+ q = find_slice_quant(avctx, avctx->coded_frame,
+ (mb + 1) * TRELLIS_WIDTH, x, y,
+ mbs_per_slice, td);
+ }
+
+ for (x = ctx->slices_width - 1; x >= 0; x--) {
+ ctx->slice_q[x + y * ctx->slices_width] = td->nodes[q].quant;
+ q = td->nodes[q].prev_node;
+ }
+
+ return 0;
+}
+
static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *pic, int *got_packet)
{
pkt_size = ctx->frame_size + FF_MIN_BUFFER_SIZE;
- if ((ret = ff_alloc_packet(pkt, pkt_size)) < 0) {
- av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n");
+ if ((ret = ff_alloc_packet2(avctx, pkt, pkt_size)) < 0)
return ret;
- }
orig_buf = pkt->data;
tmp = buf;
buf += 2; // frame header size will be stored here
bytestream_put_be16 (&buf, 0); // version 1
- bytestream_put_buffer(&buf, "Lavc", 4); // creator
+ bytestream_put_buffer(&buf, ctx->vendor, 4);
bytestream_put_be16 (&buf, avctx->width);
bytestream_put_be16 (&buf, avctx->height);
bytestream_put_byte (&buf, ctx->chroma_factor << 6); // frame flags
bytestream_put_byte (&buf, 0); // reserved
- bytestream_put_byte (&buf, 0); // primaries
- bytestream_put_byte (&buf, 0); // transfer function
- bytestream_put_byte (&buf, 6); // colour matrix - ITU-R BT.601-4
+ bytestream_put_byte (&buf, avctx->color_primaries);
+ bytestream_put_byte (&buf, avctx->color_trc);
+ bytestream_put_byte (&buf, avctx->colorspace);
bytestream_put_byte (&buf, 0x40); // source format and alpha information
bytestream_put_byte (&buf, 0); // reserved
- bytestream_put_byte (&buf, 0x03); // matrix flags - both matrices are present
- // luma quantisation matrix
- for (i = 0; i < 64; i++)
- bytestream_put_byte(&buf, ctx->profile_info->quant[i]);
- // chroma quantisation matrix
- for (i = 0; i < 64; i++)
- bytestream_put_byte(&buf, ctx->profile_info->quant[i]);
+ if (ctx->quant_sel != QUANT_MAT_DEFAULT) {
+ bytestream_put_byte (&buf, 0x03); // matrix flags - both matrices are present
+ // luma quantisation matrix
+ for (i = 0; i < 64; i++)
+ bytestream_put_byte(&buf, ctx->quant_mat[i]);
+ // chroma quantisation matrix
+ for (i = 0; i < 64; i++)
+ bytestream_put_byte(&buf, ctx->quant_mat[i]);
+ } else {
+ bytestream_put_byte (&buf, 0x00); // matrix flags - default matrices are used
+ }
bytestream_put_be16 (&tmp, buf - orig_buf); // write back frame header size
// picture header
buf += ctx->num_slices * 2;
// slices
- for (y = 0; y < ctx->mb_height; y++) {
- mbs_per_slice = ctx->mbs_per_slice;
- for (x = mb = 0; x < ctx->mb_width; x += mbs_per_slice, mb++) {
- while (ctx->mb_width - x < mbs_per_slice)
- mbs_per_slice >>= 1;
- q = find_slice_quant(avctx, pic, (mb + 1) * TRELLIS_WIDTH, x, y,
- mbs_per_slice);
- }
-
- for (x = ctx->slices_width - 1; x >= 0; x--) {
- ctx->slice_q[x] = ctx->nodes[q].quant;
- q = ctx->nodes[q].prev_node;
- }
+ if (!ctx->force_quant) {
+ ret = avctx->execute2(avctx, find_quant_thread, NULL, NULL,
+ ctx->mb_height);
+ if (ret)
+ return ret;
+ }
+ for (y = 0; y < ctx->mb_height; y++) {
mbs_per_slice = ctx->mbs_per_slice;
for (x = mb = 0; x < ctx->mb_width; x += mbs_per_slice, mb++) {
- q = ctx->slice_q[mb];
+ q = ctx->force_quant ? ctx->force_quant
+ : ctx->slice_q[mb + y * ctx->slices_width];
while (ctx->mb_width - x < mbs_per_slice)
mbs_per_slice >>= 1;
static av_cold int encode_close(AVCodecContext *avctx)
{
ProresContext *ctx = avctx->priv_data;
+ int i;
if (avctx->coded_frame->data[0])
avctx->release_buffer(avctx, avctx->coded_frame);
av_freep(&avctx->coded_frame);
- av_freep(&ctx->nodes);
+ if (ctx->tdata) {
+ for (i = 0; i < avctx->thread_count; i++)
+ av_free(ctx->tdata[i].nodes);
+ }
+ av_freep(&ctx->tdata);
av_freep(&ctx->slice_q);
return 0;
ctx->slices_width += av_popcount(ctx->mb_width - ctx->slices_width * mps);
ctx->num_slices = ctx->mb_height * ctx->slices_width;
- for (i = 0; i < NUM_MB_LIMITS - 1; i++)
- if (prores_mb_limits[i] >= ctx->mb_width * ctx->mb_height)
- break;
- ctx->bits_per_mb = ctx->profile_info->br_tab[i];
+ if (ctx->quant_sel == -1)
+ ctx->quant_mat = prores_quant_matrices[ctx->profile_info->quant];
+ else
+ ctx->quant_mat = prores_quant_matrices[ctx->quant_sel];
+
+ if (strlen(ctx->vendor) != 4) {
+ av_log(avctx, AV_LOG_ERROR, "vendor ID should be 4 bytes\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ ctx->force_quant = avctx->global_quality / FF_QP2LAMBDA;
+ if (!ctx->force_quant) {
+ if (!ctx->bits_per_mb) {
+ for (i = 0; i < NUM_MB_LIMITS - 1; i++)
+ if (prores_mb_limits[i] >= ctx->mb_width * ctx->mb_height)
+ break;
+ ctx->bits_per_mb = ctx->profile_info->br_tab[i];
+ } else if (ctx->bits_per_mb < 128) {
+ av_log(avctx, AV_LOG_ERROR, "too few bits per MB, please set at least 128\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ min_quant = ctx->profile_info->min_quant;
+ max_quant = ctx->profile_info->max_quant;
+ for (i = min_quant; i < MAX_STORED_Q; i++) {
+ for (j = 0; j < 64; j++)
+ ctx->quants[i][j] = ctx->quant_mat[j] * i;
+ }
+
+ ctx->slice_q = av_malloc(ctx->num_slices * sizeof(*ctx->slice_q));
+ if (!ctx->slice_q) {
+ encode_close(avctx);
+ return AVERROR(ENOMEM);
+ }
+
+ ctx->tdata = av_mallocz(avctx->thread_count * sizeof(*ctx->tdata));
+ if (!ctx->tdata) {
+ encode_close(avctx);
+ return AVERROR(ENOMEM);
+ }
+
+ for (j = 0; j < avctx->thread_count; j++) {
+ ctx->tdata[j].nodes = av_malloc((ctx->slices_width + 1)
+ * TRELLIS_WIDTH
+ * sizeof(*ctx->tdata->nodes));
+ if (!ctx->tdata[j].nodes) {
+ encode_close(avctx);
+ return AVERROR(ENOMEM);
+ }
+ for (i = min_quant; i < max_quant + 2; i++) {
+ ctx->tdata[j].nodes[i].prev_node = -1;
+ ctx->tdata[j].nodes[i].bits = 0;
+ ctx->tdata[j].nodes[i].score = 0;
+ }
+ }
+ } else {
+ int ls = 0;
+
+ if (ctx->force_quant > 64) {
+ av_log(avctx, AV_LOG_ERROR, "too large quantiser, maximum is 64\n");
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (j = 0; j < 64; j++) {
+ ctx->quants[0][j] = ctx->quant_mat[j] * ctx->force_quant;
+ ls += av_log2((1 << 11) / ctx->quants[0][j]) * 2 + 1;
+ }
+
+ ctx->bits_per_mb = ls * 8;
+ if (ctx->chroma_factor == CFACTOR_Y444)
+ ctx->bits_per_mb += ls * 4;
+ if (ctx->num_planes == 4)
+ ctx->bits_per_mb += ls * 4;
+ }
ctx->frame_size = ctx->num_slices * (2 + 2 * ctx->num_planes
+ (2 * mps * ctx->bits_per_mb) / 8)
+ 200;
- min_quant = ctx->profile_info->min_quant;
- max_quant = ctx->profile_info->max_quant;
- for (i = min_quant; i < MAX_STORED_Q; i++) {
- for (j = 0; j < 64; j++)
- ctx->quants[i][j] = ctx->profile_info->quant[j] * i;
- }
-
avctx->codec_tag = ctx->profile_info->tag;
av_log(avctx, AV_LOG_DEBUG, "profile %d, %d slices, %d bits per MB\n",
av_log(avctx, AV_LOG_DEBUG, "estimated frame size %d\n",
ctx->frame_size);
- ctx->nodes = av_malloc((ctx->slices_width + 1) * TRELLIS_WIDTH
- * sizeof(*ctx->nodes));
- if (!ctx->nodes) {
- encode_close(avctx);
- return AVERROR(ENOMEM);
- }
- for (i = min_quant; i < max_quant + 2; i++) {
- ctx->nodes[i].prev_node = -1;
- ctx->nodes[i].bits = 0;
- ctx->nodes[i].score = 0;
- }
-
- ctx->slice_q = av_malloc(ctx->slices_width * sizeof(*ctx->slice_q));
- if (!ctx->slice_q) {
- encode_close(avctx);
- return AVERROR(ENOMEM);
- }
-
return 0;
}
0, 0, VE, "profile" },
{ "hq", NULL, 0, AV_OPT_TYPE_CONST, { PRORES_PROFILE_HQ },
0, 0, VE, "profile" },
+ { "vendor", "vendor ID", OFFSET(vendor),
+ AV_OPT_TYPE_STRING, { .str = "Lavc" }, CHAR_MIN, CHAR_MAX, VE },
+ { "bits_per_mb", "desired bits per macroblock", OFFSET(bits_per_mb),
+ AV_OPT_TYPE_INT, { 0 }, 0, 8192, VE },
+ { "quant_mat", "quantiser matrix", OFFSET(quant_sel), AV_OPT_TYPE_INT,
+ { -1 }, -1, QUANT_MAT_DEFAULT, VE, "quant_mat" },
+ { "auto", NULL, 0, AV_OPT_TYPE_CONST, { -1 },
+ 0, 0, VE, "quant_mat" },
+ { "proxy", NULL, 0, AV_OPT_TYPE_CONST, { QUANT_MAT_PROXY },
+ 0, 0, VE, "quant_mat" },
+ { "lt", NULL, 0, AV_OPT_TYPE_CONST, { QUANT_MAT_LT },
+ 0, 0, VE, "quant_mat" },
+ { "standard", NULL, 0, AV_OPT_TYPE_CONST, { QUANT_MAT_STANDARD },
+ 0, 0, VE, "quant_mat" },
+ { "hq", NULL, 0, AV_OPT_TYPE_CONST, { QUANT_MAT_HQ },
+ 0, 0, VE, "quant_mat" },
+ { "default", NULL, 0, AV_OPT_TYPE_CONST, { QUANT_MAT_DEFAULT },
+ 0, 0, VE, "quant_mat" },
{ NULL }
};
.init = encode_init,
.close = encode_close,
.encode2 = encode_frame,
+ .capabilities = CODEC_CAP_SLICE_THREADS,
.long_name = NULL_IF_CONFIG_SMALL("Apple ProRes (iCodec Pro)"),
.pix_fmts = (const enum PixelFormat[]) {
PIX_FMT_YUV422P10, PIX_FMT_YUV444P10, PIX_FMT_NONE