FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/proresenc_kostya_common.c
Date: 2026-04-22 13:46:37
Exec Total Coverage
Lines: 51 126 40.5%
Functions: 1 3 33.3%
Branches: 20 66 30.3%

Line Branch Exec Source
1 /*
2 * Apple ProRes encoder
3 *
4 * Copyright (c) 2011 Anatoliy Wasserman
5 * Copyright (c) 2012 Konstantin Shishkov
6 *
7 * This file is part of FFmpeg.
8 *
9 * FFmpeg is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * FFmpeg is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with FFmpeg; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #include "libavutil/pixdesc.h"
25 #include "avcodec.h"
26 #include "bytestream.h"
27 #include "proresdata.h"
28 #include <sys/types.h>
29 #include "proresenc_kostya_common.h"
30
31 static const uint8_t prores_quant_matrices[][64] = {
32 { // proxy
33 4, 7, 9, 11, 13, 14, 15, 63,
34 7, 7, 11, 12, 14, 15, 63, 63,
35 9, 11, 13, 14, 15, 63, 63, 63,
36 11, 11, 13, 14, 63, 63, 63, 63,
37 11, 13, 14, 63, 63, 63, 63, 63,
38 13, 14, 63, 63, 63, 63, 63, 63,
39 13, 63, 63, 63, 63, 63, 63, 63,
40 63, 63, 63, 63, 63, 63, 63, 63,
41 },
42 { // proxy chromas
43 4, 7, 9, 11, 13, 14, 63, 63,
44 7, 7, 11, 12, 14, 63, 63, 63,
45 9, 11, 13, 14, 63, 63, 63, 63,
46 11, 11, 13, 14, 63, 63, 63, 63,
47 11, 13, 14, 63, 63, 63, 63, 63,
48 13, 14, 63, 63, 63, 63, 63, 63,
49 13, 63, 63, 63, 63, 63, 63, 63,
50 63, 63, 63, 63, 63, 63, 63, 63
51 },
52 { // LT
53 4, 5, 6, 7, 9, 11, 13, 15,
54 5, 5, 7, 8, 11, 13, 15, 17,
55 6, 7, 9, 11, 13, 15, 15, 17,
56 7, 7, 9, 11, 13, 15, 17, 19,
57 7, 9, 11, 13, 14, 16, 19, 23,
58 9, 11, 13, 14, 16, 19, 23, 29,
59 9, 11, 13, 15, 17, 21, 28, 35,
60 11, 13, 16, 17, 21, 28, 35, 41,
61 },
62 { // standard
63 4, 4, 5, 5, 6, 7, 7, 9,
64 4, 4, 5, 6, 7, 7, 9, 9,
65 5, 5, 6, 7, 7, 9, 9, 10,
66 5, 5, 6, 7, 7, 9, 9, 10,
67 5, 6, 7, 7, 8, 9, 10, 12,
68 6, 7, 7, 8, 9, 10, 12, 15,
69 6, 7, 7, 9, 10, 11, 14, 17,
70 7, 7, 9, 10, 11, 14, 17, 21,
71 },
72 { // high quality
73 4, 4, 4, 4, 4, 4, 4, 4,
74 4, 4, 4, 4, 4, 4, 4, 4,
75 4, 4, 4, 4, 4, 4, 4, 4,
76 4, 4, 4, 4, 4, 4, 4, 5,
77 4, 4, 4, 4, 4, 4, 5, 5,
78 4, 4, 4, 4, 4, 5, 5, 6,
79 4, 4, 4, 4, 5, 5, 6, 7,
80 4, 4, 4, 4, 5, 6, 7, 7,
81 },
82 { // XQ luma
83 2, 2, 2, 2, 2, 2, 2, 2,
84 2, 2, 2, 2, 2, 2, 2, 2,
85 2, 2, 2, 2, 2, 2, 2, 2,
86 2, 2, 2, 2, 2, 2, 2, 3,
87 2, 2, 2, 2, 2, 2, 3, 3,
88 2, 2, 2, 2, 2, 3, 3, 3,
89 2, 2, 2, 2, 3, 3, 3, 4,
90 2, 2, 2, 2, 3, 3, 4, 4,
91 },
92 { // codec default
93 4, 4, 4, 4, 4, 4, 4, 4,
94 4, 4, 4, 4, 4, 4, 4, 4,
95 4, 4, 4, 4, 4, 4, 4, 4,
96 4, 4, 4, 4, 4, 4, 4, 4,
97 4, 4, 4, 4, 4, 4, 4, 4,
98 4, 4, 4, 4, 4, 4, 4, 4,
99 4, 4, 4, 4, 4, 4, 4, 4,
100 4, 4, 4, 4, 4, 4, 4, 4,
101 },
102 };
103
104 static const int prores_mb_limits[NUM_MB_LIMITS] = {
105 1620, // up to 720x576
106 2700, // up to 960x720
107 6075, // up to 1440x1080
108 9216, // up to 2048x1152
109 };
110
111 static const prores_profile prores_profile_info[6] = {
112 {
113 .full_name = "proxy",
114 .tag = MKTAG('a', 'p', 'c', 'o'),
115 .min_quant = 4,
116 .max_quant = 8,
117 .br_tab = { 300, 242, 220, 194 },
118 .quant = QUANT_MAT_PROXY,
119 .quant_chroma = QUANT_MAT_PROXY_CHROMA,
120 },
121 {
122 .full_name = "LT",
123 .tag = MKTAG('a', 'p', 'c', 's'),
124 .min_quant = 1,
125 .max_quant = 9,
126 .br_tab = { 720, 560, 490, 440 },
127 .quant = QUANT_MAT_LT,
128 .quant_chroma = QUANT_MAT_LT,
129 },
130 {
131 .full_name = "standard",
132 .tag = MKTAG('a', 'p', 'c', 'n'),
133 .min_quant = 1,
134 .max_quant = 6,
135 .br_tab = { 1050, 808, 710, 632 },
136 .quant = QUANT_MAT_STANDARD,
137 .quant_chroma = QUANT_MAT_STANDARD,
138 },
139 {
140 .full_name = "high quality",
141 .tag = MKTAG('a', 'p', 'c', 'h'),
142 .min_quant = 1,
143 .max_quant = 6,
144 .br_tab = { 1566, 1216, 1070, 950 },
145 .quant = QUANT_MAT_HQ,
146 .quant_chroma = QUANT_MAT_HQ,
147 },
148 {
149 .full_name = "4444",
150 .tag = MKTAG('a', 'p', '4', 'h'),
151 .min_quant = 1,
152 .max_quant = 6,
153 .br_tab = { 2350, 1828, 1600, 1425 },
154 .quant = QUANT_MAT_HQ,
155 .quant_chroma = QUANT_MAT_HQ,
156 },
157 {
158 .full_name = "4444XQ",
159 .tag = MKTAG('a', 'p', '4', 'x'),
160 .min_quant = 1,
161 .max_quant = 6,
162 .br_tab = { 3525, 2742, 2400, 2137 },
163 .quant = QUANT_MAT_HQ, /* Fix me : use QUANT_MAT_XQ_LUMA */
164 .quant_chroma = QUANT_MAT_HQ,
165 }
166 };
167
168 4 av_cold int ff_prores_kostya_encode_init(AVCodecContext *avctx, ProresContext *ctx,
169 enum AVPixelFormat pix_fmt)
170 {
171 int mps, i, j, min_quant;
172 4 int interlaced = !!(avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT);
173
174 4 avctx->bits_per_raw_sample = 10;
175
176 4 ctx->scantable = interlaced ? ff_prores_interlaced_scan
177
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 : ff_prores_progressive_scan;
178
179 4 mps = ctx->mbs_per_slice;
180
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (mps & (mps - 1)) {
181 av_log(avctx, AV_LOG_ERROR,
182 "there should be an integer power of two MBs per slice\n");
183 return AVERROR(EINVAL);
184 }
185
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ctx->profile == PRORES_PROFILE_AUTO) {
186 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt);
187 ctx->profile = (desc->flags & AV_PIX_FMT_FLAG_ALPHA ||
188 !(desc->log2_chroma_w + desc->log2_chroma_h))
189 ? PRORES_PROFILE_4444 : PRORES_PROFILE_HQ;
190 av_log(avctx, AV_LOG_INFO, "Autoselected %s. It can be overridden "
191 "through -profile option.\n", ctx->profile == PRORES_PROFILE_4444
192 ? "4:4:4:4 profile because of the used input colorspace"
193 : "HQ profile to keep best quality");
194 }
195
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (av_pix_fmt_desc_get(pix_fmt)->flags & AV_PIX_FMT_FLAG_ALPHA) {
196 if (ctx->profile != PRORES_PROFILE_4444 &&
197 ctx->profile != PRORES_PROFILE_4444XQ) {
198 // force alpha and warn
199 av_log(avctx, AV_LOG_WARNING, "Profile selected will not "
200 "encode alpha. Override with -profile if needed.\n");
201 ctx->alpha_bits = 0;
202 }
203 if (ctx->alpha_bits & 7) {
204 av_log(avctx, AV_LOG_ERROR, "alpha bits should be 0, 8 or 16\n");
205 return AVERROR(EINVAL);
206 }
207 avctx->bits_per_coded_sample = 32;
208 } else {
209 4 ctx->alpha_bits = 0;
210 }
211
212 4 ctx->chroma_factor = pix_fmt == AV_PIX_FMT_YUV422P10
213 ? CFACTOR_Y422
214
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 : CFACTOR_Y444;
215 4 ctx->profile_info = prores_profile_info + ctx->profile;
216
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 ctx->num_planes = 3 + !!ctx->alpha_bits;
217
218 4 ctx->mb_width = FFALIGN(avctx->width, 16) >> 4;
219
220
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (interlaced)
221 ctx->mb_height = FFALIGN(avctx->height, 32) >> 5;
222 else
223 4 ctx->mb_height = FFALIGN(avctx->height, 16) >> 4;
224
225 4 ctx->slices_width = ctx->mb_width / mps;
226 4 ctx->slices_width += av_popcount(ctx->mb_width - ctx->slices_width * mps);
227 4 ctx->slices_per_picture = ctx->mb_height * ctx->slices_width;
228 4 ctx->pictures_per_frame = 1 + interlaced;
229
230
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (ctx->quant_sel == -1) {
231 4 ctx->quant_mat = prores_quant_matrices[ctx->profile_info->quant];
232 4 ctx->quant_chroma_mat = prores_quant_matrices[ctx->profile_info->quant_chroma];
233 } else {
234 ctx->quant_mat = prores_quant_matrices[ctx->quant_sel];
235 ctx->quant_chroma_mat = prores_quant_matrices[ctx->quant_sel];
236 }
237
238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (strlen(ctx->vendor) != 4) {
239 av_log(avctx, AV_LOG_ERROR, "vendor ID should be 4 bytes\n");
240 return AVERROR_INVALIDDATA;
241 }
242
243 4 ctx->force_quant = avctx->global_quality / FF_QP2LAMBDA;
244
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!ctx->force_quant) {
245
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!ctx->bits_per_mb) {
246
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 for (i = 0; i < NUM_MB_LIMITS - 1; i++)
247 4 if (prores_mb_limits[i] >= ctx->mb_width * ctx->mb_height *
248
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 ctx->pictures_per_frame)
249 4 break;
250 4 ctx->bits_per_mb = ctx->profile_info->br_tab[i];
251
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ctx->alpha_bits)
252 ctx->bits_per_mb *= 20;
253 } else if (ctx->bits_per_mb < 128) {
254 av_log(avctx, AV_LOG_ERROR, "too few bits per MB, please set at least 128\n");
255 return AVERROR_INVALIDDATA;
256 }
257
258 4 min_quant = ctx->profile_info->min_quant;
259
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 4 times.
64 for (i = min_quant; i < MAX_STORED_Q; i++) {
260
2/2
✓ Branch 0 taken 3840 times.
✓ Branch 1 taken 60 times.
3900 for (j = 0; j < 64; j++) {
261 3840 ctx->quants[i][j] = ctx->quant_mat[j] * i;
262 3840 ctx->quants_chroma[i][j] = ctx->quant_chroma_mat[j] * i;
263 }
264 }
265 } else {
266 int ls = 0;
267 int ls_chroma = 0;
268
269 if (ctx->force_quant > 64) {
270 av_log(avctx, AV_LOG_ERROR, "too large quantiser, maximum is 64\n");
271 return AVERROR_INVALIDDATA;
272 }
273
274 for (j = 0; j < 64; j++) {
275 ctx->quants[0][j] = ctx->quant_mat[j] * ctx->force_quant;
276 ctx->quants_chroma[0][j] = ctx->quant_chroma_mat[j] * ctx->force_quant;
277 ls += av_log2((1 << 11) / ctx->quants[0][j]) * 2 + 1;
278 ls_chroma += av_log2((1 << 11) / ctx->quants_chroma[0][j]) * 2 + 1;
279 }
280
281 ctx->bits_per_mb = ls * 4 + ls_chroma * 4;
282 if (ctx->chroma_factor == CFACTOR_Y444)
283 ctx->bits_per_mb += ls_chroma * 4;
284 }
285
286 4 ctx->frame_size_upper_bound = (ctx->pictures_per_frame *
287 4 ctx->slices_per_picture + 1) *
288 4 (2 + 2 * ctx->num_planes +
289 4 (mps * ctx->bits_per_mb) / 8)
290 4 + 200;
291
292
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ctx->alpha_bits) {
293 // The alpha plane is run-coded and might exceed the bit budget.
294 ctx->frame_size_upper_bound += (ctx->pictures_per_frame *
295 ctx->slices_per_picture + 1) *
296 /* num pixels per slice */ (ctx->mbs_per_slice * 256 *
297 /* bits per pixel */ (1 + ctx->alpha_bits + 1) + 7 >> 3);
298 }
299
300 4 avctx->codec_tag = ctx->profile_info->tag;
301 4 avctx->profile = ctx->profile;
302
303
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 av_log(avctx, AV_LOG_DEBUG,
304 "profile %d, %d slices, interlacing: %s, %d bits per MB\n",
305 4 ctx->profile, ctx->slices_per_picture * ctx->pictures_per_frame,
306 interlaced ? "yes" : "no", ctx->bits_per_mb);
307 4 av_log(avctx, AV_LOG_DEBUG, "frame size upper bound: %d\n",
308 ctx->frame_size_upper_bound);
309
310 4 return 0;
311 }
312
313 uint8_t *ff_prores_kostya_write_frame_header(AVCodecContext *avctx, ProresContext *ctx,
314 uint8_t **orig_buf, int flags,
315 enum AVColorPrimaries color_primaries,
316 enum AVColorTransferCharacteristic color_trc,
317 enum AVColorSpace colorspace)
318 {
319 uint8_t *buf, *tmp;
320 uint8_t frame_flags;
321
322 // frame atom
323 *orig_buf += 4; // frame size
324 bytestream_put_be32 (orig_buf, FRAME_ID); // frame container ID
325 buf = *orig_buf;
326
327 // frame header
328 tmp = buf;
329 buf += 2; // frame header size will be stored here
330 bytestream_put_be16 (&buf, ctx->chroma_factor != CFACTOR_Y422 || ctx->alpha_bits ? 1 : 0);
331 bytestream_put_buffer(&buf, (uint8_t*)ctx->vendor, 4);
332 bytestream_put_be16 (&buf, avctx->width);
333 bytestream_put_be16 (&buf, avctx->height);
334
335 frame_flags = ctx->chroma_factor << 6;
336 if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT)
337 frame_flags |= (flags & AV_FRAME_FLAG_TOP_FIELD_FIRST) ? 0x04 : 0x08;
338 bytestream_put_byte (&buf, frame_flags);
339
340 bytestream_put_byte (&buf, 0); // reserved
341 bytestream_put_byte (&buf, color_primaries);
342 bytestream_put_byte (&buf, color_trc);
343 bytestream_put_byte (&buf, colorspace);
344 bytestream_put_byte (&buf, ctx->alpha_bits >> 3);
345 bytestream_put_byte (&buf, 0); // reserved
346 if (ctx->quant_sel != QUANT_MAT_DEFAULT) {
347 bytestream_put_byte (&buf, 0x03); // matrix flags - both matrices are present
348 bytestream_put_buffer(&buf, ctx->quant_mat, 64); // luma quantisation matrix
349 bytestream_put_buffer(&buf, ctx->quant_chroma_mat, 64); // chroma quantisation matrix
350 } else {
351 bytestream_put_byte (&buf, 0x00); // matrix flags - default matrices are used
352 }
353 bytestream_put_be16 (&tmp, buf - *orig_buf); // write back frame header size
354 return buf;
355 }
356
357 uint8_t *ff_prores_kostya_write_picture_header(ProresContext *ctx, uint8_t *buf)
358 {
359 bytestream_put_byte (&buf, 0x40); // picture header size (in bits)
360 buf += 4; // picture data size will be stored here
361 bytestream_put_be16 (&buf, ctx->slices_per_picture);
362 bytestream_put_byte (&buf, av_log2(ctx->mbs_per_slice) << 4); // slice width and height in MBs
363 return buf;
364 }
365