Coverage Report

Created: 2025-09-08 07:52

/src/libheif/libheif/plugins/encoder_aom.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "libheif/heif.h"
22
#include "libheif/heif_plugin.h"
23
#include "common_utils.h"
24
#include <algorithm>
25
#include <cstring>
26
#include <cassert>
27
#include <sstream>
28
#include <vector>
29
#include <string>
30
#include <thread>
31
#include <memory>
32
#include <utility>
33
#include "encoder_aom.h"
34
35
#include <aom/aom_encoder.h>
36
#include <aom/aomcx.h>
37
#include <mutex>
38
39
// Detect whether the aom_codec_set_option() function is available.
40
// See https://aomedia.googlesource.com/aom/+/c1d42fe6615c96fc929257ed53c41fa094f38836%5E%21/aom/aom_codec.h.
41
#if AOM_CODEC_ABI_VERSION >= (6 + AOM_IMAGE_ABI_VERSION)
42
#define HAVE_AOM_CODEC_SET_OPTION 1
43
#endif
44
45
#if defined(HAVE_AOM_CODEC_SET_OPTION)
46
struct custom_option
47
{
48
    std::string name;
49
    std::string value;
50
};
51
#endif
52
53
struct encoder_struct_aom
54
{
55
  ~encoder_struct_aom()
56
0
  {
57
0
    for (auto* error : aom_errors) {
58
0
      delete[] error;
59
0
    }
60
0
  }
61
62
  // --- parameters
63
64
  bool realtime_mode;
65
  int cpu_used;  // = parameter 'speed'. I guess this is a better name than 'cpu_used'.
66
67
  int quality;
68
  int alpha_quality;
69
  int min_q;
70
  int max_q;
71
  int alpha_min_q;
72
  int alpha_max_q;
73
  int threads;
74
  bool lossless;
75
  bool lossless_alpha;
76
  bool auto_tiles;
77
  bool enable_intra_block_copy;
78
79
#if defined(HAVE_AOM_CODEC_SET_OPTION)
80
  std::vector<custom_option> custom_options;
81
82
  void add_custom_option(const custom_option&);
83
84
  void add_custom_option(std::string name, std::string value);
85
#endif
86
87
  aom_tune_metric tune;
88
89
  heif_chroma chroma = heif_chroma_420;
90
91
  // --- input
92
93
  bool alpha_quality_set = false;
94
  bool alpha_min_q_set = false;
95
  bool alpha_max_q_set = false;
96
97
  // --- output
98
99
  std::vector<uint8_t> compressedData;
100
  bool data_read = false;
101
102
  // --- error message copies
103
104
  std::mutex aom_errors_mutex;
105
  std::vector<const char*> aom_errors;
106
107
  const char* set_aom_error(const char* aom_error_detail);
108
};
109
110
#if defined(HAVE_AOM_CODEC_SET_OPTION)
111
112
void encoder_struct_aom::add_custom_option(const custom_option& p)
113
0
{
114
  // if there is already a parameter of that name, remove it from list
115
116
0
  for (auto iter = custom_options.begin(); iter != custom_options.end(); ++iter) {
117
0
    if (iter->name == p.name) {
118
0
      custom_options.erase(iter);
119
0
      break;
120
0
    }
121
0
  }
122
123
  // and add the new parameter at the end of the list
124
125
0
  custom_options.push_back(p);
126
0
}
127
128
void encoder_struct_aom::add_custom_option(std::string name, std::string value)
129
0
{
130
0
  custom_option p;
131
0
  p.name = std::move(name);
132
0
  p.value = std::move(value);
133
0
  add_custom_option(p);
134
0
}
135
136
#endif
137
138
static const char* kError_undefined_error = "Undefined AOM error";
139
static const char* kError_codec_enc_config_default = "Error creating the default encoder config";
140
141
const char* encoder_struct_aom::set_aom_error(const char* aom_error)
142
0
{
143
0
  if (aom_error) {
144
    // We have to make a copy because the error returned from aom_codec_error_detail() is only valid
145
    // while the codec structure exists.
146
147
0
    char* err_copy = new char[strlen(aom_error) + 1];
148
0
    strcpy(err_copy, aom_error);
149
150
0
    std::lock_guard<std::mutex> lock(aom_errors_mutex);
151
0
    aom_errors.push_back(err_copy);
152
153
0
    return err_copy;
154
0
  }
155
0
  else {
156
0
    return kError_undefined_error;
157
0
  }
158
0
}
159
160
static const char* kParam_min_q = "min-q";
161
static const char* kParam_max_q = "max-q";
162
static const char* kParam_alpha_quality = "alpha-quality";
163
static const char* kParam_alpha_min_q = "alpha-min-q";
164
static const char* kParam_alpha_max_q = "alpha-max-q";
165
static const char* kParam_lossless_alpha = "lossless-alpha";
166
static const char* kParam_auto_tiles = "auto-tiles";
167
static const char* kParam_enable_intra_block_copy = "enable-intrabc";
168
static const char* kParam_threads = "threads";
169
static const char* kParam_realtime = "realtime";
170
static const char* kParam_speed = "speed";
171
172
static const char* kParam_chroma = "chroma";
173
static const char* const kParam_chroma_valid_values[] = {
174
    "420", "422", "444", nullptr
175
};
176
177
static const char* kParam_tune = "tune";
178
static const char* const kParam_tune_valid_values[] = {
179
    "psnr", "ssim", "iq", nullptr
180
};
181
182
static const int AOM_PLUGIN_PRIORITY = 60;
183
184
0
#define MAX_PLUGIN_NAME_LENGTH 80
185
186
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
187
188
189
static void aom_set_default_parameters(void* encoder);
190
191
192
static const char* aom_plugin_name()
193
0
{
194
0
  const char* encoder_name = aom_codec_iface_name(aom_codec_av1_cx());
195
0
  if (strlen(encoder_name) < MAX_PLUGIN_NAME_LENGTH) {
196
0
    strcpy(plugin_name, encoder_name);
197
0
  }
198
0
  else {
199
0
    strcpy(plugin_name, "AOMedia AV1 encoder");
200
0
  }
201
202
0
  return plugin_name;
203
0
}
204
205
206
#define MAX_NPARAMETERS 16
207
208
static struct heif_encoder_parameter aom_encoder_params[MAX_NPARAMETERS];
209
static const struct heif_encoder_parameter* aom_encoder_parameter_ptrs[MAX_NPARAMETERS + 1];
210
211
static void aom_init_parameters()
212
19.1k
{
213
19.1k
  struct heif_encoder_parameter* p = aom_encoder_params;
214
19.1k
  const struct heif_encoder_parameter** d = aom_encoder_parameter_ptrs;
215
19.1k
  int i = 0;
216
217
19.1k
  assert(i < MAX_NPARAMETERS);
218
19.1k
  p->version = 2;
219
19.1k
  p->name = kParam_realtime;
220
19.1k
  p->type = heif_encoder_parameter_type_boolean;
221
19.1k
  p->boolean.default_value = false;
222
19.1k
  p->has_default = true;
223
19.1k
  d[i++] = p++;
224
225
19.1k
  assert(i < MAX_NPARAMETERS);
226
19.1k
  p->version = 2;
227
19.1k
  p->name = kParam_speed;
228
19.1k
  p->type = heif_encoder_parameter_type_integer;
229
19.1k
  p->integer.default_value = 6;
230
19.1k
  p->has_default = true;
231
19.1k
  p->integer.have_minimum_maximum = true;
232
19.1k
  p->integer.minimum = 0;
233
19.1k
  if (aom_codec_version_major() >= 3) {
234
19.1k
    p->integer.maximum = 9;
235
19.1k
  }
236
0
  else {
237
0
    p->integer.maximum = 8;
238
0
  }
239
19.1k
  p->integer.valid_values = NULL;
240
19.1k
  p->integer.num_valid_values = 0;
241
19.1k
  d[i++] = p++;
242
243
19.1k
  assert(i < MAX_NPARAMETERS);
244
19.1k
  p->version = 2;
245
19.1k
  p->name = kParam_threads;
246
19.1k
  p->type = heif_encoder_parameter_type_integer;
247
19.1k
  p->has_default = true;
248
19.1k
  p->integer.have_minimum_maximum = true;
249
19.1k
  p->integer.minimum = 1;
250
19.1k
  p->integer.maximum = 64;
251
19.1k
  int threads = static_cast<int>(std::thread::hardware_concurrency());
252
19.1k
  if (threads == 0) {
253
    // Could not autodetect, use previous default value.
254
0
    threads = 4;
255
0
  }
256
19.1k
  threads = std::min(threads, p->integer.maximum);
257
19.1k
  p->integer.default_value = threads;
258
19.1k
  p->integer.valid_values = NULL;
259
19.1k
  p->integer.num_valid_values = 0;
260
19.1k
  d[i++] = p++;
261
262
19.1k
  assert(i < MAX_NPARAMETERS);
263
19.1k
  p->version = 2;
264
19.1k
  p->name = heif_encoder_parameter_name_quality;
265
19.1k
  p->type = heif_encoder_parameter_type_integer;
266
19.1k
  p->integer.default_value = 50;
267
19.1k
  p->has_default = true;
268
19.1k
  p->integer.have_minimum_maximum = true;
269
19.1k
  p->integer.minimum = 0;
270
19.1k
  p->integer.maximum = 100;
271
19.1k
  p->integer.valid_values = NULL;
272
19.1k
  p->integer.num_valid_values = 0;
273
19.1k
  d[i++] = p++;
274
275
19.1k
  assert(i < MAX_NPARAMETERS);
276
19.1k
  p->version = 2;
277
19.1k
  p->name = heif_encoder_parameter_name_lossless;
278
19.1k
  p->type = heif_encoder_parameter_type_boolean;
279
19.1k
  p->boolean.default_value = false;
280
19.1k
  p->has_default = true;
281
19.1k
  d[i++] = p++;
282
283
19.1k
  assert(i < MAX_NPARAMETERS);
284
19.1k
  p->version = 2;
285
19.1k
  p->name = kParam_chroma;
286
19.1k
  p->type = heif_encoder_parameter_type_string;
287
19.1k
  p->string.default_value = "420";
288
19.1k
  p->has_default = true;
289
19.1k
  p->string.valid_values = kParam_chroma_valid_values;
290
19.1k
  d[i++] = p++;
291
292
19.1k
  assert(i < MAX_NPARAMETERS);
293
19.1k
  p->version = 2;
294
19.1k
  p->name = kParam_tune;
295
19.1k
  p->type = heif_encoder_parameter_type_string;
296
19.1k
  p->string.default_value = "ssim";
297
19.1k
  p->has_default = true;
298
19.1k
  p->string.valid_values = kParam_tune_valid_values;
299
19.1k
  d[i++] = p++;
300
301
19.1k
  assert(i < MAX_NPARAMETERS);
302
19.1k
  p->version = 2;
303
19.1k
  p->name = kParam_min_q;
304
19.1k
  p->type = heif_encoder_parameter_type_integer;
305
19.1k
  p->integer.default_value = 0;
306
19.1k
  p->has_default = true;
307
19.1k
  p->integer.have_minimum_maximum = true;
308
19.1k
  p->integer.minimum = 0;
309
19.1k
  p->integer.maximum = 63;
310
19.1k
  p->integer.valid_values = NULL;
311
19.1k
  p->integer.num_valid_values = 0;
312
19.1k
  d[i++] = p++;
313
314
19.1k
  assert(i < MAX_NPARAMETERS);
315
19.1k
  p->version = 2;
316
19.1k
  p->name = kParam_max_q;
317
19.1k
  p->type = heif_encoder_parameter_type_integer;
318
19.1k
  p->integer.default_value = 63;
319
19.1k
  p->has_default = true;
320
19.1k
  p->integer.have_minimum_maximum = true;
321
19.1k
  p->integer.minimum = 0;
322
19.1k
  p->integer.maximum = 63;
323
19.1k
  p->integer.valid_values = NULL;
324
19.1k
  p->integer.num_valid_values = 0;
325
19.1k
  d[i++] = p++;
326
327
19.1k
  assert(i < MAX_NPARAMETERS);
328
19.1k
  p->version = 2;
329
19.1k
  p->name = kParam_alpha_quality;
330
19.1k
  p->type = heif_encoder_parameter_type_integer;
331
19.1k
  p->has_default = false;
332
19.1k
  p->integer.have_minimum_maximum = true;
333
19.1k
  p->integer.minimum = 0;
334
19.1k
  p->integer.maximum = 100;
335
19.1k
  p->integer.valid_values = NULL;
336
19.1k
  p->integer.num_valid_values = 0;
337
19.1k
  d[i++] = p++;
338
339
19.1k
  assert(i < MAX_NPARAMETERS);
340
19.1k
  p->version = 2;
341
19.1k
  p->name = kParam_alpha_min_q;
342
19.1k
  p->type = heif_encoder_parameter_type_integer;
343
19.1k
  p->has_default = false;
344
19.1k
  p->integer.have_minimum_maximum = true;
345
19.1k
  p->integer.minimum = 0;
346
19.1k
  p->integer.maximum = 63;
347
19.1k
  p->integer.valid_values = NULL;
348
19.1k
  p->integer.num_valid_values = 0;
349
19.1k
  d[i++] = p++;
350
351
19.1k
  assert(i < MAX_NPARAMETERS);
352
19.1k
  p->version = 2;
353
19.1k
  p->name = kParam_alpha_max_q;
354
19.1k
  p->type = heif_encoder_parameter_type_integer;
355
19.1k
  p->has_default = false;
356
19.1k
  p->integer.have_minimum_maximum = true;
357
19.1k
  p->integer.minimum = 0;
358
19.1k
  p->integer.maximum = 63;
359
19.1k
  p->integer.valid_values = NULL;
360
19.1k
  p->integer.num_valid_values = 0;
361
19.1k
  d[i++] = p++;
362
363
19.1k
  assert(i < MAX_NPARAMETERS);
364
19.1k
  p->version = 2;
365
19.1k
  p->name = kParam_lossless_alpha;
366
19.1k
  p->type = heif_encoder_parameter_type_boolean;
367
19.1k
  p->boolean.default_value = false;
368
19.1k
  p->has_default = true;
369
19.1k
  d[i++] = p++;
370
371
19.1k
  assert(i < MAX_NPARAMETERS);
372
19.1k
  p->version = 2;
373
19.1k
  p->name = kParam_auto_tiles;
374
19.1k
  p->type = heif_encoder_parameter_type_boolean;
375
19.1k
  p->boolean.default_value = false;
376
19.1k
  p->has_default = true;
377
19.1k
  d[i++] = p++;
378
379
19.1k
  assert(i < MAX_NPARAMETERS);
380
19.1k
  p->version = 2;
381
19.1k
  p->name = kParam_enable_intra_block_copy;
382
19.1k
  p->type = heif_encoder_parameter_type_boolean;
383
19.1k
  p->boolean.default_value = true;
384
19.1k
  p->has_default = true;
385
19.1k
  d[i++] = p++;
386
387
19.1k
  assert(i < MAX_NPARAMETERS + 1);
388
19.1k
  d[i++] = nullptr;
389
19.1k
}
390
391
392
const struct heif_encoder_parameter** aom_list_parameters(void* encoder)
393
0
{
394
0
  return aom_encoder_parameter_ptrs;
395
0
}
396
397
static void aom_init_plugin()
398
19.1k
{
399
19.1k
  aom_init_parameters();
400
19.1k
}
401
402
403
static void aom_cleanup_plugin()
404
19.1k
{
405
19.1k
}
406
407
struct heif_error aom_new_encoder(void** enc)
408
0
{
409
0
  struct encoder_struct_aom* encoder = new encoder_struct_aom();
410
0
  struct heif_error err = heif_error_ok;
411
412
0
  *enc = encoder;
413
414
  // set default parameters
415
416
0
  aom_set_default_parameters(encoder);
417
418
0
  return err;
419
0
}
420
421
void aom_free_encoder(void* encoder_raw)
422
0
{
423
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
424
425
0
  delete encoder;
426
0
}
427
428
429
struct heif_error aom_set_parameter_quality(void* encoder_raw, int quality)
430
0
{
431
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
432
433
0
  if (quality < 0 || quality > 100) {
434
0
    return heif_error_invalid_parameter_value;
435
0
  }
436
437
0
  encoder->quality = quality;
438
439
0
  return heif_error_ok;
440
0
}
441
442
struct heif_error aom_get_parameter_quality(void* encoder_raw, int* quality)
443
0
{
444
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
445
446
0
  *quality = encoder->quality;
447
448
0
  return heif_error_ok;
449
0
}
450
451
struct heif_error aom_set_parameter_lossless(void* encoder_raw, int enable)
452
0
{
453
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
454
455
0
  if (enable) {
456
0
    encoder->min_q = 0;
457
0
    encoder->max_q = 0;
458
0
    encoder->alpha_min_q = 0;
459
0
    encoder->alpha_min_q_set = true;
460
0
    encoder->alpha_max_q = 0;
461
0
    encoder->alpha_max_q_set = true;
462
0
  }
463
464
0
  encoder->lossless = enable;
465
466
0
  return heif_error_ok;
467
0
}
468
469
struct heif_error aom_get_parameter_lossless(void* encoder_raw, int* enable)
470
0
{
471
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
472
473
0
  *enable = encoder->lossless;
474
475
0
  return heif_error_ok;
476
0
}
477
478
struct heif_error aom_set_parameter_logging_level(void* encoder_raw, int logging)
479
0
{
480
#if 0
481
  struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
482
483
  if (logging<0 || logging>4) {
484
    return heif_error_invalid_parameter_value;
485
  }
486
487
  encoder->logLevel = logging;
488
#endif
489
490
0
  return heif_error_ok;
491
0
}
492
493
struct heif_error aom_get_parameter_logging_level(void* encoder_raw, int* loglevel)
494
0
{
495
#if 0
496
  struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
497
498
  *loglevel = encoder->logLevel;
499
#else
500
0
  *loglevel = 0;
501
0
#endif
502
503
0
  return heif_error_ok;
504
0
}
505
506
0
#define set_value(paramname, paramvar) if (strcmp(name, paramname)==0) { encoder->paramvar = value; return heif_error_ok; }
507
0
#define get_value(paramname, paramvar) if (strcmp(name, paramname)==0) { *value = encoder->paramvar; return heif_error_ok; }
508
509
510
struct heif_error aom_set_parameter_integer(void* encoder_raw, const char* name, int value)
511
0
{
512
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
513
514
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
515
0
    return aom_set_parameter_quality(encoder, value);
516
0
  }
