diff --git a/libavcodec/proresenc.c b/libavcodec/proresenc.c index 8e6f93fe2b..9f26def1df 100644 --- a/libavcodec/proresenc.c +++ b/libavcodec/proresenc.c @@ -42,6 +42,67 @@ enum { 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 @@ -56,7 +117,7 @@ static const struct prores_profile { 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", @@ -64,16 +125,7 @@ static const struct prores_profile { .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", @@ -81,16 +133,7 @@ static const struct prores_profile { .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", @@ -98,16 +141,7 @@ static const struct prores_profile { .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", @@ -115,16 +149,7 @@ static const struct prores_profile { .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 } }; @@ -147,6 +172,7 @@ typedef struct ProresContext { 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; @@ -159,6 +185,9 @@ typedef struct ProresContext { int num_planes; int bits_per_mb; + char *vendor; + int quant_sel; + int frame_size; int profile; @@ -373,7 +402,7 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, } 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++) { @@ -591,7 +620,7 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic, } else { qmat = ctx->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, @@ -684,7 +713,7 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, 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 @@ -694,13 +723,17 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, 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 @@ -816,10 +849,25 @@ static av_cold int encode_init(AVCodecContext *avctx) 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; + } + + 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; + } ctx->frame_size = ctx->num_slices * (2 + 2 * ctx->num_planes + (2 * mps * ctx->bits_per_mb) / 8) @@ -829,7 +877,7 @@ static av_cold int encode_init(AVCodecContext *avctx) 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; + ctx->quants[i][j] = ctx->quant_mat[j] * i; } avctx->codec_tag = ctx->profile_info->tag; @@ -877,6 +925,24 @@ static const AVOption options[] = { 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 } };