]> git.sesse.net Git - ffmpeg/blobdiff - libavcodec/proresenc_kostya.c
Merge remote-tracking branch 'qatar/master'
[ffmpeg] / libavcodec / proresenc_kostya.c
index 242537b001669337b1fca448419cfc050c9834ba..eb962b7a98595cbd0b4c689c67ef008257c8db4e 100644 (file)
@@ -3,6 +3,9 @@
  *
  * 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
@@ -42,6 +45,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 +120,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 +128,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 +136,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 +144,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 +152,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 }
 };
@@ -139,11 +167,15 @@ struct TrellisNode {
     int score;
 };
 
+#define MAX_STORED_Q 16
+
 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[16][64];
+    int16_t quants[MAX_STORED_Q][64];
+    int16_t custom_q[64];
+    const uint8_t *quant_mat;
 
     ProresDSPContext dsp;
     ScanTable  scantable;
@@ -155,6 +187,12 @@ typedef struct ProresContext {
     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;
@@ -166,7 +204,7 @@ typedef struct 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)
+                           int mbs_per_slice, int blocks_per_mb, int is_chroma)
 {
     const uint16_t *esrc;
     const int mb_width = 4 * blocks_per_mb;
@@ -184,37 +222,50 @@ static void get_slice_data(ProresContext *ctx, const uint16_t *src,
             elinesize = linesize;
         } else {
             int bw, bh, pix;
-            const int estride = 16 / sizeof(*ctx->emu_buf);
 
             esrc      = ctx->emu_buf;
-            elinesize = 16;
+            elinesize = 16 * sizeof(*ctx->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(ctx->emu_buf + j * 16,
+                       (const uint8_t*)src + j * linesize,
                        bw * sizeof(*src));
-                pix = ctx->emu_buf[j * estride + bw - 1];
+                pix = ctx->emu_buf[j * 16 + bw - 1];
                 for (k = bw; k < mb_width; k++)
-                    ctx->emu_buf[j * estride + k] = pix;
+                    ctx->emu_buf[j * 16 + k] = pix;
             }
             for (; j < 16; j++)
-                memcpy(ctx->emu_buf + j * estride,
-                       ctx->emu_buf + (bh - 1) * estride,
+                memcpy(ctx->emu_buf + j * 16,
+                       ctx->emu_buf + (bh - 1) * 16,
                        mb_width * sizeof(*ctx->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;
@@ -224,7 +275,7 @@ static void get_slice_data(ProresContext *ctx, const uint16_t *src,
 /**
  * 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;
@@ -241,8 +292,7 @@ static inline void encode_vlc_codeword(PutBitContext *pb, uint8_t codebook, int
         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;
 
@@ -348,6 +398,17 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic,
     int slice_width_factor = av_log2(mbs_per_slice);
     int num_cblocks, pwidth;
     int plane_factor, is_chroma;
+    uint16_t *qmat;
+
+    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->quant_mat[i] * quant;
+    }
 
     for (i = 0; i < ctx->num_planes; i++) {
         is_chroma    = (i == 1 || i == 2);
@@ -369,17 +430,17 @@ static int encode_slice(AVCodecContext *avctx, const AVFrame *pic,
 
         get_slice_data(ctx, src, pic->linesize[i], xp, yp,
                        pwidth, avctx->height, ctx->blocks[0],
-                       mbs_per_slice, num_cblocks);
+                       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,
-                                      ctx->quants[quant]);
+                                      qmat);
         total_size += sizes[i];
     }
     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;
@@ -500,6 +561,8 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic,
     int error, bits, bits_limit;
     int mbs, prev, cur, new_score;
     int slice_bits[TRELLIS_WIDTH], slice_score[TRELLIS_WIDTH];
+    int overquant;
+    uint16_t *qmat;
 
     mbs = x + mbs_per_slice;
 
@@ -523,10 +586,10 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic,
 
         get_slice_data(ctx, src, pic->linesize[i], xp, yp,
                        pwidth, avctx->height, ctx->blocks[i],
-                       mbs_per_slice, num_cblocks[i]);
+                       mbs_per_slice, num_cblocks[i], is_chroma[i]);
     }
 
-    for (q = min_quant; q <= max_quant; q++) {
+    for (q = min_quant; q < max_quant + 2; q++) {
         ctx->nodes[trellis_node + q].prev_node = -1;
         ctx->nodes[trellis_node + q].quant     = q;
     }
@@ -549,12 +612,43 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic,
         slice_bits[q]  = bits;
         slice_score[q] = error;
     }
+    if (slice_bits[max_quant] <= ctx->bits_per_mb * mbs_per_slice) {
+        slice_bits[max_quant + 1]  = slice_bits[max_quant];
+        slice_score[max_quant + 1] = slice_score[max_quant] + 1;
+        overquant = max_quant;
+    } else {
+        for (q = max_quant + 1; q < 128; q++) {
+            bits  = 0;
+            error = 0;
+            if (q < MAX_STORED_Q) {
+                qmat = ctx->quants[q];
+            } else {
+                qmat = ctx->custom_q;
+                for (i = 0; i < 64; i++)
+                    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);
+            }
+            if (bits <= ctx->bits_per_mb * mbs_per_slice)
+                break;
+        }
+
+        slice_bits[max_quant + 1]  = bits;
+        slice_score[max_quant + 1] = error;
+        overquant = q;
+    }
+    ctx->nodes[trellis_node + max_quant + 1].quant = overquant;
 
     bits_limit = mbs * ctx->bits_per_mb;
-    for (pq = min_quant; pq <= max_quant; pq++) {
+    for (pq = min_quant; pq < max_quant + 2; pq++) {
         prev = trellis_node - TRELLIS_WIDTH + pq;
 
-        for (q = min_quant; q <= max_quant; q++) {
+        for (q = min_quant; q < max_quant + 2; q++) {
             cur = trellis_node + q;
 
             bits  = ctx->nodes[prev].bits + slice_bits[q];
@@ -578,7 +672,7 @@ static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic,
 
     error = ctx->nodes[trellis_node + min_quant].score;
     pq    = trellis_node + min_quant;
-    for (q = min_quant + 1; q <= max_quant; q++) {
+    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;
             pq    = trellis_node + q;
@@ -606,13 +700,10 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I;
     avctx->coded_frame->key_frame = 1;
 
-    pkt_size = ctx->mb_width * ctx->mb_height * 64 * 3 * 12
-               + ctx->num_slices * 2 + 200 + FF_MIN_BUFFER_SIZE;
+    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;
 
@@ -625,23 +716,27 @@ 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
     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
@@ -658,21 +753,23 @@ static int encode_frame(AVCodecContext *avctx, AVPacket *pkt,
     // 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);
-        }
+        if (!ctx->force_quant) {
+            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;
+            for (x = ctx->slices_width - 1; x >= 0; x--) {
+                ctx->slice_q[x] = ctx->nodes[q].quant;
+                q = ctx->nodes[q].prev_node;
+            }
         }
 
         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];
 
             while (ctx->mb_width - x < mbs_per_slice)
                 mbs_per_slice >>= 1;
@@ -757,41 +854,83 @@ 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];
 
-    min_quant = ctx->profile_info->min_quant;
-    max_quant = ctx->profile_info->max_quant;
-    for (i = min_quant; i <= max_quant; i++) {
-        for (j = 0; j < 64; j++)
-            ctx->quants[i][j] = ctx->profile_info->quant[j] * i;
+    if (strlen(ctx->vendor) != 4) {
+        av_log(avctx, AV_LOG_ERROR, "vendor ID should be 4 bytes\n");
+        return AVERROR_INVALIDDATA;
     }
 
-    avctx->codec_tag   = ctx->profile_info->tag;
+    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;
+        }
 
-    av_log(avctx, AV_LOG_DEBUG, "profile %d, %d slices, %d bits per MB\n",
-           ctx->profile, ctx->num_slices, ctx->bits_per_mb);
+        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->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; i++) {
-        ctx->nodes[i].prev_node = -1;
-        ctx->nodes[i].bits      = 0;
-        ctx->nodes[i].score     = 0;
-    }
+        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);
+        ctx->slice_q = av_malloc(ctx->slices_width * sizeof(*ctx->slice_q));
+        if (!ctx->slice_q) {
+            encode_close(avctx);
+            return AVERROR(ENOMEM);
+        }
+    } 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;
+
+    avctx->codec_tag   = ctx->profile_info->tag;
+
+    av_log(avctx, AV_LOG_DEBUG, "profile %d, %d slices, %d bits per MB\n",
+           ctx->profile, ctx->num_slices, ctx->bits_per_mb);
+    av_log(avctx, AV_LOG_DEBUG, "estimated frame size %d\n",
+           ctx->frame_size);
+
     return 0;
 }
 
@@ -812,6 +951,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 }
 };