517
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
518
0
    return aom_set_parameter_lossless(encoder, value);
519
0
  }
520
0
  else if (strcmp(name, kParam_alpha_quality) == 0) {
521
0
      if (value < 0 || value > 100) {
522
0
          return heif_error_invalid_parameter_value;
523
0
      }
524
525
0
      encoder->alpha_quality = value;
526
0
      encoder->alpha_quality_set = true;
527
0
      return heif_error_ok;
528
0
  }
529
0
  else if (strcmp(name, kParam_alpha_min_q) == 0) {
530
0
      encoder->alpha_min_q = value;
531
0
      encoder->alpha_min_q_set = true;
532
0
      return heif_error_ok;
533
0
  }
534
0
  else if (strcmp(name, kParam_alpha_max_q) == 0) {
535
0
      encoder->alpha_max_q = value;
536
0
      encoder->alpha_max_q_set = true;
537
0
      return heif_error_ok;
538
0
  }
539
540
0
  set_value(kParam_min_q, min_q);
541
0
  set_value(kParam_max_q, max_q);
542
0
  set_value(kParam_threads, threads);
543
0
  set_value(kParam_speed, cpu_used);
544
545
0
  return heif_error_unsupported_parameter;
546
0
}
547
548
struct heif_error aom_get_parameter_integer(void* encoder_raw, const char* name, int* value)
549
0
{
550
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
551
552
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
553
0
    return aom_get_parameter_quality(encoder, value);
554
0
  }
