/src/libavif/src/codec_aom.c
Line | Count | Source |
1 | | // Copyright 2019 Joe Drago. All rights reserved. |
2 | | // SPDX-License-Identifier: BSD-2-Clause |
3 | | |
4 | | #include "avif/internal.h" |
5 | | |
6 | | // These are for libaom to deal with |
7 | | #ifdef __clang__ |
8 | | #pragma clang diagnostic push |
9 | | #pragma clang diagnostic ignored "-Wduplicate-enum" |
10 | | #pragma clang diagnostic ignored "-Wextra-semi" |
11 | | #pragma clang diagnostic ignored "-Wused-but-marked-unused" |
12 | | #endif |
13 | | |
14 | | #if defined(AVIF_CODEC_AOM_ENCODE) |
15 | | #include "aom/aom_encoder.h" |
16 | | #include "aom/aomcx.h" |
17 | | #endif |
18 | | |
19 | | #if defined(AVIF_CODEC_AOM_DECODE) |
20 | | #include "aom/aom_decoder.h" |
21 | | #include "aom/aomdx.h" |
22 | | #endif |
23 | | |
24 | | #ifdef __clang__ |
25 | | #pragma clang diagnostic pop |
26 | | |
27 | | // This fixes complaints with aom_codec_control() and aom_img_fmt that are from libaom |
28 | | #pragma clang diagnostic push |
29 | | #pragma clang diagnostic ignored "-Wused-but-marked-unused" |
30 | | #pragma clang diagnostic ignored "-Wassign-enum" |
31 | | #endif |
32 | | |
33 | | #include <assert.h> |
34 | | #include <limits.h> |
35 | | #include <stdlib.h> |
36 | | #include <string.h> |
37 | | |
38 | | #if defined(AVIF_CODEC_AOM_ENCODE) |
39 | | // Detect whether the aom_codec_set_option() function is available. See aom/aom_codec.h |
40 | | // in https://aomedia-review.googlesource.com/c/aom/+/126302. |
41 | | #if AOM_CODEC_ABI_VERSION >= (6 + AOM_IMAGE_ABI_VERSION) |
42 | | #define HAVE_AOM_CODEC_SET_OPTION 1 |
43 | | #endif |
44 | | |
45 | | // Speeds 7-9 were added to all intra mode in https://aomedia-review.googlesource.com/c/aom/+/140624. |
46 | | #if AOM_ENCODER_ABI_VERSION >= (10 + AOM_CODEC_ABI_VERSION + /*AOM_EXT_PART_ABI_VERSION=*/1) |
47 | | #define ALL_INTRA_HAS_SPEEDS_7_TO_9 1 |
48 | | #endif |
49 | | #endif |
50 | | |
51 | | struct avifCodecInternal |
52 | | { |
53 | | #if defined(AVIF_CODEC_AOM_DECODE) |
54 | | avifBool decoderInitialized; |
55 | | aom_codec_ctx_t decoder; |
56 | | aom_codec_iter_t iter; |
57 | | aom_image_t * image; |
58 | | #endif |
59 | | |
60 | | #if defined(AVIF_CODEC_AOM_ENCODE) |
61 | | avifBool encoderInitialized; |
62 | | aom_codec_ctx_t encoder; |
63 | | struct aom_codec_enc_cfg cfg; |
64 | | avifPixelFormatInfo formatInfo; |
65 | | aom_img_fmt_t aomFormat; |
66 | | avifBool monochromeEnabled; |
67 | | // Whether 'tuning' (of the specified distortion metric) was set with an |
68 | | // avifEncoderSetCodecSpecificOption(encoder, "tune", value) call. |
69 | | avifBool tuningSet; |
70 | | uint32_t currentLayer; |
71 | | #endif |
72 | | }; |
73 | | |
74 | | static void aomCodecDestroyInternal(avifCodec * codec) |
75 | 12.2k | { |
76 | 12.2k | #if defined(AVIF_CODEC_AOM_DECODE) |
77 | 12.2k | if (codec->internal->decoderInitialized) { |
78 | 12.1k | aom_codec_destroy(&codec->internal->decoder); |
79 | 12.1k | } |
80 | 12.2k | #endif |
81 | | |
82 | 12.2k | #if defined(AVIF_CODEC_AOM_ENCODE) |
83 | 12.2k | if (codec->internal->encoderInitialized) { |
84 | 0 | aom_codec_destroy(&codec->internal->encoder); |
85 | 0 | } |
86 | 12.2k | #endif |
87 | | |
88 | 12.2k | avifFree(codec->internal); |
89 | 12.2k | } |
90 | | |
91 | | #if defined(AVIF_CODEC_AOM_DECODE) |
92 | | |
93 | | static avifBool aomCodecGetNextImage(struct avifCodec * codec, |
94 | | const avifDecodeSample * sample, |
95 | | avifBool alpha, |
96 | | avifBool * isLimitedRangeAlpha, |
97 | | avifImage * image) |
98 | 12.1k | { |
99 | 12.1k | if (!codec->internal->decoderInitialized) { |
100 | 12.1k | aom_codec_dec_cfg_t cfg; |
101 | 12.1k | memset(&cfg, 0, sizeof(aom_codec_dec_cfg_t)); |
102 | 12.1k | cfg.threads = codec->maxThreads; |
103 | 12.1k | cfg.allow_lowbitdepth = 1; |
104 | | |
105 | 12.1k | aom_codec_iface_t * decoder_interface = aom_codec_av1_dx(); |
106 | 12.1k | if (aom_codec_dec_init(&codec->internal->decoder, decoder_interface, &cfg, 0)) { |
107 | 0 | return AVIF_FALSE; |
108 | 0 | } |
109 | 12.1k | codec->internal->decoderInitialized = AVIF_TRUE; |
110 | | |
111 | 12.1k | if (aom_codec_control(&codec->internal->decoder, AV1D_SET_OUTPUT_ALL_LAYERS, codec->allLayers)) { |
112 | 0 | return AVIF_FALSE; |
113 | 0 | } |
114 | 12.1k | if (aom_codec_control(&codec->internal->decoder, AV1D_SET_OPERATING_POINT, codec->operatingPoint)) { |
115 | 0 | return AVIF_FALSE; |
116 | 0 | } |
117 | | |
118 | 12.1k | codec->internal->iter = NULL; |
119 | 12.1k | } |
120 | | |
121 | 12.1k | aom_image_t * nextFrame = NULL; |
122 | 12.1k | uint8_t spatialID = AVIF_SPATIAL_ID_UNSET; |
123 | 14.3k | for (;;) { |
124 | 14.3k | nextFrame = aom_codec_get_frame(&codec->internal->decoder, &codec->internal->iter); |
125 | 14.3k | if (nextFrame) { |
126 | 2.15k | if (spatialID != AVIF_SPATIAL_ID_UNSET) { |
127 | | // This requires libaom v3.1.2 or later, which has the fix for |
128 | | // https://crbug.com/aomedia/2993. |
129 | 75 | if (spatialID == nextFrame->spatial_id) { |
130 | | // Found the correct spatial_id. |
131 | 1 | break; |
132 | 1 | } |
133 | 2.08k | } else { |
134 | | // Got an image! |
135 | 2.08k | break; |
136 | 2.08k | } |
137 | 12.2k | } else if (sample) { |
138 | 12.1k | codec->internal->iter = NULL; |
139 | 12.1k | if (aom_codec_decode(&codec->internal->decoder, sample->data.data, sample->data.size, NULL)) { |
140 | 10.0k | return AVIF_FALSE; |
141 | 10.0k | } |
142 | 2.12k | spatialID = sample->spatialID; |
143 | 2.12k | sample = NULL; |
144 | 2.12k | } else { |
145 | 44 | break; |
146 | 44 | } |
147 | 14.3k | } |
148 | | |
149 | 2.12k | if (nextFrame) { |
150 | 2.08k | codec->internal->image = nextFrame; |
151 | 2.08k | } else { |
152 | 44 | if (alpha && codec->internal->image) { |
153 | | // Special case: reuse last alpha frame |
154 | 44 | } else { |
155 | 44 | return AVIF_FALSE; |
156 | 44 | } |
157 | 44 | } |
158 | | |
159 | 2.08k | avifBool isColor = !alpha; |
160 | 2.08k | if (isColor) { |
161 | | // Color (YUV) planes - set image to correct size / format, fill color |
162 | | |
163 | 2.03k | avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE; |
164 | 2.03k | switch (codec->internal->image->fmt) { |
165 | 242 | case AOM_IMG_FMT_I420: |
166 | 242 | case AOM_IMG_FMT_AOMI420: |
167 | 543 | case AOM_IMG_FMT_I42016: |
168 | 543 | yuvFormat = AVIF_PIXEL_FORMAT_YUV420; |
169 | 543 | break; |
170 | 70 | case AOM_IMG_FMT_I422: |
171 | 140 | case AOM_IMG_FMT_I42216: |
172 | 140 | yuvFormat = AVIF_PIXEL_FORMAT_YUV422; |
173 | 140 | break; |
174 | 1.00k | case AOM_IMG_FMT_I444: |
175 | 1.34k | case AOM_IMG_FMT_I44416: |
176 | 1.34k | yuvFormat = AVIF_PIXEL_FORMAT_YUV444; |
177 | 1.34k | break; |
178 | 0 | case AOM_IMG_FMT_NONE: |
179 | 0 | #if defined(AOM_HAVE_IMG_FMT_NV12) |
180 | | // Although the libaom encoder supports the NV12 image format as an input format, the |
181 | | // libaom decoder does not support NV12 as an output format. |
182 | 0 | case AOM_IMG_FMT_NV12: |
183 | 0 | #endif |
184 | 0 | case AOM_IMG_FMT_YV12: |
185 | 0 | case AOM_IMG_FMT_AOMYV12: |
186 | 0 | case AOM_IMG_FMT_YV1216: |
187 | 0 | default: |
188 | 0 | return AVIF_FALSE; |
189 | 2.03k | } |
190 | 2.03k | if (codec->internal->image->monochrome) { |
191 | 314 | yuvFormat = AVIF_PIXEL_FORMAT_YUV400; |
192 | 314 | } |
193 | | |
194 | 2.03k | if (image->width && image->height) { |
195 | 0 | if ((image->width != codec->internal->image->d_w) || (image->height != codec->internal->image->d_h) || |
196 | 0 | (image->depth != codec->internal->image->bit_depth) || (image->yuvFormat != yuvFormat)) { |
197 | | // Throw it all out |
198 | 0 | avifImageFreePlanes(image, AVIF_PLANES_ALL); |
199 | 0 | } |
200 | 0 | } |
201 | 2.03k | image->width = codec->internal->image->d_w; |
202 | 2.03k | image->height = codec->internal->image->d_h; |
203 | 2.03k | image->depth = codec->internal->image->bit_depth; |
204 | | |
205 | 2.03k | image->yuvFormat = yuvFormat; |
206 | 2.03k | image->yuvRange = (codec->internal->image->range == AOM_CR_STUDIO_RANGE) ? AVIF_RANGE_LIMITED : AVIF_RANGE_FULL; |
207 | 2.03k | image->yuvChromaSamplePosition = (avifChromaSamplePosition)codec->internal->image->csp; |
208 | | |
209 | 2.03k | image->colorPrimaries = (avifColorPrimaries)codec->internal->image->cp; |
210 | 2.03k | image->transferCharacteristics = (avifTransferCharacteristics)codec->internal->image->tc; |
211 | 2.03k | image->matrixCoefficients = (avifMatrixCoefficients)codec->internal->image->mc; |
212 | | |
213 | | // Steal the pointers from the decoder's image directly |
214 | 2.03k | avifImageFreePlanes(image, AVIF_PLANES_YUV); |
215 | 2.03k | int yuvPlaneCount = (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3; |
216 | 7.50k | for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { |
217 | 5.46k | image->yuvPlanes[yuvPlane] = codec->internal->image->planes[yuvPlane]; |
218 | 5.46k | image->yuvRowBytes[yuvPlane] = codec->internal->image->stride[yuvPlane]; |
219 | 5.46k | } |
220 | 2.03k | image->imageOwnsYUVPlanes = AVIF_FALSE; |
221 | 2.03k | } else { |
222 | | // Alpha plane - ensure image is correct size, fill color |
223 | | |
224 | 51 | if (image->width && image->height) { |
225 | 0 | if ((image->width != codec->internal->image->d_w) || (image->height != codec->internal->image->d_h) || |
226 | 0 | (image->depth != codec->internal->image->bit_depth)) { |
227 | | // Alpha plane doesn't match previous alpha plane decode, bail out |
228 | 0 | return AVIF_FALSE; |
229 | 0 | } |
230 | 0 | } |
231 | 51 | image->width = codec->internal->image->d_w; |
232 | 51 | image->height = codec->internal->image->d_h; |
233 | 51 | image->depth = codec->internal->image->bit_depth; |
234 | | |
235 | 51 | avifImageFreePlanes(image, AVIF_PLANES_A); |
236 | 51 | image->alphaPlane = codec->internal->image->planes[0]; |
237 | 51 | image->alphaRowBytes = codec->internal->image->stride[0]; |
238 | 51 | *isLimitedRangeAlpha = (codec->internal->image->range == AOM_CR_STUDIO_RANGE); |
239 | 51 | image->imageOwnsAlphaPlane = AVIF_FALSE; |
240 | 51 | } |
241 | | |
242 | 2.08k | return AVIF_TRUE; |
243 | 2.08k | } |
244 | | #endif // defined(AVIF_CODEC_AOM_DECODE) |
245 | | |
246 | | #if defined(AVIF_CODEC_AOM_ENCODE) |
247 | | |
248 | | static aom_img_fmt_t avifImageCalcAOMFmt(const avifImage * image, avifBool alpha) |
249 | 0 | { |
250 | 0 | aom_img_fmt_t fmt; |
251 | 0 | if (alpha) { |
252 | | // We're going monochrome, who cares about chroma quality |
253 | 0 | fmt = AOM_IMG_FMT_I420; |
254 | 0 | } else { |
255 | 0 | switch (image->yuvFormat) { |
256 | 0 | case AVIF_PIXEL_FORMAT_YUV444: |
257 | 0 | fmt = AOM_IMG_FMT_I444; |
258 | 0 | break; |
259 | 0 | case AVIF_PIXEL_FORMAT_YUV422: |
260 | 0 | fmt = AOM_IMG_FMT_I422; |
261 | 0 | break; |
262 | 0 | case AVIF_PIXEL_FORMAT_YUV420: |
263 | 0 | case AVIF_PIXEL_FORMAT_YUV400: |
264 | 0 | fmt = AOM_IMG_FMT_I420; |
265 | 0 | break; |
266 | 0 | case AVIF_PIXEL_FORMAT_NONE: |
267 | 0 | case AVIF_PIXEL_FORMAT_COUNT: |
268 | 0 | default: |
269 | 0 | return AOM_IMG_FMT_NONE; |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | 0 | if (image->depth > 8) { |
274 | 0 | fmt |= AOM_IMG_FMT_HIGHBITDEPTH; |
275 | 0 | } |
276 | |
|
277 | 0 | return fmt; |
278 | 0 | } |
279 | | |
280 | | #if !defined(HAVE_AOM_CODEC_SET_OPTION) |
281 | | static avifBool aomOptionParseInt(const char * str, int * val) |
282 | | { |
283 | | char * endptr; |
284 | | const long rawval = strtol(str, &endptr, 10); |
285 | | |
286 | | if (str[0] != '\0' && endptr[0] == '\0' && rawval >= INT_MIN && rawval <= INT_MAX) { |
287 | | *val = (int)rawval; |
288 | | return AVIF_TRUE; |
289 | | } |
290 | | |
291 | | return AVIF_FALSE; |
292 | | } |
293 | | |
294 | | static avifBool aomOptionParseUInt(const char * str, unsigned int * val) |
295 | | { |
296 | | char * endptr; |
297 | | const unsigned long rawval = strtoul(str, &endptr, 10); |
298 | | |
299 | | if (str[0] != '\0' && endptr[0] == '\0' && rawval <= UINT_MAX) { |
300 | | *val = (unsigned int)rawval; |
301 | | return AVIF_TRUE; |
302 | | } |
303 | | |
304 | | return AVIF_FALSE; |
305 | | } |
306 | | #endif // !defined(HAVE_AOM_CODEC_SET_OPTION) |
307 | | |
308 | | struct aomOptionEnumList |
309 | | { |
310 | | const char * name; |
311 | | int val; |
312 | | }; |
313 | | |
314 | | static avifBool aomOptionParseEnum(const char * str, const struct aomOptionEnumList * enums, int * val) |
315 | 0 | { |
316 | 0 | const struct aomOptionEnumList * listptr; |
317 | 0 | long int rawval; |
318 | 0 | char * endptr; |
319 | | |
320 | | // First see if the value can be parsed as a raw value. |
321 | 0 | rawval = strtol(str, &endptr, 10); |
322 | 0 | if (str[0] != '\0' && endptr[0] == '\0') { |
323 | | // Got a raw value, make sure it's valid. |
324 | 0 | for (listptr = enums; listptr->name; listptr++) |
325 | 0 | if (listptr->val == rawval) { |
326 | 0 | *val = (int)rawval; |
327 | 0 | return AVIF_TRUE; |
328 | 0 | } |
329 | 0 | } |
330 | | |
331 | | // Next see if it can be parsed as a string. |
332 | 0 | for (listptr = enums; listptr->name; listptr++) { |
333 | 0 | if (!strcmp(str, listptr->name)) { |
334 | 0 | *val = listptr->val; |
335 | 0 | return AVIF_TRUE; |
336 | 0 | } |
337 | 0 | } |
338 | | |
339 | 0 | return AVIF_FALSE; |
340 | 0 | } |
341 | | |
342 | | static const struct aomOptionEnumList endUsageEnum[] = { // |
343 | | { "vbr", AOM_VBR }, // Variable Bit Rate (VBR) mode |
344 | | { "cbr", AOM_CBR }, // Constant Bit Rate (CBR) mode |
345 | | { "cq", AOM_CQ }, // Constrained Quality (CQ) mode |
346 | | { "q", AOM_Q }, // Constant Quality (Q) mode |
347 | | { NULL, 0 } |
348 | | }; |
349 | | |
350 | | // Returns true if <key> equals <name> or <prefix><name>, where <prefix> is "color:" or "alpha:" |
351 | | // or the abbreviated form "c:" or "a:". |
352 | | static avifBool avifKeyEqualsName(const char * key, const char * name, avifBool alpha) |
353 | 0 | { |
354 | 0 | const char * prefix = alpha ? "alpha:" : "color:"; |
355 | 0 | size_t prefixLen = 6; |
356 | 0 | const char * shortPrefix = alpha ? "a:" : "c:"; |
357 | 0 | size_t shortPrefixLen = 2; |
358 | 0 | return !strcmp(key, name) || (!strncmp(key, prefix, prefixLen) && !strcmp(key + prefixLen, name)) || |
359 | 0 | (!strncmp(key, shortPrefix, shortPrefixLen) && !strcmp(key + shortPrefixLen, name)); |
360 | 0 | } |
361 | | |
362 | | static avifBool avifProcessAOMOptionsPreInit(avifCodec * codec, avifBool alpha, struct aom_codec_enc_cfg * cfg) |
363 | 0 | { |
364 | 0 | for (uint32_t i = 0; i < codec->csOptions->count; ++i) { |
365 | 0 | avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; |
366 | 0 | int val; |
367 | 0 | if (avifKeyEqualsName(entry->key, "end-usage", alpha)) { // Rate control mode |
368 | 0 | if (!aomOptionParseEnum(entry->value, endUsageEnum, &val)) { |
369 | 0 | avifDiagnosticsPrintf(codec->diag, "Invalid value for end-usage: %s", entry->value); |
370 | 0 | return AVIF_FALSE; |
371 | 0 | } |
372 | 0 | cfg->rc_end_usage = val; |
373 | 0 | } |
374 | 0 | } |
375 | 0 | return AVIF_TRUE; |
376 | 0 | } |
377 | | |
378 | | #if !defined(HAVE_AOM_CODEC_SET_OPTION) |
379 | | typedef enum |
380 | | { |
381 | | AVIF_AOM_OPTION_NUL = 0, |
382 | | AVIF_AOM_OPTION_STR, |
383 | | AVIF_AOM_OPTION_INT, |
384 | | AVIF_AOM_OPTION_UINT, |
385 | | AVIF_AOM_OPTION_ENUM, |
386 | | } aomOptionType; |
387 | | |
388 | | struct aomOptionDef |
389 | | { |
390 | | const char * name; |
391 | | int controlId; |
392 | | aomOptionType type; |
393 | | // If type is AVIF_AOM_OPTION_ENUM, this must be set. Otherwise should be NULL. |
394 | | const struct aomOptionEnumList * enums; |
395 | | }; |
396 | | |
397 | | static const struct aomOptionEnumList tuningEnum[] = { // |
398 | | { "psnr", AOM_TUNE_PSNR }, // |
399 | | { "ssim", AOM_TUNE_SSIM }, // |
400 | | { NULL, 0 } |
401 | | }; |
402 | | |
403 | | static const struct aomOptionDef aomOptionDefs[] = { |
404 | | // Adaptive quantization mode |
405 | | { "aq-mode", AV1E_SET_AQ_MODE, AVIF_AOM_OPTION_UINT, NULL }, |
406 | | // Constant/Constrained Quality level |
407 | | { "cq-level", AOME_SET_CQ_LEVEL, AVIF_AOM_OPTION_UINT, NULL }, |
408 | | // Enable delta quantization in chroma planes |
409 | | { "enable-chroma-deltaq", AV1E_SET_ENABLE_CHROMA_DELTAQ, AVIF_AOM_OPTION_INT, NULL }, |
410 | | // Bias towards block sharpness in rate-distortion optimization of transform coefficients |
411 | | { "sharpness", AOME_SET_SHARPNESS, AVIF_AOM_OPTION_UINT, NULL }, |
412 | | // Tune distortion metric |
413 | | { "tune", AOME_SET_TUNING, AVIF_AOM_OPTION_ENUM, tuningEnum }, |
414 | | // Film grain test vector |
415 | | { "film-grain-test", AV1E_SET_FILM_GRAIN_TEST_VECTOR, AVIF_AOM_OPTION_INT, NULL }, |
416 | | // Film grain table file |
417 | | { "film-grain-table", AV1E_SET_FILM_GRAIN_TABLE, AVIF_AOM_OPTION_STR, NULL }, |
418 | | |
419 | | // Sentinel |
420 | | { NULL, 0, AVIF_AOM_OPTION_NUL, NULL } |
421 | | }; |
422 | | #endif // !defined(HAVE_AOM_CODEC_SET_OPTION) |
423 | | |
424 | | static avifBool avifProcessAOMOptionsPostInit(avifCodec * codec, avifBool alpha) |
425 | 0 | { |
426 | 0 | for (uint32_t i = 0; i < codec->csOptions->count; ++i) { |
427 | 0 | avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; |
428 | | // Skip options for the other kind of plane. |
429 | 0 | const char * otherPrefix = alpha ? "color:" : "alpha:"; |
430 | 0 | size_t otherPrefixLen = 6; |
431 | 0 | const char * otherShortPrefix = alpha ? "c:" : "a:"; |
432 | 0 | size_t otherShortPrefixLen = 2; |
433 | 0 | if (!strncmp(entry->key, otherPrefix, otherPrefixLen) || !strncmp(entry->key, otherShortPrefix, otherShortPrefixLen)) { |
434 | 0 | continue; |
435 | 0 | } |
436 | | |
437 | | // Skip options processed by avifProcessAOMOptionsPreInit. |
438 | 0 | if (avifKeyEqualsName(entry->key, "end-usage", alpha)) { |
439 | 0 | continue; |
440 | 0 | } |
441 | | |
442 | 0 | #if defined(HAVE_AOM_CODEC_SET_OPTION) |
443 | 0 | const char * prefix = alpha ? "alpha:" : "color:"; |
444 | 0 | size_t prefixLen = 6; |
445 | 0 | const char * shortPrefix = alpha ? "a:" : "c:"; |
446 | 0 | size_t shortPrefixLen = 2; |
447 | 0 | const char * key = entry->key; |
448 | 0 | if (!strncmp(key, prefix, prefixLen)) { |
449 | 0 | key += prefixLen; |
450 | 0 | } else if (!strncmp(key, shortPrefix, shortPrefixLen)) { |
451 | 0 | key += shortPrefixLen; |
452 | 0 | } |
453 | 0 | if (aom_codec_set_option(&codec->internal->encoder, key, entry->value) != AOM_CODEC_OK) { |
454 | 0 | avifDiagnosticsPrintf(codec->diag, |
455 | 0 | "aom_codec_set_option(\"%s\", \"%s\") failed: %s: %s", |
456 | 0 | key, |
457 | 0 | entry->value, |
458 | 0 | aom_codec_error(&codec->internal->encoder), |
459 | 0 | aom_codec_error_detail(&codec->internal->encoder)); |
460 | 0 | return AVIF_FALSE; |
461 | 0 | } |
462 | 0 | if (!strcmp(key, "tune")) { |
463 | 0 | codec->internal->tuningSet = AVIF_TRUE; |
464 | 0 | } |
465 | | #else // !defined(HAVE_AOM_CODEC_SET_OPTION) |
466 | | avifBool match = AVIF_FALSE; |
467 | | for (int j = 0; aomOptionDefs[j].name; ++j) { |
468 | | if (avifKeyEqualsName(entry->key, aomOptionDefs[j].name, alpha)) { |
469 | | match = AVIF_TRUE; |
470 | | avifBool success = AVIF_FALSE; |
471 | | int valInt; |
472 | | unsigned int valUInt; |
473 | | switch (aomOptionDefs[j].type) { |
474 | | case AVIF_AOM_OPTION_NUL: |
475 | | success = AVIF_FALSE; |
476 | | break; |
477 | | case AVIF_AOM_OPTION_STR: |
478 | | success = aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, entry->value) == AOM_CODEC_OK; |
479 | | break; |
480 | | case AVIF_AOM_OPTION_INT: |
481 | | success = aomOptionParseInt(entry->value, &valInt) && |
482 | | aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, valInt) == AOM_CODEC_OK; |
483 | | break; |
484 | | case AVIF_AOM_OPTION_UINT: |
485 | | success = aomOptionParseUInt(entry->value, &valUInt) && |
486 | | aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, valUInt) == AOM_CODEC_OK; |
487 | | break; |
488 | | case AVIF_AOM_OPTION_ENUM: |
489 | | success = aomOptionParseEnum(entry->value, aomOptionDefs[j].enums, &valInt) && |
490 | | aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, valInt) == AOM_CODEC_OK; |
491 | | break; |
492 | | } |
493 | | if (!success) { |
494 | | return AVIF_FALSE; |
495 | | } |
496 | | if (aomOptionDefs[j].controlId == AOME_SET_TUNING) { |
497 | | codec->internal->tuningSet = AVIF_TRUE; |
498 | | } |
499 | | break; |
500 | | } |
501 | | } |
502 | | if (!match) { |
503 | | return AVIF_FALSE; |
504 | | } |
505 | | #endif // defined(HAVE_AOM_CODEC_SET_OPTION) |
506 | 0 | } |
507 | 0 | return AVIF_TRUE; |
508 | 0 | } |
509 | | |
510 | | struct aomScalingModeMapList |
511 | | { |
512 | | avifFraction avifMode; |
513 | | AOM_SCALING_MODE aomMode; |
514 | | }; |
515 | | |
516 | | static const struct aomScalingModeMapList scalingModeMap[] = { |
517 | | { { 1, 1 }, AOME_NORMAL }, { { 1, 2 }, AOME_ONETWO }, { { 1, 4 }, AOME_ONEFOUR }, { { 1, 8 }, AOME_ONEEIGHT }, |
518 | | { { 3, 4 }, AOME_THREEFOUR }, { { 3, 5 }, AOME_THREEFIVE }, { { 4, 5 }, AOME_FOURFIVE }, |
519 | | }; |
520 | | |
521 | | static const int scalingModeMapSize = sizeof(scalingModeMap) / sizeof(scalingModeMap[0]); |
522 | | |
523 | | static avifBool avifFindAOMScalingMode(const avifFraction * avifMode, AOM_SCALING_MODE * aomMode) |
524 | 0 | { |
525 | 0 | avifFraction simplifiedFraction = *avifMode; |
526 | 0 | avifFractionSimplify(&simplifiedFraction); |
527 | 0 | for (int i = 0; i < scalingModeMapSize; ++i) { |
528 | 0 | if (scalingModeMap[i].avifMode.n == simplifiedFraction.n && scalingModeMap[i].avifMode.d == simplifiedFraction.d) { |
529 | 0 | *aomMode = scalingModeMap[i].aomMode; |
530 | 0 | return AVIF_TRUE; |
531 | 0 | } |
532 | 0 | } |
533 | | |
534 | 0 | return AVIF_FALSE; |
535 | 0 | } |
536 | | |
537 | | static avifBool doesLevelMatch(int width, int height, int levelWidth, int levelHeight, int levelDimMult) |
538 | 0 | { |
539 | 0 | const int64_t levelLumaPels = (int64_t)levelWidth * levelHeight; |
540 | 0 | const int64_t lumaPels = (int64_t)width * height; |
541 | 0 | return lumaPels <= levelLumaPels && width <= levelWidth * levelDimMult && height <= levelHeight * levelDimMult; |
542 | 0 | } |
543 | | |
544 | | static avifBool aomCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output); |
545 | | |
546 | | static avifResult aomCodecEncodeImage(avifCodec * codec, |
547 | | avifEncoder * encoder, |
548 | | const avifImage * image, |
549 | | avifBool alpha, |
550 | | int tileRowsLog2, |
551 | | int tileColsLog2, |
552 | | int quantizer, |
553 | | avifEncoderChanges encoderChanges, |
554 | | avifBool disableLaggedOutput, |
555 | | avifAddImageFlags addImageFlags, |
556 | | avifCodecEncodeOutput * output) |
557 | 0 | { |
558 | 0 | struct aom_codec_enc_cfg * cfg = &codec->internal->cfg; |
559 | 0 | avifBool quantizerUpdated = AVIF_FALSE; |
560 | | |
561 | | // For encoder->scalingMode.horizontal and encoder->scalingMode.vertical to take effect in AOM |
562 | | // encoder, config should be applied for each frame, so we don't care about changes on these |
563 | | // two fields. |
564 | 0 | encoderChanges &= ~AVIF_ENCODER_CHANGE_SCALING_MODE; |
565 | |
|
566 | 0 | if (!codec->internal->encoderInitialized) { |
567 | | // Map encoder speed to AOM usage + CpuUsed: |
568 | | // Speed 0: GoodQuality CpuUsed 0 |
569 | | // Speed 1: GoodQuality CpuUsed 1 |
570 | | // Speed 2: GoodQuality CpuUsed 2 |
571 | | // Speed 3: GoodQuality CpuUsed 3 |
572 | | // Speed 4: GoodQuality CpuUsed 4 |
573 | | // Speed 5: GoodQuality CpuUsed 5 |
574 | | // Speed 6: GoodQuality CpuUsed 6 |
575 | | // Speed 7: RealTime CpuUsed 7 |
576 | | // Speed 8: RealTime CpuUsed 8 |
577 | | // Speed 9: RealTime CpuUsed 9 |
578 | | // Speed 10: RealTime CpuUsed 9 |
579 | 0 | unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY; |
580 | | // Use the new AOM_USAGE_ALL_INTRA (added in https://crbug.com/aomedia/2959) for still |
581 | | // image encoding if it is available. |
582 | 0 | #if defined(AOM_USAGE_ALL_INTRA) |
583 | 0 | if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { |
584 | 0 | aomUsage = AOM_USAGE_ALL_INTRA; |
585 | 0 | } |
586 | 0 | #endif |
587 | 0 | int aomCpuUsed = -1; |
588 | 0 | if (encoder->speed != AVIF_SPEED_DEFAULT) { |
589 | 0 | aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9); |
590 | 0 | if (aomCpuUsed >= 7) { |
591 | 0 | #if defined(AOM_USAGE_ALL_INTRA) && defined(ALL_INTRA_HAS_SPEEDS_7_TO_9) |
592 | 0 | if (!(addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE)) { |
593 | 0 | aomUsage = AOM_USAGE_REALTIME; |
594 | 0 | } |
595 | | #else |
596 | | aomUsage = AOM_USAGE_REALTIME; |
597 | | #endif |
598 | 0 | } |
599 | 0 | } |
600 | | |
601 | | // aom_codec.h says: aom_codec_version() == (major<<16 | minor<<8 | patch) |
602 | 0 | static const int aomVersion_2_0_0 = (2 << 16); |
603 | 0 | const int aomVersion = aom_codec_version(); |
604 | 0 | if ((aomVersion < aomVersion_2_0_0) && (image->depth > 8)) { |
605 | | // Due to a known issue with libaom v1.0.0-errata1-avif, 10bpc and |
606 | | // 12bpc image encodes will call the wrong variant of |
607 | | // aom_subtract_block when cpu-used is 7 or 8, and crash. Until we get |
608 | | // a new tagged release from libaom with the fix and can verify we're |
609 | | // running with that version of libaom, we must avoid using |
610 | | // cpu-used=7/8 on any >8bpc image encodes. |
611 | | // |
612 | | // Context: |
613 | | // * https://github.com/AOMediaCodec/libavif/issues/49 |
614 | | // * https://bugs.chromium.org/p/aomedia/issues/detail?id=2587 |
615 | | // |
616 | | // Continued bug tracking here: |
617 | | // * https://github.com/AOMediaCodec/libavif/issues/56 |
618 | |
|
619 | 0 | if (aomCpuUsed > 6) { |
620 | 0 | aomCpuUsed = 6; |
621 | 0 | } |
622 | 0 | } |
623 | |
|
624 | 0 | codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha); |
625 | 0 | if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) { |
626 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
627 | 0 | } |
628 | | |
629 | 0 | avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); |
630 | |
|
631 | 0 | aom_codec_iface_t * encoderInterface = aom_codec_av1_cx(); |
632 | 0 | aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage); |
633 | 0 | if (err != AOM_CODEC_OK) { |
634 | 0 | avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err)); |
635 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
636 | 0 | } |
637 | | |
638 | | // Set our own default cfg->rc_end_usage value, which may differ from libaom's default. |
639 | 0 | switch (aomUsage) { |
640 | 0 | case AOM_USAGE_GOOD_QUALITY: |
641 | | // libaom's default is AOM_VBR. Change the default to AOM_Q since we don't need to |
642 | | // hit a certain target bit rate. It's easier to control the worst quality in Q |
643 | | // mode. |
644 | 0 | cfg->rc_end_usage = AOM_Q; |
645 | 0 | break; |
646 | 0 | case AOM_USAGE_REALTIME: |
647 | | // For real-time mode we need to use CBR rate control mode. AOM_Q doesn't fit the |
648 | | // rate control requirements for real-time mode. CBR does. |
649 | 0 | cfg->rc_end_usage = AOM_CBR; |
650 | 0 | break; |
651 | 0 | #if defined(AOM_USAGE_ALL_INTRA) |
652 | 0 | case AOM_USAGE_ALL_INTRA: |
653 | 0 | cfg->rc_end_usage = AOM_Q; |
654 | 0 | break; |
655 | 0 | #endif |
656 | 0 | } |
657 | | |
658 | | // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only. |
659 | | // Profile 1. 8-bit and 10-bit 4:4:4 |
660 | | // Profile 2. 8-bit and 10-bit 4:2:2 |
661 | | // 12-bit 4:0:0, 4:2:0, 4:2:2 and 4:4:4 |
662 | 0 | uint8_t seqProfile = 0; |
663 | 0 | if (image->depth == 12) { |
664 | | // Only seqProfile 2 can handle 12 bit |
665 | 0 | seqProfile = 2; |
666 | 0 | } else { |
667 | | // 8-bit or 10-bit |
668 | |
|
669 | 0 | if (alpha) { |
670 | 0 | seqProfile = 0; |
671 | 0 | } else { |
672 | 0 | switch (image->yuvFormat) { |
673 | 0 | case AVIF_PIXEL_FORMAT_YUV444: |
674 | 0 | seqProfile = 1; |
675 | 0 | break; |
676 | 0 | case AVIF_PIXEL_FORMAT_YUV422: |
677 | 0 | seqProfile = 2; |
678 | 0 | break; |
679 | 0 | case AVIF_PIXEL_FORMAT_YUV420: |
680 | 0 | seqProfile = 0; |
681 | 0 | break; |
682 | 0 | case AVIF_PIXEL_FORMAT_YUV400: |
683 | 0 | seqProfile = 0; |
684 | 0 | break; |
685 | 0 | case AVIF_PIXEL_FORMAT_NONE: |
686 | 0 | case AVIF_PIXEL_FORMAT_COUNT: |
687 | 0 | default: |
688 | 0 | break; |
689 | 0 | } |
690 | 0 | } |
691 | 0 | } |
692 | | |
693 | 0 | cfg->g_profile = seqProfile; |
694 | 0 | cfg->g_bit_depth = image->depth; |
695 | 0 | cfg->g_input_bit_depth = image->depth; |
696 | 0 | cfg->g_w = image->width; |
697 | 0 | cfg->g_h = image->height; |
698 | | |
699 | | // Detect the libaom v3.6.0 bug described in |
700 | | // https://crbug.com/aomedia/2871#c12. See the changes to |
701 | | // av1/encoder/encoder.c in |
702 | | // https://aomedia-review.googlesource.com/c/aom/+/174421. |
703 | 0 | static const int aomVersion_3_6_0 = (3 << 16) | (6 << 8); |
704 | 0 | if (aomVersion == aomVersion_3_6_0) { |
705 | | // Detect the use of levels 7.x and 8.x, which use a larger max |
706 | | // tile area (4096 * 4608) than MAX_TILE_AREA (4096 * 2304). The |
707 | | // larger max tile area may not result in a different bitstream |
708 | | // (see the tile_info() function in the AV1 spec, Section 5.9.15), |
709 | | // so this is just a necessary condition for the bug. |
710 | 0 | if (!doesLevelMatch(image->width, image->height, 8192, 4352, 2) && |
711 | 0 | (doesLevelMatch(image->width, image->height, 16384, 8704, 2) || |
712 | 0 | doesLevelMatch(image->width, image->height, 32768, 17408, 2))) { |
713 | 0 | avifDiagnosticsPrintf(codec->diag, "Detected libaom v3.6.0 bug with large images. Upgrade to libaom v3.6.1 or later."); |
714 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
715 | 0 | } |
716 | 0 | } |
717 | | |
718 | 0 | if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { |
719 | | // Set the maximum number of frames to encode to 1. This instructs |
720 | | // libaom to set still_picture and reduced_still_picture_header to |
721 | | // 1 in AV1 sequence headers. |
722 | 0 | cfg->g_limit = 1; |
723 | | |
724 | | // Use the default settings of the new AOM_USAGE_ALL_INTRA (added in |
725 | | // https://crbug.com/aomedia/2959). |
726 | | // |
727 | | // Set g_lag_in_frames to 0 to reduce the number of frame buffers |
728 | | // (from 20 to 2) in libaom's lookahead structure. This reduces |
729 | | // memory consumption when encoding a single image. |
730 | 0 | cfg->g_lag_in_frames = 0; |
731 | | // Disable automatic placement of key frames by the encoder. |
732 | 0 | cfg->kf_mode = AOM_KF_DISABLED; |
733 | | // Tell libaom that all frames will be key frames. |
734 | 0 | cfg->kf_max_dist = 0; |
735 | 0 | } else { |
736 | 0 | if (encoder->keyframeInterval > 0) { |
737 | 0 | cfg->kf_max_dist = encoder->keyframeInterval; |
738 | 0 | } |
739 | 0 | } |
740 | 0 | if (encoder->extraLayerCount > 0) { |
741 | 0 | cfg->g_limit = encoder->extraLayerCount + 1; |
742 | | // For layered image, disable lagged encoding to always get output |
743 | | // frame for each input frame. |
744 | 0 | cfg->g_lag_in_frames = 0; |
745 | 0 | } |
746 | 0 | if (disableLaggedOutput) { |
747 | 0 | cfg->g_lag_in_frames = 0; |
748 | 0 | } |
749 | 0 | if (encoder->maxThreads > 1) { |
750 | | // libaom fails if cfg->g_threads is greater than 64 threads. See MAX_NUM_THREADS in |
751 | | // aom/aom_util/aom_thread.h. |
752 | 0 | cfg->g_threads = AVIF_MIN(encoder->maxThreads, 64); |
753 | 0 | } |
754 | |
|
755 | 0 | codec->internal->monochromeEnabled = AVIF_FALSE; |
756 | 0 | if (aomVersion > aomVersion_2_0_0) { |
757 | | // There exists a bug in libaom's chroma_check() function where it will attempt to |
758 | | // access nonexistent UV planes when encoding monochrome at faster libavif "speeds". It |
759 | | // was fixed shortly after the 2.0.0 libaom release, and the fix exists in both the |
760 | | // master and applejack branches. This ensures that the next version *after* 2.0.0 will |
761 | | // have the fix, and we must avoid cfg->monochrome until then. |
762 | | // |
763 | | // Bugfix Change-Id: https://aomedia-review.googlesource.com/q/I26a39791f820b4d4e1d63ff7141f594c3c7181f5 |
764 | |
|
765 | 0 | if (alpha || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) { |
766 | 0 | codec->internal->monochromeEnabled = AVIF_TRUE; |
767 | 0 | cfg->monochrome = 1; |
768 | 0 | } |
769 | 0 | } |
770 | |
|
771 | 0 | if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) { |
772 | 0 | return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; |
773 | 0 | } |
774 | | |
775 | 0 | int minQuantizer; |
776 | 0 | int maxQuantizer; |
777 | 0 | if (alpha) { |
778 | 0 | minQuantizer = encoder->minQuantizerAlpha; |
779 | 0 | maxQuantizer = encoder->maxQuantizerAlpha; |
780 | 0 | } else { |
781 | 0 | minQuantizer = encoder->minQuantizer; |
782 | 0 | maxQuantizer = encoder->maxQuantizer; |
783 | 0 | } |
784 | 0 | minQuantizer = AVIF_CLAMP(minQuantizer, 0, 63); |
785 | 0 | maxQuantizer = AVIF_CLAMP(maxQuantizer, 0, 63); |
786 | 0 | if ((cfg->rc_end_usage == AOM_VBR) || (cfg->rc_end_usage == AOM_CBR)) { |
787 | | // cq-level is ignored in these two end-usage modes, so adjust minQuantizer and |
788 | | // maxQuantizer to the target quantizer. |
789 | 0 | if (quantizer == AVIF_QUANTIZER_LOSSLESS) { |
790 | 0 | minQuantizer = AVIF_QUANTIZER_LOSSLESS; |
791 | 0 | maxQuantizer = AVIF_QUANTIZER_LOSSLESS; |
792 | 0 | } else { |
793 | 0 | minQuantizer = AVIF_MAX(quantizer - 4, minQuantizer); |
794 | 0 | maxQuantizer = AVIF_MIN(quantizer + 4, maxQuantizer); |
795 | 0 | } |
796 | 0 | } |
797 | 0 | cfg->rc_min_quantizer = minQuantizer; |
798 | 0 | cfg->rc_max_quantizer = maxQuantizer; |
799 | 0 | quantizerUpdated = AVIF_TRUE; |
800 | |
|
801 | 0 | aom_codec_flags_t encoderFlags = 0; |
802 | 0 | if (image->depth > 8) { |
803 | 0 | encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH; |
804 | 0 | } |
805 | 0 | if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) { |
806 | 0 | avifDiagnosticsPrintf(codec->diag, |
807 | 0 | "aom_codec_enc_init() failed: %s: %s", |
808 | 0 | aom_codec_error(&codec->internal->encoder), |
809 | 0 | aom_codec_error_detail(&codec->internal->encoder)); |
810 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
811 | 0 | } |
812 | 0 | codec->internal->encoderInitialized = AVIF_TRUE; |
813 | |
|
814 | 0 | if ((cfg->rc_end_usage == AOM_CQ) || (cfg->rc_end_usage == AOM_Q)) { |
815 | 0 | aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, quantizer); |
816 | 0 | } |
817 | 0 | avifBool lossless = (quantizer == AVIF_QUANTIZER_LOSSLESS); |
818 | 0 | if (lossless) { |
819 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, 1); |
820 | 0 | } |
821 | 0 | if (tileRowsLog2 != 0) { |
822 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); |
823 | 0 | } |
824 | 0 | if (tileColsLog2 != 0) { |
825 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); |
826 | 0 | } |
827 | 0 | if (encoder->extraLayerCount > 0) { |
828 | 0 | int layerCount = encoder->extraLayerCount + 1; |
829 | 0 | if (aom_codec_control(&codec->internal->encoder, AOME_SET_NUMBER_SPATIAL_LAYERS, layerCount) != AOM_CODEC_OK) { |
830 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
831 | 0 | } |
832 | 0 | } |
833 | 0 | if (aomCpuUsed != -1) { |
834 | 0 | if (aom_codec_control(&codec->internal->encoder, AOME_SET_CPUUSED, aomCpuUsed) != AOM_CODEC_OK) { |
835 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
836 | 0 | } |
837 | 0 | } |
838 | | |
839 | | // Set color_config() in the sequence header OBU. |
840 | 0 | if (alpha) { |
841 | | // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": |
842 | | // The color_range field in the Sequence Header OBU shall be set to 1. |
843 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, AOM_CR_FULL_RANGE); |
844 | | |
845 | | // Keep the default AOM_CSP_UNKNOWN value. |
846 | | |
847 | | // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. |
848 | | // Keep default Unspecified (2) colour primaries, transfer characteristics, |
849 | | // and matrix coefficients. |
850 | 0 | } else { |
851 | | // libaom's defaults are AOM_CSP_UNKNOWN and 0 (studio/limited range). |
852 | | // Call aom_codec_control() only if the values are not the defaults. |
853 | | |
854 | | // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": |
855 | | // The values of the fields in the AV1CodecConfigurationBox shall match those |
856 | | // of the Sequence Header OBU in the AV1 Image Item Data. |
857 | 0 | if (image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { |
858 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_CHROMA_SAMPLE_POSITION, (int)image->yuvChromaSamplePosition); |
859 | 0 | } |
860 | | |
861 | | // AV1-ISOBMFF specification, Section 2.3.4: |
862 | | // The value of full_range_flag in the 'colr' box SHALL match the color_range |
863 | | // flag in the Sequence Header OBU. |
864 | 0 | if (image->yuvRange != AVIF_RANGE_LIMITED) { |
865 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, (int)image->yuvRange); |
866 | 0 | } |
867 | | |
868 | | // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP |
869 | | // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). |
870 | | // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. |
871 | | // It may also save 3 bytes since the AV1 encoder can set color_description_present_flag to 0 |
872 | | // (see Section 5.5.2 "Color config syntax" of the AV1 specification). |
873 | | // libaom's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, and |
874 | | // AOM_CICP_MC_UNSPECIFIED. No need to call aom_codec_control(). |
875 | | // aom_image_t::cp, aom_image_t::tc and aom_image_t::mc are ignored by aom_codec_encode(). |
876 | 0 | } |
877 | |
|
878 | 0 | #if defined(AOM_CTRL_AV1E_SET_SKIP_POSTPROC_FILTERING) |
879 | 0 | if (cfg->g_usage == AOM_USAGE_ALL_INTRA) { |
880 | | // Enable AV1E_SET_SKIP_POSTPROC_FILTERING for still-picture encoding, which is |
881 | | // disabled by default. |
882 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_SKIP_POSTPROC_FILTERING, 1); |
883 | 0 | } |
884 | 0 | #endif |
885 | |
|
886 | 0 | if (!avifProcessAOMOptionsPostInit(codec, alpha)) { |
887 | 0 | return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; |
888 | 0 | } |
889 | 0 | if (!codec->internal->tuningSet) { |
890 | 0 | if (aom_codec_control(&codec->internal->encoder, AOME_SET_TUNING, AOM_TUNE_SSIM) != AOM_CODEC_OK) { |
891 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
892 | 0 | } |
893 | 0 | } |
894 | | |
895 | 0 | if (image->depth == 12) { |
896 | | // The encoder may produce integer overflows with 12-bit input when loop restoration is enabled. See crbug.com/aomedia/42302587. |
897 | 0 | if (aom_codec_control(&codec->internal->encoder, AV1E_SET_ENABLE_RESTORATION, 0) != AOM_CODEC_OK) { |
898 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
899 | 0 | } |
900 | 0 | } |
901 | 0 | } else { |
902 | 0 | avifBool dimensionsChanged = AVIF_FALSE; |
903 | 0 | if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) { |
904 | | // We are not ready for dimension change for now. |
905 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
906 | 0 | } |
907 | 0 | if (alpha) { |
908 | 0 | if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA)) { |
909 | 0 | cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); |
910 | 0 | cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); |
911 | 0 | quantizerUpdated = AVIF_TRUE; |
912 | 0 | } |
913 | 0 | } else { |
914 | 0 | if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER | AVIF_ENCODER_CHANGE_MAX_QUANTIZER)) { |
915 | 0 | cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); |
916 | 0 | cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); |
917 | 0 | quantizerUpdated = AVIF_TRUE; |
918 | 0 | } |
919 | 0 | } |
920 | 0 | const int quantizerChangedBit = alpha ? AVIF_ENCODER_CHANGE_QUANTIZER_ALPHA : AVIF_ENCODER_CHANGE_QUANTIZER; |
921 | 0 | if (encoderChanges & quantizerChangedBit) { |
922 | 0 | if ((cfg->rc_end_usage == AOM_VBR) || (cfg->rc_end_usage == AOM_CBR)) { |
923 | | // cq-level is ignored in these two end-usage modes, so adjust minQuantizer and |
924 | | // maxQuantizer to the target quantizer. |
925 | 0 | if (quantizer == AVIF_QUANTIZER_LOSSLESS) { |
926 | 0 | cfg->rc_min_quantizer = AVIF_QUANTIZER_LOSSLESS; |
927 | 0 | cfg->rc_max_quantizer = AVIF_QUANTIZER_LOSSLESS; |
928 | 0 | } else { |
929 | 0 | int minQuantizer; |
930 | 0 | int maxQuantizer; |
931 | 0 | if (alpha) { |
932 | 0 | minQuantizer = encoder->minQuantizerAlpha; |
933 | 0 | maxQuantizer = encoder->maxQuantizerAlpha; |
934 | 0 | } else { |
935 | 0 | minQuantizer = encoder->minQuantizer; |
936 | 0 | maxQuantizer = encoder->maxQuantizer; |
937 | 0 | } |
938 | 0 | minQuantizer = AVIF_CLAMP(minQuantizer, 0, 63); |
939 | 0 | maxQuantizer = AVIF_CLAMP(maxQuantizer, 0, 63); |
940 | 0 | cfg->rc_min_quantizer = AVIF_MAX(quantizer - 4, minQuantizer); |
941 | 0 | cfg->rc_max_quantizer = AVIF_MIN(quantizer + 4, maxQuantizer); |
942 | 0 | } |
943 | 0 | quantizerUpdated = AVIF_TRUE; |
944 | 0 | } |
945 | 0 | } |
946 | 0 | if (quantizerUpdated || dimensionsChanged) { |
947 | 0 | aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg); |
948 | 0 | if (err != AOM_CODEC_OK) { |
949 | 0 | avifDiagnosticsPrintf(codec->diag, |
950 | 0 | "aom_codec_enc_config_set() failed: %s: %s", |
951 | 0 | aom_codec_error(&codec->internal->encoder), |
952 | 0 | aom_codec_error_detail(&codec->internal->encoder)); |
953 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
954 | 0 | } |
955 | 0 | } |
956 | 0 | if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2) { |
957 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); |
958 | 0 | } |
959 | 0 | if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_COLS_LOG2) { |
960 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); |
961 | 0 | } |
962 | 0 | if (encoderChanges & quantizerChangedBit) { |
963 | 0 | if ((cfg->rc_end_usage == AOM_CQ) || (cfg->rc_end_usage == AOM_Q)) { |
964 | 0 | aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, quantizer); |
965 | 0 | } |
966 | 0 | avifBool lossless = (quantizer == AVIF_QUANTIZER_LOSSLESS); |
967 | 0 | aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); |
968 | 0 | } |
969 | 0 | if (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) { |
970 | 0 | if (!avifProcessAOMOptionsPostInit(codec, alpha)) { |
971 | 0 | return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; |
972 | 0 | } |
973 | 0 | } |
974 | 0 | } |
975 | | |
976 | 0 | if (codec->internal->currentLayer > encoder->extraLayerCount) { |
977 | 0 | avifDiagnosticsPrintf(codec->diag, |
978 | 0 | "Too many layers sent. Expected %u layers, but got %u layers.", |
979 | 0 | encoder->extraLayerCount + 1, |
980 | 0 | codec->internal->currentLayer + 1); |
981 | 0 | return AVIF_RESULT_INVALID_ARGUMENT; |
982 | 0 | } |
983 | 0 | if (encoder->extraLayerCount > 0) { |
984 | 0 | aom_codec_control(&codec->internal->encoder, AOME_SET_SPATIAL_LAYER_ID, codec->internal->currentLayer); |
985 | 0 | } |
986 | |
|
987 | 0 | aom_scaling_mode_t aomScalingMode; |
988 | 0 | if (!avifFindAOMScalingMode(&encoder->scalingMode.horizontal, &aomScalingMode.h_scaling_mode)) { |
989 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
990 | 0 | } |
991 | 0 | if (!avifFindAOMScalingMode(&encoder->scalingMode.vertical, &aomScalingMode.v_scaling_mode)) { |
992 | 0 | return AVIF_RESULT_NOT_IMPLEMENTED; |
993 | 0 | } |
994 | 0 | if ((aomScalingMode.h_scaling_mode != AOME_NORMAL) || (aomScalingMode.v_scaling_mode != AOME_NORMAL)) { |
995 | | // AOME_SET_SCALEMODE only applies to next frame (layer), so we have to set it every time. |
996 | 0 | aom_codec_control(&codec->internal->encoder, AOME_SET_SCALEMODE, &aomScalingMode); |
997 | 0 | } |
998 | |
|
999 | 0 | aom_image_t aomImage; |
1000 | | // We prefer to simply set the aomImage.planes[] pointers to the plane buffers in 'image'. When |
1001 | | // doing this, we set aomImage.w equal to aomImage.d_w and aomImage.h equal to aomImage.d_h and |
1002 | | // do not "align" aomImage.w and aomImage.h. Unfortunately this exposes a bug in libaom |
1003 | | // (https://crbug.com/aomedia/3113) if chroma is subsampled and image->width or image->height is |
1004 | | // equal to 1. To work around this libaom bug, we allocate the aomImage.planes[] buffers and |
1005 | | // copy the image YUV data if image->width or image->height is equal to 1. This bug has been |
1006 | | // fixed in libaom v3.1.3. |
1007 | | // |
1008 | | // Note: The exact condition for the bug is |
1009 | | // ((image->width == 1) && (chroma is subsampled horizontally)) || |
1010 | | // ((image->height == 1) && (chroma is subsampled vertically)) |
1011 | | // Since an image width or height of 1 is uncommon in practice, we test an inexact but simpler |
1012 | | // condition. |
1013 | 0 | avifBool aomImageAllocated = (image->width == 1) || (image->height == 1); |
1014 | 0 | if (aomImageAllocated) { |
1015 | 0 | aom_img_alloc(&aomImage, codec->internal->aomFormat, image->width, image->height, 16); |
1016 | 0 | } else { |
1017 | 0 | memset(&aomImage, 0, sizeof(aomImage)); |
1018 | 0 | aomImage.fmt = codec->internal->aomFormat; |
1019 | 0 | aomImage.bit_depth = (image->depth > 8) ? 16 : 8; |
1020 | 0 | aomImage.w = image->width; |
1021 | 0 | aomImage.h = image->height; |
1022 | 0 | aomImage.d_w = image->width; |
1023 | 0 | aomImage.d_h = image->height; |
1024 | | // Get sample size for this format. |
1025 | 0 | unsigned int bps; |
1026 | 0 | if (codec->internal->aomFormat == AOM_IMG_FMT_I420) { |
1027 | 0 | bps = 12; |
1028 | 0 | } else if (codec->internal->aomFormat == AOM_IMG_FMT_I422) { |
1029 | 0 | bps = 16; |
1030 | 0 | } else if (codec->internal->aomFormat == AOM_IMG_FMT_I444) { |
1031 | 0 | bps = 24; |
1032 | 0 | } else if (codec->internal->aomFormat == AOM_IMG_FMT_I42016) { |
1033 | 0 | bps = 24; |
1034 | 0 | } else if (codec->internal->aomFormat == AOM_IMG_FMT_I42216) { |
1035 | 0 | bps = 32; |
1036 | 0 | } else if (codec->internal->aomFormat == AOM_IMG_FMT_I44416) { |
1037 | 0 | bps = 48; |
1038 | 0 | } else { |
1039 | 0 | bps = 16; |
1040 | 0 | } |
1041 | 0 | aomImage.bps = bps; |
1042 | | // See avifImageCalcAOMFmt(). libaom doesn't have AOM_IMG_FMT_I400, so we use AOM_IMG_FMT_I420 as a substitute for monochrome. |
1043 | 0 | aomImage.x_chroma_shift = (alpha || codec->internal->formatInfo.monochrome) ? 1 : codec->internal->formatInfo.chromaShiftX; |
1044 | 0 | aomImage.y_chroma_shift = (alpha || codec->internal->formatInfo.monochrome) ? 1 : codec->internal->formatInfo.chromaShiftY; |
1045 | 0 | } |
1046 | |
|
1047 | 0 | avifBool monochromeRequested = AVIF_FALSE; |
1048 | |
|
1049 | 0 | if (alpha) { |
1050 | | // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": |
1051 | | // The color_range field in the Sequence Header OBU shall be set to 1. |
1052 | 0 | aomImage.range = AOM_CR_FULL_RANGE; |
1053 | | |
1054 | | // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": |
1055 | | // The mono_chrome field in the Sequence Header OBU shall be set to 1. |
1056 | | // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. |
1057 | 0 | monochromeRequested = AVIF_TRUE; |
1058 | 0 | if (aomImageAllocated) { |
1059 | 0 | const uint32_t bytesPerRow = ((image->depth > 8) ? 2 : 1) * image->width; |
1060 | 0 | for (uint32_t j = 0; j < image->height; ++j) { |
1061 | 0 | const uint8_t * srcAlphaRow = &image->alphaPlane[j * image->alphaRowBytes]; |
1062 | 0 | uint8_t * dstAlphaRow = &aomImage.planes[0][j * aomImage.stride[0]]; |
1063 | 0 | memcpy(dstAlphaRow, srcAlphaRow, bytesPerRow); |
1064 | 0 | } |
1065 | 0 | } else { |
1066 | 0 | aomImage.planes[0] = image->alphaPlane; |
1067 | 0 | aomImage.stride[0] = image->alphaRowBytes; |
1068 | 0 | } |
1069 | | |
1070 | | // Ignore UV planes when monochrome. Keep the default AOM_CSP_UNKNOWN value. |
1071 | 0 | } else { |
1072 | 0 | int yuvPlaneCount = 3; |
1073 | 0 | if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { |
1074 | 0 | yuvPlaneCount = 1; // Ignore UV planes when monochrome |
1075 | 0 | monochromeRequested = AVIF_TRUE; |
1076 | 0 | } |
1077 | 0 | if (aomImageAllocated) { |
1078 | 0 | uint32_t bytesPerPixel = (image->depth > 8) ? 2 : 1; |
1079 | 0 | for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { |
1080 | 0 | uint32_t planeWidth = avifImagePlaneWidth(image, yuvPlane); |
1081 | 0 | uint32_t planeHeight = avifImagePlaneHeight(image, yuvPlane); |
1082 | 0 | uint32_t bytesPerRow = bytesPerPixel * planeWidth; |
1083 | |
|
1084 | 0 | for (uint32_t j = 0; j < planeHeight; ++j) { |
1085 | 0 | const uint8_t * srcRow = &image->yuvPlanes[yuvPlane][j * image->yuvRowBytes[yuvPlane]]; |
1086 | 0 | uint8_t * dstRow = &aomImage.planes[yuvPlane][j * aomImage.stride[yuvPlane]]; |
1087 | 0 | memcpy(dstRow, srcRow, bytesPerRow); |
1088 | 0 | } |
1089 | 0 | } |
1090 | 0 | } else { |
1091 | 0 | for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { |
1092 | 0 | aomImage.planes[yuvPlane] = image->yuvPlanes[yuvPlane]; |
1093 | 0 | aomImage.stride[yuvPlane] = image->yuvRowBytes[yuvPlane]; |
1094 | 0 | } |
1095 | 0 | } |
1096 | | |
1097 | | // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": |
1098 | | // The values of the fields in the AV1CodecConfigurationBox shall match those |
1099 | | // of the Sequence Header OBU in the AV1 Image Item Data. |
1100 | 0 | aomImage.csp = (aom_chroma_sample_position_t)image->yuvChromaSamplePosition; |
1101 | | |
1102 | | // AV1-ISOBMFF specification, Section 2.3.4: |
1103 | | // The value of full_range_flag in the 'colr' box SHALL match the color_range |
1104 | | // flag in the Sequence Header OBU. |
1105 | 0 | aomImage.range = (aom_color_range_t)image->yuvRange; |
1106 | 0 | } |
1107 | |
|
1108 | 0 | unsigned char * monoUVPlane = NULL; |
1109 | 0 | if (monochromeRequested) { |
1110 | 0 | if (codec->internal->monochromeEnabled) { |
1111 | 0 | aomImage.monochrome = 1; |
1112 | 0 | } else { |
1113 | | // The user requested monochrome (via alpha or YUV400) but libaom cannot currently support |
1114 | | // monochrome (see chroma_check comment above). Manually set UV planes to 0.5. |
1115 | | |
1116 | | // aomImage is always 420 when we're monochrome |
1117 | 0 | uint32_t monoUVWidth = (image->width + 1) >> 1; |
1118 | 0 | uint32_t monoUVHeight = (image->height + 1) >> 1; |
1119 | | |
1120 | | // Allocate the U plane if necessary. |
1121 | 0 | if (!aomImageAllocated) { |
1122 | 0 | uint32_t channelSize = avifImageUsesU16(image) ? 2 : 1; |
1123 | 0 | uint32_t monoUVRowBytes = channelSize * monoUVWidth; |
1124 | 0 | size_t monoUVSize = (size_t)monoUVHeight * monoUVRowBytes; |
1125 | |
|
1126 | 0 | monoUVPlane = avifAlloc(monoUVSize); |
1127 | 0 | AVIF_CHECKERR(monoUVPlane != NULL, AVIF_RESULT_OUT_OF_MEMORY); // No need for aom_img_free() because !aomImageAllocated |
1128 | 0 | aomImage.planes[1] = monoUVPlane; |
1129 | 0 | aomImage.stride[1] = monoUVRowBytes; |
1130 | 0 | } |
1131 | | // Set the U plane to 0.5. |
1132 | 0 | if (image->depth > 8) { |
1133 | 0 | const uint16_t half = (uint16_t)(1 << (image->depth - 1)); |
1134 | 0 | for (uint32_t j = 0; j < monoUVHeight; ++j) { |
1135 | 0 | uint16_t * dstRow = (uint16_t *)&aomImage.planes[1][(size_t)j * aomImage.stride[1]]; |
1136 | 0 | for (uint32_t i = 0; i < monoUVWidth; ++i) { |
1137 | 0 | dstRow[i] = half; |
1138 | 0 | } |
1139 | 0 | } |
1140 | 0 | } else { |
1141 | 0 | const uint8_t half = 128; |
1142 | 0 | size_t planeSize = (size_t)monoUVHeight * aomImage.stride[1]; |
1143 | 0 | memset(aomImage.planes[1], half, planeSize); |
1144 | 0 | } |
1145 | | // Make the V plane the same as the U plane. |
1146 | 0 | aomImage.planes[2] = aomImage.planes[1]; |
1147 | 0 | aomImage.stride[2] = aomImage.stride[1]; |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | 0 | aom_enc_frame_flags_t encodeFlags = 0; |
1152 | 0 | if (addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) { |
1153 | 0 | encodeFlags |= AOM_EFLAG_FORCE_KF; |
1154 | 0 | } |
1155 | 0 | if (codec->internal->currentLayer > 0) { |
1156 | 0 | encodeFlags |= AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_REF_BWD | AOM_EFLAG_NO_REF_ARF2 | |
1157 | 0 | AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF; |
1158 | 0 | } |
1159 | 0 | aom_codec_err_t encodeErr = aom_codec_encode(&codec->internal->encoder, &aomImage, 0, 1, encodeFlags); |
1160 | 0 | avifFree(monoUVPlane); |
1161 | 0 | if (aomImageAllocated) { |
1162 | 0 | aom_img_free(&aomImage); |
1163 | 0 | } |
1164 | 0 | if (encodeErr != AOM_CODEC_OK) { |
1165 | 0 | avifDiagnosticsPrintf(codec->diag, |
1166 | 0 | "aom_codec_encode() failed: %s: %s", |
1167 | 0 | aom_codec_error(&codec->internal->encoder), |
1168 | 0 | aom_codec_error_detail(&codec->internal->encoder)); |
1169 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
1170 | 0 | } |
1171 | | |
1172 | 0 | aom_codec_iter_t iter = NULL; |
1173 | 0 | for (;;) { |
1174 | 0 | const aom_codec_cx_pkt_t * pkt = aom_codec_get_cx_data(&codec->internal->encoder, &iter); |
1175 | 0 | if (pkt == NULL) { |
1176 | 0 | break; |
1177 | 0 | } |
1178 | 0 | if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { |
1179 | 0 | AVIF_CHECKRES( |
1180 | 0 | avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY))); |
1181 | 0 | } |
1182 | 0 | } |
1183 | | |
1184 | 0 | if ((addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) || |
1185 | 0 | ((encoder->extraLayerCount > 0) && (encoder->extraLayerCount == codec->internal->currentLayer))) { |
1186 | | // Flush and clean up encoder resources early to save on overhead when encoding alpha or grid images, |
1187 | | // as encoding is finished now. For layered image, encoding finishes when the last layer is encoded. |
1188 | |
|
1189 | 0 | if (!aomCodecEncodeFinish(codec, output)) { |
1190 | 0 | return AVIF_RESULT_UNKNOWN_ERROR; |
1191 | 0 | } |
1192 | 0 | aom_codec_destroy(&codec->internal->encoder); |
1193 | 0 | codec->internal->encoderInitialized = AVIF_FALSE; |
1194 | 0 | } |
1195 | 0 | if (encoder->extraLayerCount > 0) { |
1196 | 0 | ++codec->internal->currentLayer; |
1197 | 0 | } |
1198 | 0 | return AVIF_RESULT_OK; |
1199 | 0 | } |
1200 | | |
1201 | | static avifBool aomCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output) |
1202 | 0 | { |
1203 | 0 | if (!codec->internal->encoderInitialized) { |
1204 | 0 | return AVIF_TRUE; |
1205 | 0 | } |
1206 | 0 | for (;;) { |
1207 | | // flush encoder |
1208 | 0 | if (aom_codec_encode(&codec->internal->encoder, NULL, 0, 1, 0) != AOM_CODEC_OK) { |
1209 | 0 | avifDiagnosticsPrintf(codec->diag, |
1210 | 0 | "aom_codec_encode() with img=NULL failed: %s: %s", |
1211 | 0 | aom_codec_error(&codec->internal->encoder), |
1212 | 0 | aom_codec_error_detail(&codec->internal->encoder)); |
1213 | 0 | return AVIF_FALSE; |
1214 | 0 | } |
1215 | | |
1216 | 0 | avifBool gotPacket = AVIF_FALSE; |
1217 | 0 | aom_codec_iter_t iter = NULL; |
1218 | 0 | for (;;) { |
1219 | 0 | const aom_codec_cx_pkt_t * pkt = aom_codec_get_cx_data(&codec->internal->encoder, &iter); |
1220 | 0 | if (pkt == NULL) { |
1221 | 0 | break; |
1222 | 0 | } |
1223 | 0 | if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { |
1224 | 0 | gotPacket = AVIF_TRUE; |
1225 | 0 | const avifResult result = avifCodecEncodeOutputAddSample(output, |
1226 | 0 | pkt->data.frame.buf, |
1227 | 0 | pkt->data.frame.sz, |
1228 | 0 | (pkt->data.frame.flags & AOM_FRAME_IS_KEY)); |
1229 | 0 | if (result != AVIF_RESULT_OK) { |
1230 | 0 | avifDiagnosticsPrintf(codec->diag, "avifCodecEncodeOutputAddSample() failed: %s", avifResultToString(result)); |
1231 | 0 | return AVIF_FALSE; |
1232 | 0 | } |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | 0 | if (!gotPacket) { |
1237 | 0 | break; |
1238 | 0 | } |
1239 | 0 | } |
1240 | 0 | return AVIF_TRUE; |
1241 | 0 | } |
1242 | | |
1243 | | #endif // defined(AVIF_CODEC_AOM_ENCODE) |
1244 | | |
1245 | | const char * avifCodecVersionAOM(void) |
1246 | 0 | { |
1247 | 0 | return aom_codec_version_str(); |
1248 | 0 | } |
1249 | | |
1250 | | avifCodec * avifCodecCreateAOM(void) |
1251 | 12.2k | { |
1252 | 12.2k | avifCodec * codec = (avifCodec *)avifAlloc(sizeof(avifCodec)); |
1253 | 12.2k | if (codec == NULL) { |
1254 | 0 | return NULL; |
1255 | 0 | } |
1256 | 12.2k | memset(codec, 0, sizeof(struct avifCodec)); |
1257 | | |
1258 | 12.2k | #if defined(AVIF_CODEC_AOM_DECODE) |
1259 | 12.2k | codec->getNextImage = aomCodecGetNextImage; |
1260 | 12.2k | #endif |
1261 | | |
1262 | 12.2k | #if defined(AVIF_CODEC_AOM_ENCODE) |
1263 | 12.2k | codec->encodeImage = aomCodecEncodeImage; |
1264 | 12.2k | codec->encodeFinish = aomCodecEncodeFinish; |
1265 | 12.2k | #endif |
1266 | | |
1267 | 12.2k | codec->destroyInternal = aomCodecDestroyInternal; |
1268 | 12.2k | codec->internal = (struct avifCodecInternal *)avifAlloc(sizeof(struct avifCodecInternal)); |
1269 | 12.2k | if (codec->internal == NULL) { |
1270 | 0 | avifFree(codec); |
1271 | 0 | return NULL; |
1272 | 0 | } |
1273 | 12.2k | memset(codec->internal, 0, sizeof(struct avifCodecInternal)); |
1274 | 12.2k | return codec; |
1275 | 12.2k | } |
1276 | | |
1277 | | #ifdef __clang__ |
1278 | | #pragma clang diagnostic pop |
1279 | | #endif |