|
|
|
@ -24,6 +24,7 @@ |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
#include "libavutil/opt.h" |
|
|
|
|
#include "libavutil/pixdesc.h" |
|
|
|
|
#include "avcodec.h" |
|
|
|
|
#include "dsputil.h" |
|
|
|
|
#include "put_bits.h" |
|
|
|
@ -37,13 +38,14 @@ |
|
|
|
|
|
|
|
|
|
#define MAX_MBS_PER_SLICE 8 |
|
|
|
|
|
|
|
|
|
#define MAX_PLANES 3 // should be increased to 4 when there's AV_PIX_FMT_YUV444AP10
|
|
|
|
|
#define MAX_PLANES 4 |
|
|
|
|
|
|
|
|
|
enum { |
|
|
|
|
PRORES_PROFILE_PROXY = 0, |
|
|
|
|
PRORES_PROFILE_LT, |
|
|
|
|
PRORES_PROFILE_STANDARD, |
|
|
|
|
PRORES_PROFILE_HQ, |
|
|
|
|
PRORES_PROFILE_4444, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
enum { |
|
|
|
@ -122,7 +124,7 @@ static const struct prores_profile { |
|
|
|
|
int max_quant; |
|
|
|
|
int br_tab[NUM_MB_LIMITS]; |
|
|
|
|
int quant; |
|
|
|
|
} prores_profile_info[4] = { |
|
|
|
|
} prores_profile_info[5] = { |
|
|
|
|
{ |
|
|
|
|
.full_name = "proxy", |
|
|
|
|
.tag = MKTAG('a', 'p', 'c', 'o'), |
|
|
|
@ -154,8 +156,15 @@ static const struct prores_profile { |
|
|
|
|
.max_quant = 6, |
|
|
|
|
.br_tab = { 1566, 1216, 1070, 950 }, |
|
|
|
|
.quant = QUANT_MAT_HQ, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
.full_name = "4444", |
|
|
|
|
.tag = MKTAG('a', 'p', '4', 'h'), |
|
|
|
|
.min_quant = 1, |
|
|
|
|
.max_quant = 6, |
|
|
|
|
.br_tab = { 2350, 1828, 1600, 1425 }, |
|
|
|
|
.quant = QUANT_MAT_HQ, |
|
|
|
|
} |
|
|
|
|
// for 4444 profile bitrate numbers are { 2350, 1828, 1600, 1425 }
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
#define TRELLIS_WIDTH 16 |
|
|
|
@ -198,6 +207,7 @@ typedef struct ProresContext { |
|
|
|
|
int num_planes; |
|
|
|
|
int bits_per_mb; |
|
|
|
|
int force_quant; |
|
|
|
|
int alpha_bits; |
|
|
|
|
|
|
|
|
|
char *vendor; |
|
|
|
|
int quant_sel; |
|
|
|
@ -283,6 +293,34 @@ static void get_slice_data(ProresContext *ctx, const uint16_t *src, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void get_alpha_data(ProresContext *ctx, const uint16_t *src, |
|
|
|
|
int linesize, int x, int y, int w, int h, |
|
|
|
|
int16_t *blocks, int mbs_per_slice, int abits) |
|
|
|
|
{ |
|
|
|
|
const int slice_width = 16 * mbs_per_slice; |
|
|
|
|
int i, j, copy_w, copy_h; |
|
|
|
|
|
|
|
|
|
copy_w = FFMIN(w - x, slice_width); |
|
|
|
|
copy_h = FFMIN(h - y, 16); |
|
|
|
|
for (i = 0; i < copy_h; i++) { |
|
|
|
|
memcpy(blocks, src, copy_w * sizeof(*src)); |
|
|
|
|
if (abits == 8) |
|
|
|
|
for (j = 0; j < copy_w; j++) |
|
|
|
|
blocks[j] >>= 2; |
|
|
|
|
else |
|
|
|
|
for (j = 0; j < copy_w; j++) |
|
|
|
|
blocks[j] = (blocks[j] << 6) | (blocks[j] >> 4); |
|
|
|
|
for (j = copy_w; j < slice_width; j++) |
|
|
|
|
blocks[j] = blocks[copy_w - 1]; |
|
|
|
|
blocks += slice_width; |
|
|
|
|
src += linesize >> 1; |
|
|
|
|
} |
|
|
|
|
for (; i < 16; i++) { |
|
|
|
|
memcpy(blocks, blocks - slice_width, slice_width * sizeof(*blocks)); |
|
|
|
|
blocks += slice_width; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Write an unsigned rice/exp golomb codeword. |
|
|
|
|
*/ |
|
|
|
@ -397,6 +435,73 @@ static int encode_slice_plane(ProresContext *ctx, PutBitContext *pb, |
|
|
|
|
return (put_bits_count(pb) - saved_pos) >> 3; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void put_alpha_diff(PutBitContext *pb, int cur, int prev, int abits) |
|
|
|
|
{ |
|
|
|
|
const int mask = (1 << abits) - 1; |
|
|
|
|
const int dbits = (abits == 8) ? 4 : 7; |
|
|
|
|
const int dsize = 1 << dbits - 1; |
|
|
|
|
int diff = cur - prev; |
|
|
|
|
|
|
|
|
|
diff &= mask; |
|
|
|
|
if (diff >= (1 << abits) - dsize) |
|
|
|
|
diff -= 1 << abits; |
|
|
|
|
if (diff < -dsize || diff > dsize || !diff) { |
|
|
|
|
put_bits(pb, 1, 1); |
|
|
|
|
put_bits(pb, abits, diff); |
|
|
|
|
} else { |
|
|
|
|
put_bits(pb, 1, 0); |
|
|
|
|
put_bits(pb, dbits - 1, FFABS(diff) - 1); |
|
|
|
|
put_bits(pb, 1, diff < 0); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void put_alpha_run(PutBitContext *pb, int run) |
|
|
|
|
{ |
|
|
|
|
if (run) { |
|
|
|
|
put_bits(pb, 1, 0); |
|
|
|
|
if (run < 0x10) |
|
|
|
|
put_bits(pb, 4, run); |
|
|
|
|
else |
|
|
|
|
put_bits(pb, 15, run); |
|
|
|
|
} else { |
|
|
|
|
put_bits(pb, 1, 1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// todo alpha quantisation for high quants
|
|
|
|
|
static int encode_alpha_plane(ProresContext *ctx, PutBitContext *pb, |
|
|
|
|
const uint16_t *src, int linesize, |
|
|
|
|
int mbs_per_slice, uint16_t *blocks, |
|
|
|
|
int quant) |
|
|
|
|
{ |
|
|
|
|
const int abits = ctx->alpha_bits; |
|
|
|
|
const int mask = (1 << abits) - 1; |
|
|
|
|
const int num_coeffs = mbs_per_slice * 256; |
|
|
|
|
int saved_pos = put_bits_count(pb); |
|
|
|
|
int prev = mask, cur; |
|
|
|
|
int idx = 0; |
|
|
|
|
int run = 0; |
|
|
|
|
|
|
|
|
|
cur = blocks[idx++]; |
|
|
|
|
put_alpha_diff(pb, cur, prev, abits); |
|
|
|
|
prev = cur; |
|
|
|
|
do { |
|
|
|
|
cur = blocks[idx++]; |
|
|
|
|
if (cur != prev) { |
|
|
|
|
put_alpha_run (pb, run); |
|
|
|
|
put_alpha_diff(pb, cur, prev, abits); |
|
|
|
|
prev = cur; |
|
|
|
|
run = 0; |
|
|
|
|
} else { |
|
|
|
|
run++; |
|
|
|
|
} |
|
|
|
|
} while (idx < num_coeffs); |
|
|
|
|
if (run) |
|
|
|
|
put_alpha_run(pb, run); |
|
|
|
|
flush_put_bits(pb); |
|
|
|
|
return (put_bits_count(pb) - saved_pos) >> 3; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, |
|
|
|
|
PutBitContext *pb, |
|
|
|
|
int sizes[4], int x, int y, int quant, |
|
|
|
@ -447,14 +552,23 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, |
|
|
|
|
src = (const uint16_t*)(pic->data[i] + yp * linesize + |
|
|
|
|
line_add * pic->linesize[i]) + xp; |
|
|
|
|
|
|
|
|
|
get_slice_data(ctx, src, linesize, xp, yp, |
|
|
|
|
pwidth, avctx->height / ctx->pictures_per_frame, |
|
|
|
|
ctx->blocks[0], ctx->emu_buf, |
|
|
|
|
mbs_per_slice, num_cblocks, is_chroma); |
|
|
|
|
sizes[i] = encode_slice_plane(ctx, pb, src, linesize, |
|
|
|
|
mbs_per_slice, ctx->blocks[0], |
|
|
|
|
num_cblocks, plane_factor, |
|
|
|
|
qmat); |
|
|
|
|
if (i < 3) { |
|
|
|
|
get_slice_data(ctx, src, linesize, xp, yp, |
|
|
|
|
pwidth, avctx->height / ctx->pictures_per_frame, |
|
|
|
|
ctx->blocks[0], ctx->emu_buf, |
|
|
|
|
mbs_per_slice, num_cblocks, is_chroma); |
|
|
|
|
sizes[i] = encode_slice_plane(ctx, pb, src, linesize, |
|
|
|
|
mbs_per_slice, ctx->blocks[0], |
|
|
|
|
num_cblocks, plane_factor, |
|
|
|
|
qmat); |
|
|
|
|
} else { |
|
|
|
|
get_alpha_data(ctx, src, linesize, xp, yp, |
|
|
|
|
pwidth, avctx->height / ctx->pictures_per_frame, |
|
|
|
|
ctx->blocks[0], mbs_per_slice, ctx->alpha_bits); |
|
|
|
|
sizes[i] = encode_alpha_plane(ctx, pb, src, linesize, |
|
|
|
|
mbs_per_slice, ctx->blocks[0], |
|
|
|
|
quant); |
|
|
|
|
} |
|
|
|
|
total_size += sizes[i]; |
|
|
|
|
} |
|
|
|
|
return total_size; |
|
|
|
@ -567,6 +681,66 @@ static int estimate_slice_plane(ProresContext *ctx, int *error, int plane, |
|
|
|
|
return FFALIGN(bits, 8); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int est_alpha_diff(int cur, int prev, int abits) |
|
|
|
|
{ |
|
|
|
|
const int mask = (1 << abits) - 1; |
|
|
|
|
const int dbits = (abits == 8) ? 4 : 7; |
|
|
|
|
const int dsize = 1 << dbits - 1; |
|
|
|
|
int diff = cur - prev; |
|
|
|
|
|
|
|
|
|
diff &= mask; |
|
|
|
|
if (diff >= (1 << abits) - dsize) |
|
|
|
|
diff -= 1 << abits; |
|
|
|
|
if (diff < -dsize || diff > dsize || !diff) |
|
|
|
|
return abits + 1; |
|
|
|
|
else |
|
|
|
|
return dbits + 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int estimate_alpha_plane(ProresContext *ctx, int *error, |
|
|
|
|
const uint16_t *src, int linesize, |
|
|
|
|
int mbs_per_slice, int quant, |
|
|
|
|
int16_t *blocks) |
|
|
|
|
{ |
|
|
|
|
const int abits = ctx->alpha_bits; |
|
|
|
|
const int mask = (1 << abits) - 1; |
|
|
|
|
const int num_coeffs = mbs_per_slice * 256; |
|
|
|
|
int prev = mask, cur; |
|
|
|
|
int idx = 0; |
|
|
|
|
int run = 0; |
|
|
|
|
int bits; |
|
|
|
|
|
|
|
|
|
*error = 0; |
|
|
|
|
cur = blocks[idx++]; |
|
|
|
|
bits = est_alpha_diff(cur, prev, abits); |
|
|
|
|
prev = cur; |
|
|
|
|
do { |
|
|
|
|
cur = blocks[idx++]; |
|
|
|
|
if (cur != prev) { |
|
|
|
|
if (!run) |
|
|
|
|
bits++; |
|
|
|
|
else if (run < 0x10) |
|
|
|
|
bits += 4; |
|
|
|
|
else |
|
|
|
|
bits += 15; |
|
|
|
|
bits += est_alpha_diff(cur, prev, abits); |
|
|
|
|
prev = cur; |
|
|
|
|
run = 0; |
|
|
|
|
} else { |
|
|
|
|
run++; |
|
|
|
|
} |
|
|
|
|
} while (idx < num_coeffs); |
|
|
|
|
|
|
|
|
|
if (run) { |
|
|
|
|
if (run < 0x10) |
|
|
|
|
bits += 4; |
|
|
|
|
else |
|
|
|
|
bits += 15; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return bits; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic, |
|
|
|
|
int trellis_node, int x, int y, int mbs_per_slice, |
|
|
|
|
ProresThreadData *td) |
|
|
|
@ -613,10 +787,16 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic, |
|
|
|
|
src = (const uint16_t*)(pic->data[i] + yp * linesize[i] + |
|
|
|
|
line_add * pic->linesize[i]) + xp; |
|
|
|
|
|
|
|
|
|
get_slice_data(ctx, src, linesize[i], xp, yp, |
|
|
|
|
pwidth, avctx->height / ctx->pictures_per_frame, |
|
|
|
|
td->blocks[i], td->emu_buf, |
|
|
|
|
mbs_per_slice, num_cblocks[i], is_chroma[i]); |
|
|
|
|
if (i < 3) { |
|
|
|
|
get_slice_data(ctx, src, linesize[i], xp, yp, |
|
|
|
|
pwidth, avctx->height / ctx->pictures_per_frame, |
|
|
|
|
td->blocks[i], td->emu_buf, |
|
|
|
|
mbs_per_slice, num_cblocks[i], is_chroma[i]); |
|
|
|
|
} else { |
|
|
|
|
get_alpha_data(ctx, src, linesize[i], xp, yp, |
|
|
|
|
pwidth, avctx->height / ctx->pictures_per_frame, |
|
|
|
|
td->blocks[i], mbs_per_slice, ctx->alpha_bits); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (q = min_quant; q < max_quant + 2; q++) { |
|
|
|
@ -628,13 +808,16 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic, |
|
|
|
|
for (q = min_quant; q <= max_quant; q++) { |
|
|
|
|
bits = 0; |
|
|
|
|
error = 0; |
|
|
|
|
for (i = 0; i < ctx->num_planes; i++) { |
|
|
|
|
for (i = 0; i < ctx->num_planes - !!ctx->alpha_bits; i++) { |
|
|
|
|
bits += estimate_slice_plane(ctx, &error, i, |
|
|
|
|
src, linesize[i], |
|
|
|
|
mbs_per_slice, |
|
|
|
|
num_cblocks[i], plane_factor[i], |
|
|
|
|
ctx->quants[q], td); |
|
|
|
|
} |
|
|
|
|
if (ctx->alpha_bits) |
|
|
|
|
bits += estimate_alpha_plane(ctx, &error, src, linesize[3], |
|
|
|
|
mbs_per_slice, q, td->blocks[3]); |
|
|
|
|
if (bits > 65000 * 8) { |
|
|
|
|
error = SCORE_LIMIT; |
|
|
|
|
break; |
|
|
|
@ -657,13 +840,16 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic, |
|
|
|
|
for (i = 0; i < 64; i++) |
|
|
|
|
qmat[i] = ctx->quant_mat[i] * q; |
|
|
|
|
} |
|
|
|
|
for (i = 0; i < ctx->num_planes; i++) { |
|
|
|
|
for (i = 0; i < ctx->num_planes - !!ctx->alpha_bits; i++) { |
|
|
|
|
bits += estimate_slice_plane(ctx, &error, i, |
|
|
|
|
src, linesize[i], |
|
|
|
|
mbs_per_slice, |
|
|
|
|
num_cblocks[i], plane_factor[i], |
|
|
|
|
qmat, td); |
|
|
|
|
} |
|
|
|
|
if (ctx->alpha_bits) |
|
|
|
|
bits += estimate_alpha_plane(ctx, &error, src, linesize[3], |
|
|
|
|
mbs_per_slice, q, td->blocks[3]); |
|
|
|
|
if (bits <= ctx->bits_per_mb * mbs_per_slice) |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
@ -783,7 +969,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
|
|
|
|
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, 0x40 | (ctx->alpha_bits >> 3)); |
|
|
|
|
bytestream_put_byte (&buf, 0); // reserved
|
|
|
|
|
if (ctx->quant_sel != QUANT_MAT_DEFAULT) { |
|
|
|
|
bytestream_put_byte (&buf, 0x03); // matrix flags - both matrices are present
|
|
|
|
@ -902,12 +1088,20 @@ static av_cold int encode_init(AVCodecContext *avctx) |
|
|
|
|
"there should be an integer power of two MBs per slice\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
if (av_pix_fmt_desc_get(avctx->pix_fmt)->flags & AV_PIX_FMT_FLAG_ALPHA) { |
|
|
|
|
if (ctx->alpha_bits & 7) { |
|
|
|
|
av_log(avctx, AV_LOG_ERROR, "alpha bits should be 0, 8 or 16\n"); |
|
|
|
|
return AVERROR(EINVAL); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
ctx->alpha_bits = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ctx->chroma_factor = avctx->pix_fmt == AV_PIX_FMT_YUV422P10 |
|
|
|
|
? CFACTOR_Y422 |
|
|
|
|
: CFACTOR_Y444; |
|
|
|
|
ctx->profile_info = prores_profile_info + ctx->profile; |
|
|
|
|
ctx->num_planes = 3; |
|
|
|
|
ctx->num_planes = 3 + !!ctx->alpha_bits; |
|
|
|
|
|
|
|
|
|
ctx->mb_width = FFALIGN(avctx->width, 16) >> 4; |
|
|
|
|
|
|
|
|
@ -1023,7 +1217,7 @@ static const AVOption options[] = { |
|
|
|
|
AV_OPT_TYPE_INT, { .i64 = 8 }, 1, MAX_MBS_PER_SLICE, VE }, |
|
|
|
|
{ "profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, |
|
|
|
|
{ .i64 = PRORES_PROFILE_STANDARD }, |
|
|
|
|
PRORES_PROFILE_PROXY, PRORES_PROFILE_HQ, VE, "profile" }, |
|
|
|
|
PRORES_PROFILE_PROXY, PRORES_PROFILE_4444, VE, "profile" }, |
|
|
|
|
{ "proxy", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_PROXY }, |
|
|
|
|
0, 0, VE, "profile" }, |
|
|
|
|
{ "lt", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_LT }, |
|
|
|
@ -1032,6 +1226,8 @@ static const AVOption options[] = { |
|
|
|
|
0, 0, VE, "profile" }, |
|
|
|
|
{ "hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_HQ }, |
|
|
|
|
0, 0, VE, "profile" }, |
|
|
|
|
{ "4444", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_4444 }, |
|
|
|
|
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), |
|
|
|
@ -1050,6 +1246,8 @@ static const AVOption options[] = { |
|
|
|
|
0, 0, VE, "quant_mat" }, |
|
|
|
|
{ "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_DEFAULT }, |
|
|
|
|
0, 0, VE, "quant_mat" }, |
|
|
|
|
{ "alpha_bits", "bits for alpha plane", OFFSET(alpha_bits), AV_OPT_TYPE_INT, |
|
|
|
|
{ .i64 = 16 }, 0, 16, VE }, |
|
|
|
|
{ NULL } |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
@ -1071,7 +1269,8 @@ AVCodec ff_prores_ks_encoder = { |
|
|
|
|
.capabilities = CODEC_CAP_SLICE_THREADS, |
|
|
|
|
.long_name = NULL_IF_CONFIG_SMALL("Apple ProRes (iCodec Pro)"), |
|
|
|
|
.pix_fmts = (const enum AVPixelFormat[]) { |
|
|
|
|
AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_NONE |
|
|
|
|
AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, |
|
|
|
|
AV_PIX_FMT_YUVA444P10, AV_PIX_FMT_NONE |
|
|
|
|
}, |
|
|
|
|
.priv_class = &proresenc_class, |
|
|
|
|
}; |
|
|
|
|