555
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
556
0
    return aom_get_parameter_lossless(encoder, value);
557
0
  }
558
0
  else if (strcmp(name, kParam_alpha_quality) == 0) {
559
0
      *value = encoder->alpha_quality_set ? encoder->alpha_quality : encoder->quality;
560
0
      return heif_error_ok;
561
0
  }
562
0
  else if (strcmp(name, kParam_alpha_max_q) == 0) {
563
0
      *value = encoder->alpha_max_q_set ? encoder->alpha_max_q : encoder->max_q;
564
0
      return heif_error_ok;
565
0
  }
566
0
  else if (strcmp(name, kParam_alpha_min_q) == 0) {
567
0
      *value = encoder->alpha_min_q_set ? encoder->alpha_min_q : encoder->min_q;
568
0
      return heif_error_ok;
569
0
  }
570
571
0
  get_value(kParam_min_q, min_q);
572
0
  get_value(kParam_max_q, max_q);
573
0
  get_value(kParam_threads, threads);
574
0
  get_value(kParam_speed, cpu_used);
575
576
0
  return heif_error_unsupported_parameter;
577
0
}
578
579
580
struct heif_error aom_set_parameter_boolean(void* encoder_raw, const char* name, int value)
581
0
{
582
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
583
584
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
585
0
    return aom_set_parameter_lossless(encoder, value);
586
0
  }
