FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavcodec/proresenc_kostya_common.c
Date: 2026-09-27 18:38:33
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