yading@10: /* yading@10: * Apple ProRes encoder yading@10: * yading@10: * Copyright (c) 2012 Konstantin Shishkov yading@10: * yading@10: * This encoder appears to be based on Anatoliy Wassermans considering yading@10: * similarities in the bugs. yading@10: * yading@10: * This file is part of Libav. yading@10: * yading@10: * Libav is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * Libav is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with Libav; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: #include "libavutil/opt.h" yading@10: #include "avcodec.h" yading@10: #include "dsputil.h" yading@10: #include "put_bits.h" yading@10: #include "bytestream.h" yading@10: #include "internal.h" yading@10: #include "proresdsp.h" yading@10: #include "proresdata.h" yading@10: yading@10: #define CFACTOR_Y422 2 yading@10: #define CFACTOR_Y444 3 yading@10: yading@10: #define MAX_MBS_PER_SLICE 8 yading@10: yading@10: #define MAX_PLANES 3 // should be increased to 4 when there's AV_PIX_FMT_YUV444AP10 yading@10: yading@10: enum { yading@10: PRORES_PROFILE_PROXY = 0, yading@10: PRORES_PROFILE_LT, yading@10: PRORES_PROFILE_STANDARD, yading@10: PRORES_PROFILE_HQ, yading@10: }; yading@10: yading@10: enum { yading@10: QUANT_MAT_PROXY = 0, yading@10: QUANT_MAT_LT, yading@10: QUANT_MAT_STANDARD, yading@10: QUANT_MAT_HQ, yading@10: QUANT_MAT_DEFAULT, yading@10: }; yading@10: yading@10: static const uint8_t prores_quant_matrices[][64] = { yading@10: { // proxy yading@10: 4, 7, 9, 11, 13, 14, 15, 63, yading@10: 7, 7, 11, 12, 14, 15, 63, 63, yading@10: 9, 11, 13, 14, 15, 63, 63, 63, yading@10: 11, 11, 13, 14, 63, 63, 63, 63, yading@10: 11, 13, 14, 63, 63, 63, 63, 63, yading@10: 13, 14, 63, 63, 63, 63, 63, 63, yading@10: 13, 63, 63, 63, 63, 63, 63, 63, yading@10: 63, 63, 63, 63, 63, 63, 63, 63, yading@10: }, yading@10: { // LT yading@10: 4, 5, 6, 7, 9, 11, 13, 15, yading@10: 5, 5, 7, 8, 11, 13, 15, 17, yading@10: 6, 7, 9, 11, 13, 15, 15, 17, yading@10: 7, 7, 9, 11, 13, 15, 17, 19, yading@10: 7, 9, 11, 13, 14, 16, 19, 23, yading@10: 9, 11, 13, 14, 16, 19, 23, 29, yading@10: 9, 11, 13, 15, 17, 21, 28, 35, yading@10: 11, 13, 16, 17, 21, 28, 35, 41, yading@10: }, yading@10: { // standard yading@10: 4, 4, 5, 5, 6, 7, 7, 9, yading@10: 4, 4, 5, 6, 7, 7, 9, 9, yading@10: 5, 5, 6, 7, 7, 9, 9, 10, yading@10: 5, 5, 6, 7, 7, 9, 9, 10, yading@10: 5, 6, 7, 7, 8, 9, 10, 12, yading@10: 6, 7, 7, 8, 9, 10, 12, 15, yading@10: 6, 7, 7, 9, 10, 11, 14, 17, yading@10: 7, 7, 9, 10, 11, 14, 17, 21, yading@10: }, yading@10: { // high quality yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 5, yading@10: 4, 4, 4, 4, 4, 4, 5, 5, yading@10: 4, 4, 4, 4, 4, 5, 5, 6, yading@10: 4, 4, 4, 4, 5, 5, 6, 7, yading@10: 4, 4, 4, 4, 5, 6, 7, 7, yading@10: }, yading@10: { // codec default yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: 4, 4, 4, 4, 4, 4, 4, 4, yading@10: }, yading@10: }; yading@10: yading@10: #define NUM_MB_LIMITS 4 yading@10: static const int prores_mb_limits[NUM_MB_LIMITS] = { yading@10: 1620, // up to 720x576 yading@10: 2700, // up to 960x720 yading@10: 6075, // up to 1440x1080 yading@10: 9216, // up to 2048x1152 yading@10: }; yading@10: yading@10: static const struct prores_profile { yading@10: const char *full_name; yading@10: uint32_t tag; yading@10: int min_quant; yading@10: int max_quant; yading@10: int br_tab[NUM_MB_LIMITS]; yading@10: int quant; yading@10: } prores_profile_info[4] = { yading@10: { yading@10: .full_name = "proxy", yading@10: .tag = MKTAG('a', 'p', 'c', 'o'), yading@10: .min_quant = 4, yading@10: .max_quant = 8, yading@10: .br_tab = { 300, 242, 220, 194 }, yading@10: .quant = QUANT_MAT_PROXY, yading@10: }, yading@10: { yading@10: .full_name = "LT", yading@10: .tag = MKTAG('a', 'p', 'c', 's'), yading@10: .min_quant = 1, yading@10: .max_quant = 9, yading@10: .br_tab = { 720, 560, 490, 440 }, yading@10: .quant = QUANT_MAT_LT, yading@10: }, yading@10: { yading@10: .full_name = "standard", yading@10: .tag = MKTAG('a', 'p', 'c', 'n'), yading@10: .min_quant = 1, yading@10: .max_quant = 6, yading@10: .br_tab = { 1050, 808, 710, 632 }, yading@10: .quant = QUANT_MAT_STANDARD, yading@10: }, yading@10: { yading@10: .full_name = "high quality", yading@10: .tag = MKTAG('a', 'p', 'c', 'h'), yading@10: .min_quant = 1, yading@10: .max_quant = 6, yading@10: .br_tab = { 1566, 1216, 1070, 950 }, yading@10: .quant = QUANT_MAT_HQ, yading@10: } yading@10: // for 4444 profile bitrate numbers are { 2350, 1828, 1600, 1425 } yading@10: }; yading@10: yading@10: #define TRELLIS_WIDTH 16 yading@10: #define SCORE_LIMIT INT_MAX / 2 yading@10: yading@10: struct TrellisNode { yading@10: int prev_node; yading@10: int quant; yading@10: int bits; yading@10: int score; yading@10: }; yading@10: yading@10: #define MAX_STORED_Q 16 yading@10: yading@10: typedef struct ProresThreadData { yading@10: DECLARE_ALIGNED(16, int16_t, blocks)[MAX_PLANES][64 * 4 * MAX_MBS_PER_SLICE]; yading@10: DECLARE_ALIGNED(16, uint16_t, emu_buf)[16 * 16]; yading@10: int16_t custom_q[64]; yading@10: struct TrellisNode *nodes; yading@10: } ProresThreadData; yading@10: yading@10: typedef struct ProresContext { yading@10: AVClass *class; yading@10: DECLARE_ALIGNED(16, int16_t, blocks)[MAX_PLANES][64 * 4 * MAX_MBS_PER_SLICE]; yading@10: DECLARE_ALIGNED(16, uint16_t, emu_buf)[16*16]; yading@10: int16_t quants[MAX_STORED_Q][64]; yading@10: int16_t custom_q[64]; yading@10: const uint8_t *quant_mat; yading@10: yading@10: ProresDSPContext dsp; yading@10: ScanTable scantable; yading@10: yading@10: int mb_width, mb_height; yading@10: int mbs_per_slice; yading@10: int num_chroma_blocks, chroma_factor; yading@10: int slices_width; yading@10: int slices_per_picture; yading@10: int pictures_per_frame; // 1 for progressive, 2 for interlaced yading@10: int cur_picture_idx; yading@10: int num_planes; yading@10: int bits_per_mb; yading@10: int force_quant; yading@10: yading@10: char *vendor; yading@10: int quant_sel; yading@10: yading@10: int frame_size_upper_bound; yading@10: yading@10: int profile; yading@10: const struct prores_profile *profile_info; yading@10: yading@10: int *slice_q; yading@10: yading@10: ProresThreadData *tdata; yading@10: } ProresContext; yading@10: yading@10: static void get_slice_data(ProresContext *ctx, const uint16_t *src, yading@10: int linesize, int x, int y, int w, int h, yading@10: int16_t *blocks, uint16_t *emu_buf, yading@10: int mbs_per_slice, int blocks_per_mb, int is_chroma) yading@10: { yading@10: const uint16_t *esrc; yading@10: const int mb_width = 4 * blocks_per_mb; yading@10: int elinesize; yading@10: int i, j, k; yading@10: yading@10: for (i = 0; i < mbs_per_slice; i++, src += mb_width) { yading@10: if (x >= w) { yading@10: memset(blocks, 0, 64 * (mbs_per_slice - i) * blocks_per_mb yading@10: * sizeof(*blocks)); yading@10: return; yading@10: } yading@10: if (x + mb_width <= w && y + 16 <= h) { yading@10: esrc = src; yading@10: elinesize = linesize; yading@10: } else { yading@10: int bw, bh, pix; yading@10: yading@10: esrc = emu_buf; yading@10: elinesize = 16 * sizeof(*emu_buf); yading@10: yading@10: bw = FFMIN(w - x, mb_width); yading@10: bh = FFMIN(h - y, 16); yading@10: yading@10: for (j = 0; j < bh; j++) { yading@10: memcpy(emu_buf + j * 16, yading@10: (const uint8_t*)src + j * linesize, yading@10: bw * sizeof(*src)); yading@10: pix = emu_buf[j * 16 + bw - 1]; yading@10: for (k = bw; k < mb_width; k++) yading@10: emu_buf[j * 16 + k] = pix; yading@10: } yading@10: for (; j < 16; j++) yading@10: memcpy(emu_buf + j * 16, yading@10: emu_buf + (bh - 1) * 16, yading@10: mb_width * sizeof(*emu_buf)); yading@10: } yading@10: if (!is_chroma) { yading@10: ctx->dsp.fdct(esrc, elinesize, blocks); yading@10: blocks += 64; yading@10: if (blocks_per_mb > 2) { yading@10: ctx->dsp.fdct(esrc + 8, elinesize, blocks); yading@10: blocks += 64; yading@10: } yading@10: ctx->dsp.fdct(esrc + elinesize * 4, elinesize, blocks); yading@10: blocks += 64; yading@10: if (blocks_per_mb > 2) { yading@10: ctx->dsp.fdct(esrc + elinesize * 4 + 8, elinesize, blocks); yading@10: blocks += 64; yading@10: } yading@10: } else { yading@10: ctx->dsp.fdct(esrc, elinesize, blocks); yading@10: blocks += 64; yading@10: ctx->dsp.fdct(esrc + elinesize * 4, elinesize, blocks); yading@10: blocks += 64; yading@10: if (blocks_per_mb > 2) { yading@10: ctx->dsp.fdct(esrc + 8, elinesize, blocks); yading@10: blocks += 64; yading@10: ctx->dsp.fdct(esrc + elinesize * 4 + 8, elinesize, blocks); yading@10: blocks += 64; yading@10: } yading@10: } yading@10: yading@10: x += mb_width; yading@10: } yading@10: } yading@10: yading@10: /** yading@10: * Write an unsigned rice/exp golomb codeword. yading@10: */ yading@10: static inline void encode_vlc_codeword(PutBitContext *pb, unsigned codebook, int val) yading@10: { yading@10: unsigned int rice_order, exp_order, switch_bits, switch_val; yading@10: int exponent; yading@10: yading@10: /* number of prefix bits to switch between Rice and expGolomb */ yading@10: switch_bits = (codebook & 3) + 1; yading@10: rice_order = codebook >> 5; /* rice code order */ yading@10: exp_order = (codebook >> 2) & 7; /* exp golomb code order */ yading@10: yading@10: switch_val = switch_bits << rice_order; yading@10: yading@10: if (val >= switch_val) { yading@10: val -= switch_val - (1 << exp_order); yading@10: exponent = av_log2(val); yading@10: yading@10: put_bits(pb, exponent - exp_order + switch_bits, 0); yading@10: put_bits(pb, exponent + 1, val); yading@10: } else { yading@10: exponent = val >> rice_order; yading@10: yading@10: if (exponent) yading@10: put_bits(pb, exponent, 0); yading@10: put_bits(pb, 1, 1); yading@10: if (rice_order) yading@10: put_sbits(pb, rice_order, val); yading@10: } yading@10: } yading@10: yading@10: #define GET_SIGN(x) ((x) >> 31) yading@10: #define MAKE_CODE(x) (((x) << 1) ^ GET_SIGN(x)) yading@10: yading@10: static void encode_dcs(PutBitContext *pb, int16_t *blocks, yading@10: int blocks_per_slice, int scale) yading@10: { yading@10: int i; yading@10: int codebook = 3, code, dc, prev_dc, delta, sign, new_sign; yading@10: yading@10: prev_dc = (blocks[0] - 0x4000) / scale; yading@10: encode_vlc_codeword(pb, FIRST_DC_CB, MAKE_CODE(prev_dc)); yading@10: sign = 0; yading@10: codebook = 3; yading@10: blocks += 64; yading@10: yading@10: for (i = 1; i < blocks_per_slice; i++, blocks += 64) { yading@10: dc = (blocks[0] - 0x4000) / scale; yading@10: delta = dc - prev_dc; yading@10: new_sign = GET_SIGN(delta); yading@10: delta = (delta ^ sign) - sign; yading@10: code = MAKE_CODE(delta); yading@10: encode_vlc_codeword(pb, ff_prores_dc_codebook[codebook], code); yading@10: codebook = (code + (code & 1)) >> 1; yading@10: codebook = FFMIN(codebook, 3); yading@10: sign = new_sign; yading@10: prev_dc = dc; yading@10: } yading@10: } yading@10: yading@10: static void encode_acs(PutBitContext *pb, int16_t *blocks, yading@10: int blocks_per_slice, yading@10: int plane_size_factor, yading@10: const uint8_t *scan, const int16_t *qmat) yading@10: { yading@10: int idx, i; yading@10: int run, level, run_cb, lev_cb; yading@10: int max_coeffs, abs_level; yading@10: yading@10: max_coeffs = blocks_per_slice << 6; yading@10: run_cb = ff_prores_run_to_cb_index[4]; yading@10: lev_cb = ff_prores_lev_to_cb_index[2]; yading@10: run = 0; yading@10: yading@10: for (i = 1; i < 64; i++) { yading@10: for (idx = scan[i]; idx < max_coeffs; idx += 64) { yading@10: level = blocks[idx] / qmat[scan[i]]; yading@10: if (level) { yading@10: abs_level = FFABS(level); yading@10: encode_vlc_codeword(pb, ff_prores_ac_codebook[run_cb], run); yading@10: encode_vlc_codeword(pb, ff_prores_ac_codebook[lev_cb], yading@10: abs_level - 1); yading@10: put_sbits(pb, 1, GET_SIGN(level)); yading@10: yading@10: run_cb = ff_prores_run_to_cb_index[FFMIN(run, 15)]; yading@10: lev_cb = ff_prores_lev_to_cb_index[FFMIN(abs_level, 9)]; yading@10: run = 0; yading@10: } else { yading@10: run++; yading@10: } yading@10: } yading@10: } yading@10: } yading@10: yading@10: static int encode_slice_plane(ProresContext *ctx, PutBitContext *pb, yading@10: const uint16_t *src, int linesize, yading@10: int mbs_per_slice, int16_t *blocks, yading@10: int blocks_per_mb, int plane_size_factor, yading@10: const int16_t *qmat) yading@10: { yading@10: int blocks_per_slice, saved_pos; yading@10: yading@10: saved_pos = put_bits_count(pb); yading@10: blocks_per_slice = mbs_per_slice * blocks_per_mb; yading@10: yading@10: encode_dcs(pb, blocks, blocks_per_slice, qmat[0]); yading@10: encode_acs(pb, blocks, blocks_per_slice, plane_size_factor, yading@10: ctx->scantable.permutated, qmat); yading@10: flush_put_bits(pb); yading@10: yading@10: return (put_bits_count(pb) - saved_pos) >> 3; yading@10: } yading@10: yading@10: static int encode_slice(AVCodecContext *avctx, const AVFrame *pic, yading@10: PutBitContext *pb, yading@10: int sizes[4], int x, int y, int quant, yading@10: int mbs_per_slice) yading@10: { yading@10: ProresContext *ctx = avctx->priv_data; yading@10: int i, xp, yp; yading@10: int total_size = 0; yading@10: const uint16_t *src; yading@10: int slice_width_factor = av_log2(mbs_per_slice); yading@10: int num_cblocks, pwidth, linesize, line_add; yading@10: int plane_factor, is_chroma; yading@10: uint16_t *qmat; yading@10: yading@10: if (ctx->pictures_per_frame == 1) yading@10: line_add = 0; yading@10: else yading@10: line_add = ctx->cur_picture_idx ^ !pic->top_field_first; yading@10: yading@10: if (ctx->force_quant) { yading@10: qmat = ctx->quants[0]; yading@10: } else if (quant < MAX_STORED_Q) { yading@10: qmat = ctx->quants[quant]; yading@10: } else { yading@10: qmat = ctx->custom_q; yading@10: for (i = 0; i < 64; i++) yading@10: qmat[i] = ctx->quant_mat[i] * quant; yading@10: } yading@10: yading@10: for (i = 0; i < ctx->num_planes; i++) { yading@10: is_chroma = (i == 1 || i == 2); yading@10: plane_factor = slice_width_factor + 2; yading@10: if (is_chroma) yading@10: plane_factor += ctx->chroma_factor - 3; yading@10: if (!is_chroma || ctx->chroma_factor == CFACTOR_Y444) { yading@10: xp = x << 4; yading@10: yp = y << 4; yading@10: num_cblocks = 4; yading@10: pwidth = avctx->width; yading@10: } else { yading@10: xp = x << 3; yading@10: yp = y << 4; yading@10: num_cblocks = 2; yading@10: pwidth = avctx->width >> 1; yading@10: } yading@10: yading@10: linesize = pic->linesize[i] * ctx->pictures_per_frame; yading@10: src = (const uint16_t*)(pic->data[i] + yp * linesize + yading@10: line_add * pic->linesize[i]) + xp; yading@10: yading@10: get_slice_data(ctx, src, linesize, xp, yp, yading@10: pwidth, avctx->height / ctx->pictures_per_frame, yading@10: ctx->blocks[0], ctx->emu_buf, yading@10: mbs_per_slice, num_cblocks, is_chroma); yading@10: sizes[i] = encode_slice_plane(ctx, pb, src, linesize, yading@10: mbs_per_slice, ctx->blocks[0], yading@10: num_cblocks, plane_factor, yading@10: qmat); yading@10: total_size += sizes[i]; yading@10: } yading@10: return total_size; yading@10: } yading@10: yading@10: static inline int estimate_vlc(unsigned codebook, int val) yading@10: { yading@10: unsigned int rice_order, exp_order, switch_bits, switch_val; yading@10: int exponent; yading@10: yading@10: /* number of prefix bits to switch between Rice and expGolomb */ yading@10: switch_bits = (codebook & 3) + 1; yading@10: rice_order = codebook >> 5; /* rice code order */ yading@10: exp_order = (codebook >> 2) & 7; /* exp golomb code order */ yading@10: yading@10: switch_val = switch_bits << rice_order; yading@10: yading@10: if (val >= switch_val) { yading@10: val -= switch_val - (1 << exp_order); yading@10: exponent = av_log2(val); yading@10: yading@10: return exponent * 2 - exp_order + switch_bits + 1; yading@10: } else { yading@10: return (val >> rice_order) + rice_order + 1; yading@10: } yading@10: } yading@10: yading@10: static int estimate_dcs(int *error, int16_t *blocks, int blocks_per_slice, yading@10: int scale) yading@10: { yading@10: int i; yading@10: int codebook = 3, code, dc, prev_dc, delta, sign, new_sign; yading@10: int bits; yading@10: yading@10: prev_dc = (blocks[0] - 0x4000) / scale; yading@10: bits = estimate_vlc(FIRST_DC_CB, MAKE_CODE(prev_dc)); yading@10: sign = 0; yading@10: codebook = 3; yading@10: blocks += 64; yading@10: *error += FFABS(blocks[0] - 0x4000) % scale; yading@10: yading@10: for (i = 1; i < blocks_per_slice; i++, blocks += 64) { yading@10: dc = (blocks[0] - 0x4000) / scale; yading@10: *error += FFABS(blocks[0] - 0x4000) % scale; yading@10: delta = dc - prev_dc; yading@10: new_sign = GET_SIGN(delta); yading@10: delta = (delta ^ sign) - sign; yading@10: code = MAKE_CODE(delta); yading@10: bits += estimate_vlc(ff_prores_dc_codebook[codebook], code); yading@10: codebook = (code + (code & 1)) >> 1; yading@10: codebook = FFMIN(codebook, 3); yading@10: sign = new_sign; yading@10: prev_dc = dc; yading@10: } yading@10: yading@10: return bits; yading@10: } yading@10: yading@10: static int estimate_acs(int *error, int16_t *blocks, int blocks_per_slice, yading@10: int plane_size_factor, yading@10: const uint8_t *scan, const int16_t *qmat) yading@10: { yading@10: int idx, i; yading@10: int run, level, run_cb, lev_cb; yading@10: int max_coeffs, abs_level; yading@10: int bits = 0; yading@10: yading@10: max_coeffs = blocks_per_slice << 6; yading@10: run_cb = ff_prores_run_to_cb_index[4]; yading@10: lev_cb = ff_prores_lev_to_cb_index[2]; yading@10: run = 0; yading@10: yading@10: for (i = 1; i < 64; i++) { yading@10: for (idx = scan[i]; idx < max_coeffs; idx += 64) { yading@10: level = blocks[idx] / qmat[scan[i]]; yading@10: *error += FFABS(blocks[idx]) % qmat[scan[i]]; yading@10: if (level) { yading@10: abs_level = FFABS(level); yading@10: bits += estimate_vlc(ff_prores_ac_codebook[run_cb], run); yading@10: bits += estimate_vlc(ff_prores_ac_codebook[lev_cb], yading@10: abs_level - 1) + 1; yading@10: yading@10: run_cb = ff_prores_run_to_cb_index[FFMIN(run, 15)]; yading@10: lev_cb = ff_prores_lev_to_cb_index[FFMIN(abs_level, 9)]; yading@10: run = 0; yading@10: } else { yading@10: run++; yading@10: } yading@10: } yading@10: } yading@10: yading@10: return bits; yading@10: } yading@10: yading@10: static int estimate_slice_plane(ProresContext *ctx, int *error, int plane, yading@10: const uint16_t *src, int linesize, yading@10: int mbs_per_slice, yading@10: int blocks_per_mb, int plane_size_factor, yading@10: const int16_t *qmat, ProresThreadData *td) yading@10: { yading@10: int blocks_per_slice; yading@10: int bits; yading@10: yading@10: blocks_per_slice = mbs_per_slice * blocks_per_mb; yading@10: yading@10: bits = estimate_dcs(error, td->blocks[plane], blocks_per_slice, qmat[0]); yading@10: bits += estimate_acs(error, td->blocks[plane], blocks_per_slice, yading@10: plane_size_factor, ctx->scantable.permutated, qmat); yading@10: yading@10: return FFALIGN(bits, 8); yading@10: } yading@10: yading@10: static int find_slice_quant(AVCodecContext *avctx, const AVFrame *pic, yading@10: int trellis_node, int x, int y, int mbs_per_slice, yading@10: ProresThreadData *td) yading@10: { yading@10: ProresContext *ctx = avctx->priv_data; yading@10: int i, q, pq, xp, yp; yading@10: const uint16_t *src; yading@10: int slice_width_factor = av_log2(mbs_per_slice); yading@10: int num_cblocks[MAX_PLANES], pwidth; yading@10: int plane_factor[MAX_PLANES], is_chroma[MAX_PLANES]; yading@10: const int min_quant = ctx->profile_info->min_quant; yading@10: const int max_quant = ctx->profile_info->max_quant; yading@10: int error, bits, bits_limit; yading@10: int mbs, prev, cur, new_score; yading@10: int slice_bits[TRELLIS_WIDTH], slice_score[TRELLIS_WIDTH]; yading@10: int overquant; yading@10: uint16_t *qmat; yading@10: int linesize[4], line_add; yading@10: yading@10: if (ctx->pictures_per_frame == 1) yading@10: line_add = 0; yading@10: else yading@10: line_add = ctx->cur_picture_idx ^ !pic->top_field_first; yading@10: mbs = x + mbs_per_slice; yading@10: yading@10: for (i = 0; i < ctx->num_planes; i++) { yading@10: is_chroma[i] = (i == 1 || i == 2); yading@10: plane_factor[i] = slice_width_factor + 2; yading@10: if (is_chroma[i]) yading@10: plane_factor[i] += ctx->chroma_factor - 3; yading@10: if (!is_chroma[i] || ctx->chroma_factor == CFACTOR_Y444) { yading@10: xp = x << 4; yading@10: yp = y << 4; yading@10: num_cblocks[i] = 4; yading@10: pwidth = avctx->width; yading@10: } else { yading@10: xp = x << 3; yading@10: yp = y << 4; yading@10: num_cblocks[i] = 2; yading@10: pwidth = avctx->width >> 1; yading@10: } yading@10: yading@10: linesize[i] = pic->linesize[i] * ctx->pictures_per_frame; yading@10: src = (const uint16_t*)(pic->data[i] + yp * linesize[i] + yading@10: line_add * pic->linesize[i]) + xp; yading@10: yading@10: get_slice_data(ctx, src, linesize[i], xp, yp, yading@10: pwidth, avctx->height / ctx->pictures_per_frame, yading@10: td->blocks[i], td->emu_buf, yading@10: mbs_per_slice, num_cblocks[i], is_chroma[i]); yading@10: } yading@10: yading@10: for (q = min_quant; q < max_quant + 2; q++) { yading@10: td->nodes[trellis_node + q].prev_node = -1; yading@10: td->nodes[trellis_node + q].quant = q; yading@10: } yading@10: yading@10: // todo: maybe perform coarser quantising to fit into frame size when needed yading@10: for (q = min_quant; q <= max_quant; q++) { yading@10: bits = 0; yading@10: error = 0; yading@10: for (i = 0; i < ctx->num_planes; i++) { yading@10: bits += estimate_slice_plane(ctx, &error, i, yading@10: src, linesize[i], yading@10: mbs_per_slice, yading@10: num_cblocks[i], plane_factor[i], yading@10: ctx->quants[q], td); yading@10: } yading@10: if (bits > 65000 * 8) { yading@10: error = SCORE_LIMIT; yading@10: break; yading@10: } yading@10: slice_bits[q] = bits; yading@10: slice_score[q] = error; yading@10: } yading@10: if (slice_bits[max_quant] <= ctx->bits_per_mb * mbs_per_slice) { yading@10: slice_bits[max_quant + 1] = slice_bits[max_quant]; yading@10: slice_score[max_quant + 1] = slice_score[max_quant] + 1; yading@10: overquant = max_quant; yading@10: } else { yading@10: for (q = max_quant + 1; q < 128; q++) { yading@10: bits = 0; yading@10: error = 0; yading@10: if (q < MAX_STORED_Q) { yading@10: qmat = ctx->quants[q]; yading@10: } else { yading@10: qmat = td->custom_q; yading@10: for (i = 0; i < 64; i++) yading@10: qmat[i] = ctx->quant_mat[i] * q; yading@10: } yading@10: for (i = 0; i < ctx->num_planes; i++) { yading@10: bits += estimate_slice_plane(ctx, &error, i, yading@10: src, linesize[i], yading@10: mbs_per_slice, yading@10: num_cblocks[i], plane_factor[i], yading@10: qmat, td); yading@10: } yading@10: if (bits <= ctx->bits_per_mb * mbs_per_slice) yading@10: break; yading@10: } yading@10: yading@10: slice_bits[max_quant + 1] = bits; yading@10: slice_score[max_quant + 1] = error; yading@10: overquant = q; yading@10: } yading@10: td->nodes[trellis_node + max_quant + 1].quant = overquant; yading@10: yading@10: bits_limit = mbs * ctx->bits_per_mb; yading@10: for (pq = min_quant; pq < max_quant + 2; pq++) { yading@10: prev = trellis_node - TRELLIS_WIDTH + pq; yading@10: yading@10: for (q = min_quant; q < max_quant + 2; q++) { yading@10: cur = trellis_node + q; yading@10: yading@10: bits = td->nodes[prev].bits + slice_bits[q]; yading@10: error = slice_score[q]; yading@10: if (bits > bits_limit) yading@10: error = SCORE_LIMIT; yading@10: yading@10: if (td->nodes[prev].score < SCORE_LIMIT && error < SCORE_LIMIT) yading@10: new_score = td->nodes[prev].score + error; yading@10: else yading@10: new_score = SCORE_LIMIT; yading@10: if (td->nodes[cur].prev_node == -1 || yading@10: td->nodes[cur].score >= new_score) { yading@10: yading@10: td->nodes[cur].bits = bits; yading@10: td->nodes[cur].score = new_score; yading@10: td->nodes[cur].prev_node = prev; yading@10: } yading@10: } yading@10: } yading@10: yading@10: error = td->nodes[trellis_node + min_quant].score; yading@10: pq = trellis_node + min_quant; yading@10: for (q = min_quant + 1; q < max_quant + 2; q++) { yading@10: if (td->nodes[trellis_node + q].score <= error) { yading@10: error = td->nodes[trellis_node + q].score; yading@10: pq = trellis_node + q; yading@10: } yading@10: } yading@10: yading@10: return pq; yading@10: } yading@10: yading@10: static int find_quant_thread(AVCodecContext *avctx, void *arg, yading@10: int jobnr, int threadnr) yading@10: { yading@10: ProresContext *ctx = avctx->priv_data; yading@10: ProresThreadData *td = ctx->tdata + threadnr; yading@10: int mbs_per_slice = ctx->mbs_per_slice; yading@10: int x, y = jobnr, mb, q = 0; yading@10: yading@10: for (x = mb = 0; x < ctx->mb_width; x += mbs_per_slice, mb++) { yading@10: while (ctx->mb_width - x < mbs_per_slice) yading@10: mbs_per_slice >>= 1; yading@10: q = find_slice_quant(avctx, avctx->coded_frame, yading@10: (mb + 1) * TRELLIS_WIDTH, x, y, yading@10: mbs_per_slice, td); yading@10: } yading@10: yading@10: for (x = ctx->slices_width - 1; x >= 0; x--) { yading@10: ctx->slice_q[x + y * ctx->slices_width] = td->nodes[q].quant; yading@10: q = td->nodes[q].prev_node; yading@10: } yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, yading@10: const AVFrame *pic, int *got_packet) yading@10: { yading@10: ProresContext *ctx = avctx->priv_data; yading@10: uint8_t *orig_buf, *buf, *slice_hdr, *slice_sizes, *tmp; yading@10: uint8_t *picture_size_pos; yading@10: PutBitContext pb; yading@10: int x, y, i, mb, q = 0; yading@10: int sizes[4] = { 0 }; yading@10: int slice_hdr_size = 2 + 2 * (ctx->num_planes - 1); yading@10: int frame_size, picture_size, slice_size; yading@10: int pkt_size, ret; yading@10: uint8_t frame_flags; yading@10: yading@10: *avctx->coded_frame = *pic; yading@10: avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; yading@10: avctx->coded_frame->key_frame = 1; yading@10: yading@10: pkt_size = ctx->frame_size_upper_bound + FF_MIN_BUFFER_SIZE; yading@10: yading@10: if ((ret = ff_alloc_packet2(avctx, pkt, pkt_size)) < 0) yading@10: return ret; yading@10: yading@10: orig_buf = pkt->data; yading@10: yading@10: // frame atom yading@10: orig_buf += 4; // frame size yading@10: bytestream_put_be32 (&orig_buf, FRAME_ID); // frame container ID yading@10: buf = orig_buf; yading@10: yading@10: // frame header yading@10: tmp = buf; yading@10: buf += 2; // frame header size will be stored here yading@10: bytestream_put_be16 (&buf, 0); // version 1 yading@10: bytestream_put_buffer(&buf, ctx->vendor, 4); yading@10: bytestream_put_be16 (&buf, avctx->width); yading@10: bytestream_put_be16 (&buf, avctx->height); yading@10: yading@10: frame_flags = ctx->chroma_factor << 6; yading@10: if (avctx->flags & CODEC_FLAG_INTERLACED_DCT) yading@10: frame_flags |= pic->top_field_first ? 0x04 : 0x08; yading@10: bytestream_put_byte (&buf, frame_flags); yading@10: yading@10: bytestream_put_byte (&buf, 0); // reserved yading@10: bytestream_put_byte (&buf, avctx->color_primaries); yading@10: bytestream_put_byte (&buf, avctx->color_trc); yading@10: bytestream_put_byte (&buf, avctx->colorspace); yading@10: bytestream_put_byte (&buf, 0x40); // source format and alpha information yading@10: bytestream_put_byte (&buf, 0); // reserved yading@10: if (ctx->quant_sel != QUANT_MAT_DEFAULT) { yading@10: bytestream_put_byte (&buf, 0x03); // matrix flags - both matrices are present yading@10: // luma quantisation matrix yading@10: for (i = 0; i < 64; i++) yading@10: bytestream_put_byte(&buf, ctx->quant_mat[i]); yading@10: // chroma quantisation matrix yading@10: for (i = 0; i < 64; i++) yading@10: bytestream_put_byte(&buf, ctx->quant_mat[i]); yading@10: } else { yading@10: bytestream_put_byte (&buf, 0x00); // matrix flags - default matrices are used yading@10: } yading@10: bytestream_put_be16 (&tmp, buf - orig_buf); // write back frame header size yading@10: yading@10: for (ctx->cur_picture_idx = 0; yading@10: ctx->cur_picture_idx < ctx->pictures_per_frame; yading@10: ctx->cur_picture_idx++) { yading@10: // picture header yading@10: picture_size_pos = buf + 1; yading@10: bytestream_put_byte (&buf, 0x40); // picture header size (in bits) yading@10: buf += 4; // picture data size will be stored here yading@10: bytestream_put_be16 (&buf, ctx->slices_per_picture); yading@10: bytestream_put_byte (&buf, av_log2(ctx->mbs_per_slice) << 4); // slice width and height in MBs yading@10: yading@10: // seek table - will be filled during slice encoding yading@10: slice_sizes = buf; yading@10: buf += ctx->slices_per_picture * 2; yading@10: yading@10: // slices yading@10: if (!ctx->force_quant) { yading@10: ret = avctx->execute2(avctx, find_quant_thread, NULL, NULL, yading@10: ctx->mb_height); yading@10: if (ret) yading@10: return ret; yading@10: } yading@10: yading@10: for (y = 0; y < ctx->mb_height; y++) { yading@10: int mbs_per_slice = ctx->mbs_per_slice; yading@10: for (x = mb = 0; x < ctx->mb_width; x += mbs_per_slice, mb++) { yading@10: q = ctx->force_quant ? ctx->force_quant yading@10: : ctx->slice_q[mb + y * ctx->slices_width]; yading@10: yading@10: while (ctx->mb_width - x < mbs_per_slice) yading@10: mbs_per_slice >>= 1; yading@10: yading@10: bytestream_put_byte(&buf, slice_hdr_size << 3); yading@10: slice_hdr = buf; yading@10: buf += slice_hdr_size - 1; yading@10: init_put_bits(&pb, buf, (pkt_size - (buf - orig_buf)) * 8); yading@10: encode_slice(avctx, pic, &pb, sizes, x, y, q, mbs_per_slice); yading@10: yading@10: bytestream_put_byte(&slice_hdr, q); yading@10: slice_size = slice_hdr_size + sizes[ctx->num_planes - 1]; yading@10: for (i = 0; i < ctx->num_planes - 1; i++) { yading@10: bytestream_put_be16(&slice_hdr, sizes[i]); yading@10: slice_size += sizes[i]; yading@10: } yading@10: bytestream_put_be16(&slice_sizes, slice_size); yading@10: buf += slice_size - slice_hdr_size; yading@10: } yading@10: } yading@10: yading@10: picture_size = buf - (picture_size_pos - 1); yading@10: bytestream_put_be32(&picture_size_pos, picture_size); yading@10: } yading@10: yading@10: orig_buf -= 8; yading@10: frame_size = buf - orig_buf; yading@10: bytestream_put_be32(&orig_buf, frame_size); yading@10: yading@10: pkt->size = frame_size; yading@10: pkt->flags |= AV_PKT_FLAG_KEY; yading@10: *got_packet = 1; yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int encode_close(AVCodecContext *avctx) yading@10: { yading@10: ProresContext *ctx = avctx->priv_data; yading@10: int i; yading@10: yading@10: av_freep(&avctx->coded_frame); yading@10: yading@10: if (ctx->tdata) { yading@10: for (i = 0; i < avctx->thread_count; i++) yading@10: av_free(ctx->tdata[i].nodes); yading@10: } yading@10: av_freep(&ctx->tdata); yading@10: av_freep(&ctx->slice_q); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: static av_cold int encode_init(AVCodecContext *avctx) yading@10: { yading@10: ProresContext *ctx = avctx->priv_data; yading@10: int mps; yading@10: int i, j; yading@10: int min_quant, max_quant; yading@10: int interlaced = !!(avctx->flags & CODEC_FLAG_INTERLACED_DCT); yading@10: yading@10: avctx->bits_per_raw_sample = 10; yading@10: avctx->coded_frame = avcodec_alloc_frame(); yading@10: if (!avctx->coded_frame) yading@10: return AVERROR(ENOMEM); yading@10: yading@10: ff_proresdsp_init(&ctx->dsp, avctx); yading@10: ff_init_scantable(ctx->dsp.dct_permutation, &ctx->scantable, yading@10: interlaced ? ff_prores_interlaced_scan yading@10: : ff_prores_progressive_scan); yading@10: yading@10: mps = ctx->mbs_per_slice; yading@10: if (mps & (mps - 1)) { yading@10: av_log(avctx, AV_LOG_ERROR, yading@10: "there should be an integer power of two MBs per slice\n"); yading@10: return AVERROR(EINVAL); yading@10: } yading@10: yading@10: ctx->chroma_factor = avctx->pix_fmt == AV_PIX_FMT_YUV422P10 yading@10: ? CFACTOR_Y422 yading@10: : CFACTOR_Y444; yading@10: ctx->profile_info = prores_profile_info + ctx->profile; yading@10: ctx->num_planes = 3; yading@10: yading@10: ctx->mb_width = FFALIGN(avctx->width, 16) >> 4; yading@10: yading@10: if (interlaced) yading@10: ctx->mb_height = FFALIGN(avctx->height, 32) >> 5; yading@10: else yading@10: ctx->mb_height = FFALIGN(avctx->height, 16) >> 4; yading@10: yading@10: ctx->slices_width = ctx->mb_width / mps; yading@10: ctx->slices_width += av_popcount(ctx->mb_width - ctx->slices_width * mps); yading@10: ctx->slices_per_picture = ctx->mb_height * ctx->slices_width; yading@10: ctx->pictures_per_frame = 1 + interlaced; yading@10: yading@10: if (ctx->quant_sel == -1) yading@10: ctx->quant_mat = prores_quant_matrices[ctx->profile_info->quant]; yading@10: else yading@10: ctx->quant_mat = prores_quant_matrices[ctx->quant_sel]; yading@10: yading@10: if (strlen(ctx->vendor) != 4) { yading@10: av_log(avctx, AV_LOG_ERROR, "vendor ID should be 4 bytes\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: ctx->force_quant = avctx->global_quality / FF_QP2LAMBDA; yading@10: if (!ctx->force_quant) { yading@10: if (!ctx->bits_per_mb) { yading@10: for (i = 0; i < NUM_MB_LIMITS - 1; i++) yading@10: if (prores_mb_limits[i] >= ctx->mb_width * ctx->mb_height * yading@10: ctx->pictures_per_frame) yading@10: break; yading@10: ctx->bits_per_mb = ctx->profile_info->br_tab[i]; yading@10: } else if (ctx->bits_per_mb < 128) { yading@10: av_log(avctx, AV_LOG_ERROR, "too few bits per MB, please set at least 128\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: min_quant = ctx->profile_info->min_quant; yading@10: max_quant = ctx->profile_info->max_quant; yading@10: for (i = min_quant; i < MAX_STORED_Q; i++) { yading@10: for (j = 0; j < 64; j++) yading@10: ctx->quants[i][j] = ctx->quant_mat[j] * i; yading@10: } yading@10: yading@10: ctx->slice_q = av_malloc(ctx->slices_per_picture * sizeof(*ctx->slice_q)); yading@10: if (!ctx->slice_q) { yading@10: encode_close(avctx); yading@10: return AVERROR(ENOMEM); yading@10: } yading@10: yading@10: ctx->tdata = av_mallocz(avctx->thread_count * sizeof(*ctx->tdata)); yading@10: if (!ctx->tdata) { yading@10: encode_close(avctx); yading@10: return AVERROR(ENOMEM); yading@10: } yading@10: yading@10: for (j = 0; j < avctx->thread_count; j++) { yading@10: ctx->tdata[j].nodes = av_malloc((ctx->slices_width + 1) yading@10: * TRELLIS_WIDTH yading@10: * sizeof(*ctx->tdata->nodes)); yading@10: if (!ctx->tdata[j].nodes) { yading@10: encode_close(avctx); yading@10: return AVERROR(ENOMEM); yading@10: } yading@10: for (i = min_quant; i < max_quant + 2; i++) { yading@10: ctx->tdata[j].nodes[i].prev_node = -1; yading@10: ctx->tdata[j].nodes[i].bits = 0; yading@10: ctx->tdata[j].nodes[i].score = 0; yading@10: } yading@10: } yading@10: } else { yading@10: int ls = 0; yading@10: yading@10: if (ctx->force_quant > 64) { yading@10: av_log(avctx, AV_LOG_ERROR, "too large quantiser, maximum is 64\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: for (j = 0; j < 64; j++) { yading@10: ctx->quants[0][j] = ctx->quant_mat[j] * ctx->force_quant; yading@10: ls += av_log2((1 << 11) / ctx->quants[0][j]) * 2 + 1; yading@10: } yading@10: yading@10: ctx->bits_per_mb = ls * 8; yading@10: if (ctx->chroma_factor == CFACTOR_Y444) yading@10: ctx->bits_per_mb += ls * 4; yading@10: if (ctx->num_planes == 4) yading@10: ctx->bits_per_mb += ls * 4; yading@10: } yading@10: yading@10: ctx->frame_size_upper_bound = ctx->pictures_per_frame * yading@10: ctx->slices_per_picture * yading@10: (2 + 2 * ctx->num_planes + yading@10: (mps * ctx->bits_per_mb) / 8) yading@10: + 200; yading@10: yading@10: avctx->codec_tag = ctx->profile_info->tag; yading@10: yading@10: av_log(avctx, AV_LOG_DEBUG, yading@10: "profile %d, %d slices, interlacing: %s, %d bits per MB\n", yading@10: ctx->profile, ctx->slices_per_picture * ctx->pictures_per_frame, yading@10: interlaced ? "yes" : "no", ctx->bits_per_mb); yading@10: av_log(avctx, AV_LOG_DEBUG, "frame size upper bound: %d\n", yading@10: ctx->frame_size_upper_bound); yading@10: yading@10: return 0; yading@10: } yading@10: yading@10: #define OFFSET(x) offsetof(ProresContext, x) yading@10: #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM yading@10: yading@10: static const AVOption options[] = { yading@10: { "mbs_per_slice", "macroblocks per slice", OFFSET(mbs_per_slice), yading@10: AV_OPT_TYPE_INT, { .i64 = 8 }, 1, MAX_MBS_PER_SLICE, VE }, yading@10: { "profile", NULL, OFFSET(profile), AV_OPT_TYPE_INT, yading@10: { .i64 = PRORES_PROFILE_STANDARD }, yading@10: PRORES_PROFILE_PROXY, PRORES_PROFILE_HQ, VE, "profile" }, yading@10: { "proxy", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_PROXY }, yading@10: 0, 0, VE, "profile" }, yading@10: { "lt", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_LT }, yading@10: 0, 0, VE, "profile" }, yading@10: { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_STANDARD }, yading@10: 0, 0, VE, "profile" }, yading@10: { "hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PRORES_PROFILE_HQ }, yading@10: 0, 0, VE, "profile" }, yading@10: { "vendor", "vendor ID", OFFSET(vendor), yading@10: AV_OPT_TYPE_STRING, { .str = "Lavc" }, CHAR_MIN, CHAR_MAX, VE }, yading@10: { "bits_per_mb", "desired bits per macroblock", OFFSET(bits_per_mb), yading@10: AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8192, VE }, yading@10: { "quant_mat", "quantiser matrix", OFFSET(quant_sel), AV_OPT_TYPE_INT, yading@10: { .i64 = -1 }, -1, QUANT_MAT_DEFAULT, VE, "quant_mat" }, yading@10: { "auto", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = -1 }, yading@10: 0, 0, VE, "quant_mat" }, yading@10: { "proxy", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_PROXY }, yading@10: 0, 0, VE, "quant_mat" }, yading@10: { "lt", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_LT }, yading@10: 0, 0, VE, "quant_mat" }, yading@10: { "standard", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_STANDARD }, yading@10: 0, 0, VE, "quant_mat" }, yading@10: { "hq", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_HQ }, yading@10: 0, 0, VE, "quant_mat" }, yading@10: { "default", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = QUANT_MAT_DEFAULT }, yading@10: 0, 0, VE, "quant_mat" }, yading@10: { NULL } yading@10: }; yading@10: yading@10: static const AVClass proresenc_class = { yading@10: .class_name = "ProRes encoder", yading@10: .item_name = av_default_item_name, yading@10: .option = options, yading@10: .version = LIBAVUTIL_VERSION_INT, yading@10: }; yading@10: yading@10: AVCodec ff_prores_ks_encoder = { yading@10: .name = "prores_ks", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_PRORES, yading@10: .priv_data_size = sizeof(ProresContext), yading@10: .init = encode_init, yading@10: .close = encode_close, yading@10: .encode2 = encode_frame, yading@10: .capabilities = CODEC_CAP_SLICE_THREADS, yading@10: .long_name = NULL_IF_CONFIG_SMALL("Apple ProRes (iCodec Pro)"), yading@10: .pix_fmts = (const enum AVPixelFormat[]) { yading@10: AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV444P10, AV_PIX_FMT_NONE yading@10: }, yading@10: .priv_class = &proresenc_class, yading@10: };