587
0
  else if (strcmp(name, kParam_lossless_alpha) == 0) {
588
0
      encoder->lossless_alpha = value;
589
0
      if (value) {
590
0
          encoder->alpha_max_q = 0;
591
0
          encoder->alpha_max_q_set = true;
592
0
          encoder->alpha_min_q = 0;
593
0
          encoder->alpha_min_q_set = true;
594
0
      }
595
0
      return heif_error_ok;
596
0
  } else if (strcmp(name, kParam_auto_tiles) == 0) {
597
0
      encoder->auto_tiles = value;
598
0
      return heif_error_ok;
599
0
  } else if (strcmp(name, kParam_enable_intra_block_copy) == 0) {
600
0
      encoder->enable_intra_block_copy = value;
601
0
      return heif_error_ok;
602
0
  }
603
604
0
  set_value(kParam_realtime, realtime_mode);
605
606
0
  return heif_error_unsupported_parameter;
607
0
}
608
609
struct heif_error aom_get_parameter_boolean(void* encoder_raw, const char* name, int* value)
610
0
{
611
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
612
613
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
614
0
    return aom_get_parameter_lossless(encoder, value);
615
0
  }
616
617
0
  get_value(kParam_realtime, realtime_mode);
618
0
  get_value(kParam_lossless_alpha, lossless_alpha);
619
0
  get_value(kParam_auto_tiles, auto_tiles);
620
0
  get_value(kParam_enable_intra_block_copy, enable_intra_block_copy);
621
622
0
  return heif_error_unsupported_parameter;
623
0
}
624
625
626
struct heif_error aom_set_parameter_string(void* encoder_raw, const char* name, const char* value)
627
0
{
628
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
629
630
0
  if (strcmp(name, kParam_chroma) == 0) {
631
0
    if (strcmp(value, "420") == 0) {
632
0
      encoder->chroma = heif_chroma_420;
633
0
      return heif_error_ok;
634
0
    }
635
0
    else if (strcmp(value, "422") == 0) {
636
0
      encoder->chroma = heif_chroma_422;
637
0
      return heif_error_ok;
638
0
    }
639
0
    else if (strcmp(value, "444") == 0) {
640
0
      encoder->chroma = heif_chroma_444;
641
0
      return heif_error_ok;
642
0
    }
643
0
    else {
644
0
      return heif_error_invalid_parameter_value;
645
0
    }
646
0
  }
647
648
0
  if (strcmp(name, kParam_tune) == 0) {
649
0
    if (strcmp(value, "psnr") == 0) {
650
0
      encoder->tune = AOM_TUNE_PSNR;
651
0
      return heif_error_ok;
652
0
    }
653
0
    else if (strcmp(value, "ssim") == 0) {
654
0
      encoder->tune = AOM_TUNE_SSIM;
655
0
      return heif_error_ok;
656
0
    }
657
0
#if defined(AOM_HAVE_TUNE_IQ)
658
0
    else if (strcmp(value, "iq") == 0) {
659
0
      encoder->tune = AOM_TUNE_IQ;
660
0
      return heif_error_ok;
661
0
    }
662
0
#endif
663
0
    else {
664
0
      return heif_error_invalid_parameter_value;
665
0
    }
666
0
  }
667
668
0
#if defined(HAVE_AOM_CODEC_SET_OPTION)
669
0
  if (strncmp(name, "aom:", 4) == 0) {
670
0
    encoder->add_custom_option(std::string(name).substr(4), std::string(value));
671
0
    return heif_error_ok;
672
0
  }
673
0
#endif
674
675
0
  return heif_error_unsupported_parameter;
676
0
}
677
678
679
static void save_strcpy(char* dst, int dst_size, const char* src)
680
0
{
681
0
  strncpy(dst, src, dst_size - 1);
682
0
  dst[dst_size - 1] = 0;
683
0
}
684
685
686
struct heif_error aom_get_parameter_string(void* encoder_raw, const char* name,
687
                                           char* value, int value_size)
688
0
{
689
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
690
691
0
  if (strcmp(name, kParam_chroma) == 0) {
692
0
    switch (encoder->chroma) {
693
0
      case heif_chroma_420:
694
0
        save_strcpy(value, value_size, "420");
695
0
        break;
696
0
      case heif_chroma_422:
697
0
        save_strcpy(value, value_size, "422");
698
0
        break;
699
0
      case heif_chroma_444:
700
0
        save_strcpy(value, value_size, "444");
701
0
        break;
702
0
      default:
703
0
        assert(false);
704
0
        return heif_error_invalid_parameter_value;
705
0
    }
706
0
    return heif_error_ok;
707
0
  }
708
0
  else if (strcmp(name, kParam_tune) == 0) {
709
0
    switch (encoder->tune) {
710
0
      case AOM_TUNE_PSNR:
711
0
        save_strcpy(value, value_size, "psnr");
712
0
        break;
713
0
      case AOM_TUNE_SSIM:
714
0
        save_strcpy(value, value_size, "ssim");
715
0
        break;
716
0
#if defined(AOM_HAVE_TUNE_IQ)
717
0
      case AOM_TUNE_IQ:
718
0
        save_strcpy(value, value_size, "iq");
719
0
        break;
720
0
#endif
721
0
      default:
722
0
        assert(false);
723
0
        return heif_error_invalid_parameter_value;
724
0
    }
725
0
    return heif_error_ok;
726
0
  }
727
728
0
  return heif_error_unsupported_parameter;
729
0
}
730
731
732
static void aom_set_default_parameters(void* encoder)
733
0
{
734
0
  for (const struct heif_encoder_parameter** p = aom_encoder_parameter_ptrs; *p; p++) {
735
0
    const struct heif_encoder_parameter* param = *p;
736
737
0
    if (param->has_default) {
738
0
      switch (param->type) {
739
0
        case heif_encoder_parameter_type_integer:
740
0
          aom_set_parameter_integer(encoder, param->name, param->integer.default_value);
741
0
          break;
742
0
        case heif_encoder_parameter_type_boolean:
743
0
          aom_set_parameter_boolean(encoder, param->name, param->boolean.default_value);
744
0
          break;
745
0
        case heif_encoder_parameter_type_string:
746
0
          aom_set_parameter_string(encoder, param->name, param->string.default_value);
747
0
          break;
748
0
      }
749
0
    }
750
0
  }
751
0
}
752
753
754
void aom_query_input_colorspace(heif_colorspace* colorspace, heif_chroma* chroma)
755
0
{
756
0
  *colorspace = heif_colorspace_YCbCr;
757
0
  *chroma = heif_chroma_420;
758
0
}
759
760
761
void aom_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace, heif_chroma* chroma)
762
0
{
763
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
764
765
0
  if (*colorspace == heif_colorspace_monochrome) {
766
    // keep the monochrome colorspace
767
0
  }
768
0
  else {
769
0
    *colorspace = heif_colorspace_YCbCr;
770
0
    *chroma = encoder->chroma;
771
0
  }
772
0
}
773
774
// returns 'true' when an error was detected
775
// Note: some older AOM versions take a non-const pointer to aom_codec_error(). Thus, we also have to use a non-const pointer here.
776
static bool check_aom_error(aom_codec_err_t aom_error, /*const*/ aom_codec_ctx_t* codec, encoder_struct_aom* encoder, struct heif_error* heif_error)
777
0
{
778
0
  if (aom_error == AOM_CODEC_OK) {
779
0
    return false;
780
0
  }
781
782
0
  std::stringstream sstr;
783
0
  sstr << "AOM encoder error: " << aom_codec_error(codec) << " - " << aom_codec_error_detail(codec);
784
785
0
  heif_error->code = heif_error_Encoder_plugin_error;
786
0
  heif_error->subcode = heif_suberror_Unsupported_parameter;
787
0
  heif_error->message = encoder->set_aom_error(sstr.str().c_str());
788
789
0
  return true;
790
0
}
791
792
0
#define CHECK_ERROR \
793
0
if (check_aom_error(aom_error, &codec, encoder, &err)) { \
794
0
  return err; \
795
0
}
796
797
798
struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* image,
799
                                   heif_image_input_class input_class)
800
0
{
801
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
802
803
0
  struct heif_error err;
804
805
0
  const int source_width = heif_image_get_width(image, heif_channel_Y);
806
0
  const int source_height = heif_image_get_height(image, heif_channel_Y);
807
808
0
  const heif_chroma chroma = heif_image_get_chroma_format(image);
809
810
0
  int bpp_y = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
811
812
813
  // --- check for AOM 3.6.0 bug
814
815
0
  bool is_aom_3_6_0 = (aom_codec_version() == 0x030600);
816
817
0
  if (is_aom_3_6_0) {
818
    // This bound might be too tight, as I still could encode images with 8193 x 4353 correctly. Even 8200x4400, but 8200x4800 fails.
819
    // Let's still keep it as most images will be smaller anyway.
820
0
    if (!(source_width <= 8192 * 2 && source_height <= 4352 * 2 && source_width * source_height <= 8192 * 4352)) {
821
0
      err = {heif_error_Encoding_error,
822
0
             heif_suberror_Encoder_encoding,
823
0
             "AOM v3.6.0 has a bug when encoding large images. Please upgrade to at least AOM v3.6.1."};
824
0
      return err;
825
0
    }
826
0
  }
827
828
829
  // --- copy libheif image to aom image
830
831
0
  aom_img_fmt_t img_format = AOM_IMG_FMT_NONE;
832
833
0
  int chroma_height = 0;
834
0
  int chroma_sample_position = AOM_CSP_UNKNOWN;
835
836
0
  switch (chroma) {
837
0
    case heif_chroma_420:
838
0
    case heif_chroma_monochrome:
839
0
      img_format = AOM_IMG_FMT_I420;
840
0
      chroma_height = (source_height+1)/2;
841
0
      chroma_sample_position = AOM_CSP_UNKNOWN; // TODO: change this to CSP_CENTER in the future (https://github.com/AOMediaCodec/av1-avif/issues/88)
842
0
      break;
843
0
    case heif_chroma_422:
844
0
      img_format = AOM_IMG_FMT_I422;
845
0
      chroma_height = (source_height+1)/2;
846
0
      chroma_sample_position = AOM_CSP_COLOCATED;
847
0
      break;
848
0
    case heif_chroma_444:
849
0
      img_format = AOM_IMG_FMT_I444;
850
0
      chroma_height = source_height;
851
0
      chroma_sample_position = AOM_CSP_COLOCATED;
852
0
      break;
853
0
    default:
854
0
      img_format = AOM_IMG_FMT_NONE;
855
0
      chroma_sample_position = AOM_CSP_UNKNOWN;
856
0
      assert(false);
857
0
      break;
858
0
  }
859
860
0
  if (bpp_y > 8) {
861
0
    img_format = (aom_img_fmt_t) (img_format | AOM_IMG_FMT_HIGHBITDEPTH);
862
0
  }
863
864
0
  std::unique_ptr<aom_image_t, void (*)(aom_image_t*)> input_image(aom_img_alloc(nullptr, img_format,
865
0
                                                                                 source_width, source_height, 1),
866
0
                                                                   aom_img_free);
867
0
  if (!input_image) {
868
0
    err = {heif_error_Memory_allocation_error,
869
0
           heif_suberror_Unspecified,
870
0
           "Failed to allocate image"};
871
0
    return err;
872
0
  }
873
874
875
0
  for (int plane = 0; plane < 3; plane++) {
876
0
    unsigned char* buf = input_image->planes[plane];
877
0
    const int stride = input_image->stride[plane];
878
879
0
    if (chroma == heif_chroma_monochrome && plane != 0) {
880
0
      if (bpp_y == 8) {
881
0
        memset(buf, 128, chroma_height * stride);
882
0
      }
883
0
      else {
884
0
        uint16_t* buf16 = (uint16_t*) buf;
885
0
        uint16_t half_range = (uint16_t) (1 << (bpp_y - 1));
886
0
        for (int i = 0; i < chroma_height * stride / 2; i++) {
887
0
          buf16[i] = half_range;
888
0
        }
889
0
      }
890
891
0
      continue;
892
0
    }
893
894
    /*
895
    const int w = aom_img_plane_width(img, plane) *
896
                  ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
897
    const int h = aom_img_plane_height(img, plane);
898
    */
899
900
0
    size_t in_stride = 0;
901
0
    const uint8_t* in_p = heif_image_get_plane_readonly2(image, (heif_channel) plane, &in_stride);
902
903
0
    int w = source_width;
904
0
    int h = source_height;
905
906
0
    if (plane != 0) {
907
0
      if (chroma != heif_chroma_444) { w = (w + 1) / 2; }
908
0
      if (chroma == heif_chroma_420) { h = (h + 1) / 2; }
909
910
0
      assert(w == heif_image_get_width(image, (heif_channel) plane));
911
0
      assert(h == heif_image_get_height(image, (heif_channel) plane));
912
0
    }
913
914
0
    if (bpp_y > 8) {
915
0
      w *= 2;
916
0
    }
917
918
0
    for (int y = 0; y < h; y++) {
919
0
      memcpy(buf, &in_p[y * in_stride], w);
920
0
      buf += stride;
921
0
    }
922
0
  }
923
924
925
926
  // --- configure codec
927
928
0
  aom_codec_iface_t* iface;
929
0
  aom_codec_ctx_t codec;
930
931
0
  iface = aom_codec_av1_cx();
932
  //encoder->encoder = get_aom_encoder_by_name("av1");
933
0
  if (!iface) {
934
0
    err = {heif_error_Unsupported_feature,
935
0
           heif_suberror_Unsupported_codec,
936
0
           "Unsupported codec: AOMedia Project AV1 Encoder"};
937
0
    return err;
938
0
  }
939
940
941
0
#if defined(AOM_USAGE_ALL_INTRA)
942
  // aom 3.1.0
943
0
  unsigned int aomUsage = AOM_USAGE_ALL_INTRA;
944
#else
945
  // aom 2.0
946
  unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY;
947
#endif
948
0
  if (encoder->realtime_mode) {
949
0
    aomUsage = AOM_USAGE_REALTIME;
950
0
  }
951
952
0
  aom_codec_enc_cfg_t cfg;
953
0
  aom_codec_err_t res = aom_codec_enc_config_default(iface, &cfg, aomUsage);
954
0
  if (res) {
955
0
    err = {heif_error_Encoder_plugin_error,
956
0
           heif_suberror_Encoder_initialization,
957
0
           kError_codec_enc_config_default};
958
0
    return err;
959
0
  }
960
961
0
  int seq_profile = compute_avif_profile(heif_image_get_bits_per_pixel_range(image, heif_channel_Y),
962
0
                                         heif_image_get_chroma_format(image));
963
964
0
  cfg.g_w = source_width;
965
0
  cfg.g_h = source_height;
966
  // Set the max number of frames to encode to 1. This makes the libaom encoder
967
  // set still_picture and reduced_still_picture_header to 1 in the AV1 sequence
968
  // header OBU.
969
0
  cfg.g_limit = 1;
970
971
  // Use the default settings of the new AOM_USAGE_ALL_INTRA (added in
972
  // https://crbug.com/aomedia/2959).
973
  //
974
  // Set g_lag_in_frames to 0 to reduce the number of frame buffers (from 20
975
  // to 2) in libaom's lookahead structure. This reduces memory consumption when
976
  // encoding a single image.
977
0
  cfg.g_lag_in_frames = 0;
978
  // Disable automatic placement of key frames by the encoder.
979
0
  cfg.kf_mode = AOM_KF_DISABLED;
980
  // Tell libaom that all frames will be key frames.
981
0
  cfg.kf_max_dist = 0;
982
983
0
  cfg.g_profile = seq_profile;
984
0
  cfg.g_bit_depth = (aom_bit_depth_t) bpp_y;
985
0
  cfg.g_input_bit_depth = bpp_y;
986
987
0
  cfg.rc_end_usage = AOM_Q;
988
989
0
  int min_q = encoder->min_q;
990
0
  int max_q = encoder->max_q;
991
992
0
  if (input_class == heif_image_input_class_alpha && encoder->alpha_min_q_set && encoder->alpha_max_q_set) {
993
0
      min_q = encoder->alpha_min_q;
994
0
      max_q = encoder->alpha_max_q;
995
0
  }
996
997
0
  int quality = encoder->quality;
998
999
0
  if (input_class == heif_image_input_class_alpha && encoder->alpha_quality_set) {
1000
0
      quality = encoder->alpha_quality;
1001
0
  }
1002
1003
0
  int cq_level = ((100 - quality) * 63 + 50) / 100;
1004
1005
  // Work around the bug in libaom v2.0.2 or older fixed by
1006
  // https://aomedia-review.googlesource.com/c/aom/+/113064. If using a libaom
1007
  // release with the bug, set cfg.rc_min_quantizer to cq_level to prevent
1008
  // libaom from incorrectly using a quantizer index lower than cq_level.
1009
0
  bool aom_2_0_2_or_older = aom_codec_version() <= 0x020002;
1010
1011
0
  cfg.rc_min_quantizer = aom_2_0_2_or_older ? cq_level : min_q;
1012
0
  cfg.rc_max_quantizer = max_q;
1013
0
  cfg.g_error_resilient = 0;
1014
0
  cfg.g_threads = encoder->threads;
1015
1016
0
  if (chroma == heif_chroma_monochrome) {
1017
0
    cfg.monochrome = 1;
1018
0
  }
1019
1020
  // --- initialize codec
1021
1022
0
  aom_codec_flags_t encoder_flags = 0;
1023
0
  if (bpp_y > 8) {
1024
0
    encoder_flags = (aom_codec_flags_t) (encoder_flags | AOM_CODEC_USE_HIGHBITDEPTH);
1025
0
  }
1026
1027
  // allocate aom_codec_ctx_t
1028
0
  if (aom_codec_enc_init(&codec, iface, &cfg, encoder_flags)) {
1029
    // AOM makes sure that the error text returned by aom_codec_error_detail() is always a static
1030
    // text that is valid even though the codec allocation failed (#788).
1031
0
    err = {heif_error_Encoder_plugin_error,
1032
0
           heif_suberror_Encoder_initialization,
1033
0
           encoder->set_aom_error(aom_codec_error_detail(&codec))};
1034
0
    return err;
1035
0
  }
1036
1037
  // automatically destroy aom_codec_ctx_t when we leave the function
1038
0
  auto codec_ctx_deleter = std::unique_ptr<aom_codec_ctx_t, aom_codec_err_t (*)(aom_codec_ctx_t*)>(&codec, aom_codec_destroy);
1039
1040
0
  aom_codec_err_t aom_error;
1041
1042
0
  aom_error = aom_codec_control(&codec, AOME_SET_CPUUSED, encoder->cpu_used); CHECK_ERROR;
1043
1044
0
  aom_error = aom_codec_control(&codec, AOME_SET_CQ_LEVEL, cq_level); CHECK_ERROR;
1045
1046
0
  if (encoder->threads > 1) {
1047
0
#if defined(AOM_CTRL_AV1E_SET_ROW_MT)
1048
    // aom 2.0
1049
0
    aom_error = aom_codec_control(&codec, AV1E_SET_ROW_MT, 1); CHECK_ERROR;
1050
0
#endif
1051
0
  }
1052
1053
0
#if defined(AOM_CTRL_AV1E_SET_AUTO_TILES)
1054
  // aom 3.10.0
1055
0
  aom_error = aom_codec_control(&codec, AV1E_SET_AUTO_TILES, encoder->auto_tiles); CHECK_ERROR;
1056
0
#endif
1057
1058
  // TODO: set AV1E_SET_TILE_ROWS and AV1E_SET_TILE_COLUMNS.
1059
1060
1061
0
  struct heif_color_profile_nclx* nclx = nullptr;
1062
0
  err = heif_image_get_nclx_color_profile(image, &nclx);
1063
0
  if (err.code != heif_error_Ok) {
1064
0
    assert(nclx == nullptr);
1065
0
  }
1066
1067
  // make sure NCLX profile is deleted at end of function
1068
0
  auto nclx_deleter = std::unique_ptr<heif_color_profile_nclx, void (*)(heif_color_profile_nclx*)>(nclx, heif_nclx_color_profile_free);
1069
1070
  // In aom, color_range defaults to limited range (0). Set it to full range (1).
1071
0
  aom_error = aom_codec_control(&codec, AV1E_SET_COLOR_RANGE, nclx ? nclx->full_range_flag : 1); CHECK_ERROR;
1072
0
  aom_error = aom_codec_control(&codec, AV1E_SET_CHROMA_SAMPLE_POSITION, chroma_sample_position); CHECK_ERROR;
1073
1074
0
  if (nclx &&
1075
0
      (input_class == heif_image_input_class_normal ||
1076
0
       input_class == heif_image_input_class_thumbnail)) {
1077
0
    aom_error = aom_codec_control(&codec, AV1E_SET_COLOR_PRIMARIES, nclx->color_primaries); CHECK_ERROR
1078
0
    aom_error = aom_codec_control(&codec, AV1E_SET_MATRIX_COEFFICIENTS, nclx->matrix_coefficients); CHECK_ERROR;
1079
0
    aom_error = aom_codec_control(&codec, AV1E_SET_TRANSFER_CHARACTERISTICS, nclx->transfer_characteristics); CHECK_ERROR;
1080
0
  }
1081
1082
0
  aom_error = aom_codec_control(&codec, AOME_SET_TUNING, encoder->tune); CHECK_ERROR;
1083
1084
0
  if (encoder->lossless || (input_class == heif_image_input_class_alpha && encoder->lossless_alpha)) {
1085
0
    aom_error = aom_codec_control(&codec, AV1E_SET_LOSSLESS, 1); CHECK_ERROR;
1086
0
  }
1087
1088
0
#if defined(AOM_CTRL_AV1E_SET_SKIP_POSTPROC_FILTERING)
1089
0
  if (cfg.g_usage == AOM_USAGE_ALL_INTRA) {
1090
    // Enable AV1E_SET_SKIP_POSTPROC_FILTERING for still-picture encoding,
1091
    // which is disabled by default.
1092
0
    aom_error = aom_codec_control(&codec, AV1E_SET_SKIP_POSTPROC_FILTERING, 1); CHECK_ERROR;
1093
0
  }
1094
0
#endif
1095
1096
0
  aom_error = aom_codec_control(&codec, AV1E_SET_ENABLE_INTRABC, encoder->enable_intra_block_copy); CHECK_ERROR;
1097
1098
0
#if defined(HAVE_AOM_CODEC_SET_OPTION)
1099
  // Apply the custom AOM encoder options.
1100
  // These should always be applied last as they can override the values that were set above.
1101
0
  for (const auto& p : encoder->custom_options) {
1102
0
    if (aom_codec_set_option(&codec, p.name.c_str(), p.value.c_str()) != AOM_CODEC_OK) {
1103
0
      std::stringstream sstr;
1104
0
      sstr << "Cannot set AOM encoder option (name: " << p.name << ", value: " << p.value << "): "
1105
0
           << aom_codec_error(&codec) << " - " << aom_codec_error_detail(&codec);
1106
1107
0
      err = {
1108
0
        heif_error_Encoder_plugin_error,
1109
0
        heif_suberror_Unsupported_parameter,
1110
0
        encoder->set_aom_error(sstr.str().c_str())
1111
0
      };
1112
0
      return err;
1113
0
    }
1114
0
  }
1115
0
#endif
1116
1117
  // --- encode frame
1118
1119
0
  res = aom_codec_encode(&codec, input_image.get(),
1120
0
                         0, // only encoding a single frame
1121
0
                         1,
1122
0
                         0); // no flags
1123
1124
0
  if (res != AOM_CODEC_OK) {
1125
0
    err = {
1126
0
        heif_error_Encoder_plugin_error,
1127
0
        heif_suberror_Encoder_encoding,
1128
0
        encoder->set_aom_error(aom_codec_error_detail(&codec))
1129
0
    };
1130
0
    return err;
1131
0
  }
1132
1133
0
  encoder->compressedData.clear();
1134
0
  const aom_codec_cx_pkt_t* pkt = NULL;
1135
0
  aom_codec_iter_t iter = NULL; // for extracting the compressed packets
1136
1137
0
  while ((pkt = aom_codec_get_cx_data(&codec, &iter)) != NULL) {
1138
1139
0
    if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
1140
      //std::cerr.write((char*)pkt->data.frame.buf, pkt->data.frame.sz);
1141
1142
      //printf("packet of size: %d\n",(int)pkt->data.frame.sz);
1143
1144
1145
      // TODO: split the received data into separate OBUs
1146
      // This allows libheif to easily extract the sequence header for the av1C header
1147
1148
0
      size_t n = pkt->data.frame.sz;
1149
0
      size_t oldSize = encoder->compressedData.size();
1150
0
      encoder->compressedData.resize(oldSize + n);
1151
1152
0
      memcpy(encoder->compressedData.data() + oldSize,
1153
0
             pkt->data.frame.buf,
1154
0
             n);
1155
1156
0
      encoder->data_read = false;
1157
0
    }
1158
0
  }
1159
1160
0
  int flags = 0;
1161
0
  res = aom_codec_encode(&codec, NULL, -1, 0, flags);
1162
0
  if (res != AOM_CODEC_OK) {
1163
0
    err = {heif_error_Encoder_plugin_error,
1164
0
           heif_suberror_Encoder_encoding,
1165
0
           encoder->set_aom_error(aom_codec_error_detail(&codec))};
1166
0
    return err;
1167
0
  }
1168
1169
0
  iter = NULL;
1170
1171
0
  while ((pkt = aom_codec_get_cx_data(&codec, &iter)) != NULL) {
1172
1173
0
    if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
1174
      //std::cerr.write((char*)pkt->data.frame.buf, pkt->data.frame.sz);
1175
1176
      //printf("packet of size: %d\n",(int)pkt->data.frame.sz);
1177
1178
1179
      // TODO: split the received data into separate OBUs
1180
      // This allows libheif to easily extract the sequence header for the av1C header
1181
1182
0
      size_t n = pkt->data.frame.sz;
1183
0
      size_t oldSize = encoder->compressedData.size();
1184
0
      encoder->compressedData.resize(oldSize + n);
1185
1186
0
      memcpy(encoder->compressedData.data() + oldSize,
1187
0
             pkt->data.frame.buf,
1188
0
             n);
1189
1190
0
      encoder->data_read = false;
1191
0
    }
1192
0
  }
1193
1194
0
  return heif_error_ok;
1195
0
}
1196
1197
1198
struct heif_error aom_get_compressed_data(void* encoder_raw, uint8_t** data, int* size,
1199
                                          enum heif_encoded_data_type* type)
1200
0
{
1201
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
1202
1203
0
  if (encoder->data_read) {
1204
0
    *size = 0;
1205
0
    *data = nullptr;
1206
0
  }
1207
0
  else {
1208
0
    *size = (int) encoder->compressedData.size();
1209
0
    *data = encoder->compressedData.data();
1210
0
    encoder->data_read = true;
1211
0
  }
1212
1213
0
  return heif_error_ok;
1214
0
}
1215
1216
1217
static const struct heif_encoder_plugin encoder_plugin_aom
1218
    {
1219
        /* plugin_api_version */ 3,
1220
        /* compression_format */ heif_compression_AV1,
1221
        /* id_name */ "aom",
1222
        /* priority */ AOM_PLUGIN_PRIORITY,
1223
        /* supports_lossy_compression */ true,
1224
        /* supports_lossless_compression */ true,
1225
        /* get_plugin_name */ aom_plugin_name,
1226
        /* init_plugin */ aom_init_plugin,
1227
        /* cleanup_plugin */ aom_cleanup_plugin,
1228
        /* new_encoder */ aom_new_encoder,
1229
        /* free_encoder */ aom_free_encoder,
1230
        /* set_parameter_quality */ aom_set_parameter_quality,
1231
        /* get_parameter_quality */ aom_get_parameter_quality,
1232
        /* set_parameter_lossless */ aom_set_parameter_lossless,
1233
        /* get_parameter_lossless */ aom_get_parameter_lossless,
1234
        /* set_parameter_logging_level */ aom_set_parameter_logging_level,
1235
        /* get_parameter_logging_level */ aom_get_parameter_logging_level,
1236
        /* list_parameters */ aom_list_parameters,
1237
        /* set_parameter_integer */ aom_set_parameter_integer,
1238
        /* get_parameter_integer */ aom_get_parameter_integer,
1239
        /* set_parameter_boolean */ aom_set_parameter_boolean,
1240
        /* get_parameter_boolean */ aom_get_parameter_boolean,
1241
        /* set_parameter_string */ aom_set_parameter_string,
1242
        /* get_parameter_string */ aom_get_parameter_string,
1243
        /* query_input_colorspace */ aom_query_input_colorspace,
1244
        /* encode_image */ aom_encode_image,
1245
        /* get_compressed_data */ aom_get_compressed_data,
1246
        /* query_input_colorspace (v2) */ aom_query_input_colorspace2,
1247
        /* query_encoded_size (v3) */ nullptr
1248
    };
1249
1250
const struct heif_encoder_plugin* get_encoder_plugin_aom()
1251
19.1k
{
1252
19.1k
  return &encoder_plugin_aom;
1253
19.1k
}
1254
1255
1256
#if PLUGIN_AOM_ENCODER
1257
heif_plugin_info plugin_info {
1258
  1,
1259
  heif_plugin_type_encoder,
1260
  &encoder_plugin_aom
1261
};
1262
#endif