Coverage Report

Created: 2022-08-24 06:11

/src/libheif/libheif/heif_encoder_aom.cc
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 struktur AG, Dirk Farin <farin@struktur.de>
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 "heif.h"
22
#include "heif_plugin.h"
23
#include "heif_avif.h"
24
#include "heif_api_structs.h"
25
26
#if defined(HAVE_CONFIG_H)
27
#include "config.h"
28
#endif
29
30
#include <algorithm>
31
#include <cstring>
32
#include <cassert>
33
#include <vector>
34
35
#include <aom/aom_encoder.h>
36
#include <aom/aomcx.h>
37
38
// Detect whether the aom_codec_set_option() function is available.
39
// See https://aomedia.googlesource.com/aom/+/c1d42fe6615c96fc929257ed53c41fa094f38836%5E%21/aom/aom_codec.h.
40
#if AOM_CODEC_ABI_VERSION >= (6 + AOM_IMAGE_ABI_VERSION)
41
#define HAVE_AOM_CODEC_SET_OPTION 1
42
#endif
43
44
#if defined(HAVE_AOM_CODEC_SET_OPTION)
45
struct custom_option
46
{
47
    std::string name;
48
    std::string value;
49
};
50
#endif
51
52
struct encoder_struct_aom
53
{
54
  // --- parameters
55
56
  bool realtime_mode;
57
  int cpu_used;  // = parameter 'speed'. I guess this is a better name than 'cpu_used'.
58
59
  int quality;
60
  int alpha_quality;
61
  int min_q;
62
  int max_q;
63
  int alpha_min_q;
64
  int alpha_max_q;
65
  int threads;
66
  bool lossless;
67
  bool lossless_alpha;
68
69
#if defined(HAVE_AOM_CODEC_SET_OPTION)
70
  std::vector<custom_option> custom_options;
71
72
  void add_custom_option(const custom_option&);
73
74
  void add_custom_option(std::string name, std::string value);
75
#endif
76
77
  aom_tune_metric tune;
78
79
  heif_chroma chroma = heif_chroma_420;
80
81
  // --- input
82
83
  bool alpha_quality_set = false;
84
  bool alpha_min_q_set = false;
85
  bool alpha_max_q_set = false;
86
87
  // --- output
88
89
  std::vector<uint8_t> compressedData;
90
  bool data_read = false;
91
};
92
93
#if defined(HAVE_AOM_CODEC_SET_OPTION)
94
void encoder_struct_aom::add_custom_option(const custom_option& p)
95
0
{
96
  // if there is already a parameter of that name, remove it from list
97
98
0
  for (size_t i = 0; i < custom_options.size(); i++) {
99
0
    if (custom_options[i].name == p.name) {
100
0
      for (size_t k = i + 1; k < custom_options.size(); k++) {
101
0
        custom_options[k - 1] = custom_options[k];
102
0
      }
103
0
      custom_options.pop_back();
104
0
      break;
105
0
    }
106
0
  }
107
108
  // and add the new parameter at the end of the list
109
110
0
  custom_options.push_back(p);
111
0
}
112
113
void encoder_struct_aom::add_custom_option(std::string name, std::string value)
114
0
{
115
0
    custom_option p;
116
0
    p.name = name;
117
0
    p.value = value;
118
0
    add_custom_option(p);
119
0
}
120
#endif
121
122
static const char* kError_out_of_memory = "Out of memory";
123
static const char* kError_encode_frame = "Failed to encode frame";
124
125
static const char* kParam_min_q = "min-q";
126
static const char* kParam_max_q = "max-q";
127
static const char* kParam_alpha_quality = "alpha-quality";
128
static const char* kParam_alpha_min_q = "alpha-min-q";
129
static const char* kParam_alpha_max_q = "alpha-max-q";
130
static const char* kParam_lossless_alpha = "lossless-alpha";
131
static const char* kParam_threads = "threads";
132
static const char* kParam_realtime = "realtime";
133
static const char* kParam_speed = "speed";
134
135
static const char* kParam_chroma = "chroma";
136
static const char* const kParam_chroma_valid_values[] = {
137
    "420", "422", "444", nullptr
138
};
139
140
static const char* kParam_tune = "tune";
141
static const char* const kParam_tune_valid_values[] = {
142
    "psnr", "ssim", nullptr
143
};
144
145
static const int AOM_PLUGIN_PRIORITY = 40;
146
147
0
#define MAX_PLUGIN_NAME_LENGTH 80
148
149
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
150
151
152
static void aom_set_default_parameters(void* encoder);
153
154
155
static const char* aom_plugin_name()
156
0
{
157
0
  if (strlen(aom_codec_iface_name(aom_codec_av1_cx())) < MAX_PLUGIN_NAME_LENGTH) {
158
0
    strcpy(plugin_name, aom_codec_iface_name(aom_codec_av1_cx()));
159
0
  }
160
0
  else {
161
0
    strcpy(plugin_name, "AOMedia AV1 encoder");
162
0
  }
163
164
0
  return plugin_name;
165
0
}
166
167
168
#define MAX_NPARAMETERS 14
169
170
static struct heif_encoder_parameter aom_encoder_params[MAX_NPARAMETERS];
171
static const struct heif_encoder_parameter* aom_encoder_parameter_ptrs[MAX_NPARAMETERS + 1];
172
173
static void aom_init_parameters()
174
2
{
175
2
  struct heif_encoder_parameter* p = aom_encoder_params;
176
2
  const struct heif_encoder_parameter** d = aom_encoder_parameter_ptrs;
177
2
  int i = 0;
178
179
2
  assert(i < MAX_NPARAMETERS);
180
0
  p->version = 2;
181
2
  p->name = kParam_realtime;
182
2
  p->type = heif_encoder_parameter_type_boolean;
183
2
  p->boolean.default_value = false;
184
2
  p->has_default = true;
185
2
  d[i++] = p++;
186
187
2
  assert(i < MAX_NPARAMETERS);
188
0
  p->version = 2;
189
2
  p->name = kParam_speed;
190
2
  p->type = heif_encoder_parameter_type_integer;
191
2
  p->integer.default_value = 5;
192
2
  p->has_default = true;
193
2
  p->integer.have_minimum_maximum = true;
194
2
  p->integer.minimum = 0;
195
196
2
  if (aom_codec_version_major() >= 3) {
197
2
    p->integer.maximum = 9;
198
2
  }
199
0
  else {
200
0
    p->integer.maximum = 8;
201
0
  }
202
2
  p->integer.valid_values = NULL;
203
2
  p->integer.num_valid_values = 0;
204
2
  d[i++] = p++;
205
206
2
  assert(i < MAX_NPARAMETERS);
207
0
  p->version = 2;
208
2
  p->name = kParam_threads;
209
2
  p->type = heif_encoder_parameter_type_integer;
210
2
  p->integer.default_value = 4;
211
2
  p->has_default = true;
212
2
  p->integer.have_minimum_maximum = true;
213
2
  p->integer.minimum = 1;
214
2
  p->integer.maximum = 16;
215
2
  p->integer.valid_values = NULL;
216
2
  p->integer.num_valid_values = 0;
217
2
  d[i++] = p++;
218
219
2
  assert(i < MAX_NPARAMETERS);
220
0
  p->version = 2;
221
2
  p->name = heif_encoder_parameter_name_quality;
222
2
  p->type = heif_encoder_parameter_type_integer;
223
2
  p->integer.default_value = 50;
224
2
  p->has_default = true;
225
2
  p->integer.have_minimum_maximum = true;
226
2
  p->integer.minimum = 0;
227
2
  p->integer.maximum = 100;
228
2
  p->integer.valid_values = NULL;
229
2
  p->integer.num_valid_values = 0;
230
2
  d[i++] = p++;
231
232
2
  assert(i < MAX_NPARAMETERS);
233
0
  p->version = 2;
234
2
  p->name = heif_encoder_parameter_name_lossless;
235
2
  p->type = heif_encoder_parameter_type_boolean;
236
2
  p->boolean.default_value = false;
237
2
  p->has_default = true;
238
2
  d[i++] = p++;
239
240
2
  assert(i < MAX_NPARAMETERS);
241
0
  p->version = 2;
242
2
  p->name = kParam_chroma;
243
2
  p->type = heif_encoder_parameter_type_string;
244
2
  p->string.default_value = "420";
245
2
  p->has_default = true;
246
2
  p->string.valid_values = kParam_chroma_valid_values;
247
2
  d[i++] = p++;
248
249
2
  assert(i < MAX_NPARAMETERS);
250
0
  p->version = 2;
251
2
  p->name = kParam_tune;
252
2
  p->type = heif_encoder_parameter_type_string;
253
2
  p->string.default_value = "ssim";
254
2
  p->has_default = true;
255
2
  p->string.valid_values = kParam_tune_valid_values;
256
2
  d[i++] = p++;
257
258
2
  assert(i < MAX_NPARAMETERS);
259
0
  p->version = 2;
260
2
  p->name = kParam_min_q;
261
2
  p->type = heif_encoder_parameter_type_integer;
262
2
  p->integer.default_value = 1;
263
2
  p->has_default = true;
264
2
  p->integer.have_minimum_maximum = true;
265
2
  p->integer.minimum = 1;
266
2
  p->integer.maximum = 62;
267
2
  p->integer.valid_values = NULL;
268
2
  p->integer.num_valid_values = 0;
269
2
  d[i++] = p++;
270
271
2
  assert(i < MAX_NPARAMETERS);
272
0
  p->version = 2;
273
2
  p->name = kParam_max_q;
274
2
  p->type = heif_encoder_parameter_type_integer;
275
2
  p->integer.default_value = 63;
276
2
  p->has_default = true;
277
2
  p->integer.have_minimum_maximum = true;
278
2
  p->integer.minimum = 0;
279
2
  p->integer.maximum = 63;
280
2
  p->integer.valid_values = NULL;
281
2
  p->integer.num_valid_values = 0;
282
2
  d[i++] = p++;
283
284
2
  assert(i < MAX_NPARAMETERS);
285
0
  p->version = 2;
286
2
  p->name = kParam_alpha_quality;
287
2
  p->type = heif_encoder_parameter_type_integer;
288
2
  p->has_default = false;
289
2
  p->integer.have_minimum_maximum = true;
290
2
  p->integer.minimum = 0;
291
2
  p->integer.maximum = 100;
292
2
  p->integer.valid_values = NULL;
293
2
  p->integer.num_valid_values = 0;
294
2
  d[i++] = p++;
295
296
2
  assert(i < MAX_NPARAMETERS);
297
0
  p->version = 2;
298
2
  p->name = kParam_alpha_min_q;
299
2
  p->type = heif_encoder_parameter_type_integer;
300
2
  p->has_default = false;
301
2
  p->integer.have_minimum_maximum = true;
302
2
  p->integer.minimum = 1;
303
2
  p->integer.maximum = 62;
304
2
  p->integer.valid_values = NULL;
305
2
  p->integer.num_valid_values = 0;
306
2
  d[i++] = p++;
307
308
2
  assert(i < MAX_NPARAMETERS);
309
0
  p->version = 2;
310
2
  p->name = kParam_alpha_max_q;
311
2
  p->type = heif_encoder_parameter_type_integer;
312
2
  p->has_default = false;
313
2
  p->integer.have_minimum_maximum = true;
314
2
  p->integer.minimum = 0;
315
2
  p->integer.maximum = 63;
316
2
  p->integer.valid_values = NULL;
317
2
  p->integer.num_valid_values = 0;
318
2
  d[i++] = p++;
319
320
2
  assert(i < MAX_NPARAMETERS);
321
0
  p->version = 2;
322
2
  p->name = kParam_lossless_alpha;
323
2
  p->type = heif_encoder_parameter_type_boolean;
324
2
  p->boolean.default_value = false;
325
2
  p->has_default = true;
326
2
  d[i++] = p++;
327
328
2
  d[i++] = nullptr;
329
2
}
330
331
332
const struct heif_encoder_parameter** aom_list_parameters(void* encoder)
333
0
{
334
0
  return aom_encoder_parameter_ptrs;
335
0
}
336
337
static void aom_init_plugin()
338
2
{
339
2
  aom_init_parameters();
340
2
}
341
342
343
static void aom_cleanup_plugin()
344
0
{
345
0
}
346
347
struct heif_error aom_new_encoder(void** enc)
348
0
{
349
0
  struct encoder_struct_aom* encoder = new encoder_struct_aom();
350
0
  struct heif_error err = heif_error_ok;
351
352
0
  *enc = encoder;
353
354
  // set default parameters
355
356
0
  aom_set_default_parameters(encoder);
357
358
0
  return err;
359
0
}
360
361
void aom_free_encoder(void* encoder_raw)
362
0
{
363
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
364
365
0
  delete encoder;
366
0
}
367
368
369
struct heif_error aom_set_parameter_quality(void* encoder_raw, int quality)
370
0
{
371
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
372
373
0
  if (quality < 0 || quality > 100) {
374
0
    return heif_error_invalid_parameter_value;
375
0
  }
376
377
0
  encoder->quality = quality;
378
379
0
  return heif_error_ok;
380
0
}
381
382
struct heif_error aom_get_parameter_quality(void* encoder_raw, int* quality)
383
0
{
384
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
385
386
0
  *quality = encoder->quality;
387
388
0
  return heif_error_ok;
389
0
}
390
391
struct heif_error aom_set_parameter_lossless(void* encoder_raw, int enable)
392
0
{
393
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
394
395
0
  if (enable) {
396
0
    encoder->min_q = 0;
397
0
    encoder->max_q = 0;
398
0
    encoder->alpha_min_q = 0;
399
0
    encoder->alpha_min_q_set = true;
400
0
    encoder->alpha_max_q = 0;
401
0
    encoder->alpha_max_q_set = true;
402
0
  }
403
404
0
  encoder->lossless = enable;
405
406
0
  return heif_error_ok;
407
0
}
408
409
struct heif_error aom_get_parameter_lossless(void* encoder_raw, int* enable)
410
0
{
411
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
412
413
0
  *enable = encoder->lossless;
414
415
0
  return heif_error_ok;
416
0
}
417
418
struct heif_error aom_set_parameter_logging_level(void* encoder_raw, int logging)
419
0
{
420
#if 0
421
  struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
422
423
  if (logging<0 || logging>4) {
424
    return heif_error_invalid_parameter_value;
425
  }
426
427
  encoder->logLevel = logging;
428
#endif
429
430
0
  return heif_error_ok;
431
0
}
432
433
struct heif_error aom_get_parameter_logging_level(void* encoder_raw, int* loglevel)
434
0
{
435
#if 0
436
  struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
437
438
  *loglevel = encoder->logLevel;
439
#else
440
0
  *loglevel = 0;
441
0
#endif
442
443
0
  return heif_error_ok;
444
0
}
445
446
0
#define set_value(paramname, paramvar) if (strcmp(name, paramname)==0) { encoder->paramvar = value; return heif_error_ok; }
447
0
#define get_value(paramname, paramvar) if (strcmp(name, paramname)==0) { *value = encoder->paramvar; return heif_error_ok; }
448
449
450
struct heif_error aom_set_parameter_integer(void* encoder_raw, const char* name, int value)
451
0
{
452
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
453
454
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
455
0
    return aom_set_parameter_quality(encoder, value);
456
0
  }
457
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
458
0
    return aom_set_parameter_lossless(encoder, value);
459
0
  }
460
0
  else if (strcmp(name, kParam_alpha_quality) == 0) {
461
0
      if (value < 0 || value > 100) {
462
0
          return heif_error_invalid_parameter_value;
463
0
      }
464
465
0
      encoder->alpha_quality = value;
466
0
      encoder->alpha_quality_set = true;
467
0
      return heif_error_ok;
468
0
  }
469
0
  else if (strcmp(name, kParam_alpha_min_q) == 0) {
470
0
      encoder->alpha_min_q = value;
471
0
      encoder->alpha_min_q_set = true;
472
0
      return heif_error_ok;
473
0
  }
474
0
  else if (strcmp(name, kParam_alpha_max_q) == 0) {
475
0
      encoder->alpha_max_q = value;
476
0
      encoder->alpha_max_q_set = true;
477
0
      return heif_error_ok;
478
0
  }
479
480
0
  set_value(kParam_min_q, min_q);
481
0
  set_value(kParam_max_q, max_q);
482
0
  set_value(kParam_threads, threads);
483
0
  set_value(kParam_speed, cpu_used);
484
485
0
  return heif_error_unsupported_parameter;
486
0
}
487
488
struct heif_error aom_get_parameter_integer(void* encoder_raw, const char* name, int* value)
489
0
{
490
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
491
492
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
493
0
    return aom_get_parameter_quality(encoder, value);
494
0
  }
495
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
496
0
    return aom_get_parameter_lossless(encoder, value);
497
0
  }
498
0
  else if (strcmp(name, kParam_alpha_quality) == 0) {
499
0
      *value = encoder->alpha_quality_set ? encoder->alpha_quality : encoder->quality;
500
0
      return heif_error_ok;
501
0
  }
502
0
  else if (strcmp(name, kParam_alpha_max_q) == 0) {
503
0
      *value = encoder->alpha_max_q_set ? encoder->alpha_max_q : encoder->max_q;
504
0
      return heif_error_ok;
505
0
  }
506
0
  else if (strcmp(name, kParam_alpha_min_q) == 0) {
507
0
      *value = encoder->alpha_min_q_set ? encoder->alpha_min_q : encoder->min_q;
508
0
      return heif_error_ok;
509
0
  }
510
511
0
  get_value(kParam_min_q, min_q);
512
0
  get_value(kParam_max_q, max_q);
513
0
  get_value(kParam_threads, threads);
514
0
  get_value(kParam_speed, cpu_used);
515
516
0
  return heif_error_unsupported_parameter;
517
0
}
518
519
520
struct heif_error aom_set_parameter_boolean(void* encoder_raw, const char* name, int value)
521
0
{
522
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
523
524
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
525
0
    return aom_set_parameter_lossless(encoder, value);
526
0
  }
527
0
  else if (strcmp(name, kParam_lossless_alpha) == 0) {
528
0
      encoder->lossless_alpha = value;
529
0
      if (value) {
530
0
          encoder->alpha_max_q = 0;
531
0
          encoder->alpha_max_q_set = true;
532
0
          encoder->alpha_min_q = 0;
533
0
          encoder->alpha_min_q_set = true;
534
0
      }
535
0
      return heif_error_ok;
536
0
  }
537
538
0
  set_value(kParam_realtime, realtime_mode);
539
540
0
  return heif_error_unsupported_parameter;
541
0
}
542
543
struct heif_error aom_get_parameter_boolean(void* encoder_raw, const char* name, int* value)
544
0
{
545
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
546
547
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
548
0
    return aom_get_parameter_lossless(encoder, value);
549
0
  }
550
551
0
  get_value(kParam_realtime, realtime_mode);
552
0
  get_value(kParam_lossless_alpha, lossless_alpha);
553
554
0
  return heif_error_unsupported_parameter;
555
0
}
556
557
558
struct heif_error aom_set_parameter_string(void* encoder_raw, const char* name, const char* value)
559
0
{
560
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
561
562
0
  if (strcmp(name, kParam_chroma) == 0) {
563
0
    if (strcmp(value, "420") == 0) {
564
0
      encoder->chroma = heif_chroma_420;
565
0
      return heif_error_ok;
566
0
    }
567
0
    else if (strcmp(value, "422") == 0) {
568
0
      encoder->chroma = heif_chroma_422;
569
0
      return heif_error_ok;
570
0
    }
571
0
    else if (strcmp(value, "444") == 0) {
572
0
      encoder->chroma = heif_chroma_444;
573
0
      return heif_error_ok;
574
0
    }
575
0
    else {
576
0
      return heif_error_invalid_parameter_value;
577
0
    }
578
0
  }
579
580
0
  if (strcmp(name, kParam_tune) == 0) {
581
0
    if (strcmp(value, "psnr") == 0) {
582
0
      encoder->tune = AOM_TUNE_PSNR;
583
0
      return heif_error_ok;
584
0
    }
585
0
    else if (strcmp(value, "ssim") == 0) {
586
0
      encoder->tune = AOM_TUNE_SSIM;
587
0
      return heif_error_ok;
588
0
    }
589
0
    else {
590
0
      return heif_error_invalid_parameter_value;
591
0
    }
592
0
  }
593
594
0
#if defined(HAVE_AOM_CODEC_SET_OPTION)
595
0
  if (strncmp(name, "aom:", 4) == 0) {
596
0
    encoder->add_custom_option(std::string(name).substr(4), std::string(value));
597
0
    return heif_error_ok;
598
0
  }
599
0
#endif
600
601
0
  return heif_error_unsupported_parameter;
602
0
}
603
604
605
static void save_strcpy(char* dst, int dst_size, const char* src)
606
0
{
607
0
  strncpy(dst, src, dst_size - 1);
608
0
  dst[dst_size - 1] = 0;
609
0
}
610
611
612
struct heif_error aom_get_parameter_string(void* encoder_raw, const char* name,
613
                                           char* value, int value_size)
614
0
{
615
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
616
617
0
  if (strcmp(name, kParam_chroma) == 0) {
618
0
    switch (encoder->chroma) {
619
0
      case heif_chroma_420:
620
0
        save_strcpy(value, value_size, "420");
621
0
        break;
622
0
      case heif_chroma_422:
623
0
        save_strcpy(value, value_size, "422");
624
0
        break;
625
0
      case heif_chroma_444:
626
0
        save_strcpy(value, value_size, "444");
627
0
        break;
628
0
      default:
629
0
        assert(false);
630
0
        return heif_error_invalid_parameter_value;
631
0
    }
632
0
  }
633
0
  else if (strcmp(name, kParam_tune) == 0) {
634
0
    switch (encoder->tune) {
635
0
      case AOM_TUNE_PSNR:
636
0
        save_strcpy(value, value_size, "psnr");
637
0
        break;
638
0
      case AOM_TUNE_SSIM:
639
0
        save_strcpy(value, value_size, "ssim");
640
0
        break;
641
0
      default:
642
0
        assert(false);
643
0
        return heif_error_invalid_parameter_value;
644
0
    }
645
0
  }
646
647
0
  return heif_error_unsupported_parameter;
648
0
}
649
650
651
static void aom_set_default_parameters(void* encoder)
652
0
{
653
0
  for (const struct heif_encoder_parameter** p = aom_encoder_parameter_ptrs; *p; p++) {
654
0
    const struct heif_encoder_parameter* param = *p;
655
656
0
    if (param->has_default) {
657
0
      switch (param->type) {
658
0
        case heif_encoder_parameter_type_integer:
659
0
          aom_set_parameter_integer(encoder, param->name, param->integer.default_value);
660
0
          break;
661
0
        case heif_encoder_parameter_type_boolean:
662
0
          aom_set_parameter_boolean(encoder, param->name, param->boolean.default_value);
663
0
          break;
664
0
        case heif_encoder_parameter_type_string:
665
0
          aom_set_parameter_string(encoder, param->name, param->string.default_value);
666
0
          break;
667
0
      }
668
0
    }
669
0
  }
670
0
}
671
672
673
void aom_query_input_colorspace(heif_colorspace* colorspace, heif_chroma* chroma)
674
0
{
675
0
  *colorspace = heif_colorspace_YCbCr;
676
0
  *chroma = heif_chroma_420;
677
0
}
678
679
680
void aom_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace, heif_chroma* chroma)
681
0
{
682
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
683
684
0
  if (*colorspace == heif_colorspace_monochrome) {
685
    // keep the monochrome colorspace
686
0
  }
687
0
  else {
688
0
    *colorspace = heif_colorspace_YCbCr;
689
0
    *chroma = encoder->chroma;
690
0
  }
691
0
}
692
693
694
void aom_query_encoded_size(void* encoder, uint32_t input_width, uint32_t input_height,
695
                            uint32_t* encoded_width, uint32_t* encoded_height)
696
0
{
697
0
  *encoded_width = input_width;
698
0
  *encoded_height = input_height;
699
0
}
700
701
702
static heif_error encode_frame(aom_codec_ctx_t* codec, aom_image_t* img)
703
0
{
704
  //aom_codec_iter_t iter = NULL;
705
0
  int frame_index = 0; // only encoding a single frame
706
0
  int flags = 0; // no flags
707
708
  //const aom_codec_cx_pkt_t *pkt = NULL;
709
0
  const aom_codec_err_t res = aom_codec_encode(codec, img, frame_index, 1, flags);
710
0
  if (res != AOM_CODEC_OK) {
711
0
    struct heif_error err = {
712
0
        heif_error_Encoder_plugin_error,
713
0
        heif_suberror_Unspecified,
714
0
        kError_encode_frame
715
0
    };
716
0
    return err;
717
0
  }
718
719
0
  return heif_error_ok;
720
0
}
721
722
723
struct heif_error aom_encode_image(void* encoder_raw, const struct heif_image* image,
724
                                   heif_image_input_class input_class)
725
0
{
726
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
727
728
0
  struct heif_error err;
729
730
0
  bool success = image->image->extend_padding_to_size(image->image->get_width(),
731
0
                                                      image->image->get_height());
732
0
  if (!success) {
733
0
    err = {heif_error_Memory_allocation_error,
734
0
           heif_suberror_Unspecified,
735
0
           kError_out_of_memory};
736
0
    return err;
737
0
  }
738
739
740
0
  const int source_width = heif_image_get_width(image, heif_channel_Y);
741
0
  const int source_height = heif_image_get_height(image, heif_channel_Y);
742
743
0
  const heif_chroma chroma = heif_image_get_chroma_format(image);
744
745
0
  int bpp_y = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
746
747
  // --- copy libheif image to aom image
748
749
0
  aom_image_t input_image;
750
751
0
  aom_img_fmt_t img_format = AOM_IMG_FMT_NONE;
752
753
0
  int chroma_height = 0;
754
0
  int chroma_sample_position = AOM_CSP_UNKNOWN;
755
756
0
  switch (chroma) {
757
0
    case heif_chroma_420:
758
0
    case heif_chroma_monochrome:
759
0
      img_format = AOM_IMG_FMT_I420;
760
0
      chroma_height = (source_height+1)/2;
761
0
      chroma_sample_position = AOM_CSP_UNKNOWN; // TODO: change this to CSP_CENTER in the future (https://github.com/AOMediaCodec/av1-avif/issues/88)
762
0
      break;
763
0
    case heif_chroma_422:
764
0
      img_format = AOM_IMG_FMT_I422;
765
0
      chroma_height = (source_height+1)/2;
766
0
      chroma_sample_position = AOM_CSP_COLOCATED;
767
0
      break;
768
0
    case heif_chroma_444:
769
0
      img_format = AOM_IMG_FMT_I444;
770
0
      chroma_height = source_height;
771
0
      chroma_sample_position = AOM_CSP_COLOCATED;
772
0
      break;
773
0
    default:
774
0
      img_format = AOM_IMG_FMT_NONE;
775
0
      chroma_sample_position = AOM_CSP_UNKNOWN;
776
0
      assert(false);
777
0
      break;
778
0
  }
779
780
0
  if (bpp_y > 8) {
781
0
    img_format = (aom_img_fmt_t) (img_format | AOM_IMG_FMT_HIGHBITDEPTH);
782
0
  }
783
784
0
  if (!aom_img_alloc(&input_image, img_format,
785
0
                     source_width, source_height, 1)) {
786
0
    err = {heif_error_Memory_allocation_error,
787
0
           heif_suberror_Unspecified,
788
0
           "Failed to allocate image"};
789
0
    return err;
790
0
  }
791
792
793
0
  for (int plane = 0; plane < 3; plane++) {
794
0
    unsigned char* buf = input_image.planes[plane];
795
0
    const int stride = input_image.stride[plane];
796
797
0
    if (chroma == heif_chroma_monochrome && plane != 0) {
798
0
      if (bpp_y == 8) {
799
0
        memset(buf, 128, chroma_height * stride);
800
0
      }
801
0
      else {
802
0
        uint16_t* buf16 = (uint16_t*) buf;
803
0
        uint16_t half_range = (uint16_t) (1 << (bpp_y - 1));
804
0
        for (int i = 0; i < chroma_height * stride / 2; i++) {
805
0
          buf16[i] = half_range;
806
0
        }
807
0
      }
808
809
0
      continue;
810
0
    }
811
812
    /*
813
    const int w = aom_img_plane_width(img, plane) *
814
                  ((img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1);
815
    const int h = aom_img_plane_height(img, plane);
816
    */
817
818
0
    int in_stride = 0;
819
0
    const uint8_t* in_p = heif_image_get_plane_readonly(image, (heif_channel) plane, &in_stride);
820
821
0
    int w = source_width;
822
0
    int h = source_height;
823
824
0
    if (plane != 0) {
825
0
      if (chroma != heif_chroma_444) { w = (w + 1) / 2; }
826
0
      if (chroma == heif_chroma_420) { h = (h + 1) / 2; }
827
828
0
      assert(w == heif_image_get_width(image, (heif_channel) plane));
829
0
      assert(h == heif_image_get_height(image, (heif_channel) plane));
830
0
    }
831
832
0
    if (bpp_y > 8) {
833
0
      w *= 2;
834
0
    }
835
836
0
    for (int y = 0; y < h; y++) {
837
0
      memcpy(buf, &in_p[y * in_stride], w);
838
0
      buf += stride;
839
0
    }
840
0
  }
841
842
843
844
  // --- configure codec
845
846
0
  aom_codec_iface_t* iface;
847
0
  aom_codec_ctx_t codec;
848
849
0
  iface = aom_codec_av1_cx();
850
  //encoder->encoder = get_aom_encoder_by_name("av1");
851
0
  if (!iface) {
852
0
    err = {heif_error_Unsupported_feature,
853
0
           heif_suberror_Unsupported_codec,
854
0
           "Unsupported codec: AOMedia Project AV1 Encoder"};
855
0
    return err;
856
0
  }
857
858
859
0
  unsigned int aomUsage = 0;
860
0
#if defined(AOM_USAGE_ALL_INTRA)
861
  // aom 3.1.0
862
0
  aomUsage = (encoder->realtime_mode ? AOM_USAGE_REALTIME : AOM_USAGE_ALL_INTRA);
863
#elif defined(AOM_USAGE_REALTIME)
864
  // aom 2.0
865
  aomUsage = (encoder->realtime_mode ? AOM_USAGE_REALTIME : AOM_USAGE_GOOD_QUALITY);
866
#endif
867
868
0
  aom_codec_enc_cfg_t cfg;
869
0
  aom_codec_err_t res = aom_codec_enc_config_default(iface, &cfg, aomUsage);
870
0
  if (res) {
871
0
    err = {heif_error_Encoder_plugin_error,
872
0
           heif_suberror_Unspecified,
873
0
           "Failed to get default codec config"};
874
0
    return err;
875
0
  }
876
877
0
  heif::Box_av1C::configuration inout_config;
878
0
  heif::fill_av1C_configuration(&inout_config, image->image);
879
880
0
  cfg.g_w = source_width;
881
0
  cfg.g_h = source_height;
882
  // Set the max number of frames to encode to 1. This makes the libaom encoder
883
  // set still_picture and reduced_still_picture_header to 1 in the AV1 sequence
884
  // header OBU.
885
0
  cfg.g_limit = 1;
886
887
0
  cfg.g_profile = inout_config.seq_profile;
888
0
  cfg.g_bit_depth = (aom_bit_depth_t) bpp_y;
889
0
  cfg.g_input_bit_depth = bpp_y;
890
891
0
  cfg.rc_end_usage = AOM_Q;
892
893
0
  int min_q = encoder->min_q;
894
0
  int max_q = encoder->max_q;
895
896
0
  if (input_class == heif_image_input_class_alpha && encoder->alpha_min_q_set && encoder->alpha_max_q_set) {
897
0
      min_q = encoder->alpha_min_q;
898
0
      max_q = encoder->alpha_max_q;
899
0
  }
900
901
0
  cfg.rc_min_quantizer = min_q;
902
0
  cfg.rc_max_quantizer = max_q;
903
0
  cfg.g_error_resilient = 0;
904
0
  cfg.g_threads = encoder->threads;
905
906
0
  if (chroma == heif_chroma_monochrome) {
907
0
    cfg.monochrome = 1;
908
0
  }
909
910
  // --- initialize codec
911
912
0
  aom_codec_flags_t encoder_flags = 0;
913
0
  if (bpp_y > 8) {
914
0
    encoder_flags = (aom_codec_flags_t) (encoder_flags | AOM_CODEC_USE_HIGHBITDEPTH);
915
0
  }
916
917
0
  if (aom_codec_enc_init(&codec, iface, &cfg, encoder_flags)) {
918
0
    err = {heif_error_Encoder_plugin_error,
919
0
           heif_suberror_Unspecified,
920
0
           "Failed to initialize encoder"};
921
0
    return err;
922
0
  }
923
924
0
  aom_codec_control(&codec, AOME_SET_CPUUSED, encoder->cpu_used);
925
926
0
  int quality = encoder->quality;
927
928
0
  if (input_class == heif_image_input_class_alpha && encoder->alpha_quality_set) {
929
0
      quality = encoder->alpha_quality;
930
0
  }
931
932
0
  int cq_level = ((100 - quality) * 63 + 50) / 100;
933
0
  aom_codec_control(&codec, AOME_SET_CQ_LEVEL, cq_level);
934
935
0
  if (encoder->threads > 1) {
936
#if defined(AV1E_SET_ROW_MT)
937
    // aom 2.0
938
    aom_codec_control(&encoder->codec, AV1E_SET_ROW_MT, 1);
939
#endif
940
0
  }
941
942
943
0
  auto nclx = image->image->get_color_profile_nclx();
944
945
  // In aom, color_range defaults to limited range (0). Set it to full range (1).
946
0
  aom_codec_control(&codec, AV1E_SET_COLOR_RANGE, nclx ? nclx->get_full_range_flag() : 1);
947
0
  aom_codec_control(&codec, AV1E_SET_CHROMA_SAMPLE_POSITION, chroma_sample_position);
948
949
0
  if (nclx &&
950
0
      (input_class == heif_image_input_class_normal ||
951
0
       input_class == heif_image_input_class_thumbnail)) {
952
0
    aom_codec_control(&codec, AV1E_SET_COLOR_PRIMARIES, nclx->get_colour_primaries());
953
0
    aom_codec_control(&codec, AV1E_SET_MATRIX_COEFFICIENTS, nclx->get_matrix_coefficients());
954
0
    aom_codec_control(&codec, AV1E_SET_TRANSFER_CHARACTERISTICS, nclx->get_transfer_characteristics());
955
0
  }
956
957
0
  aom_codec_control(&codec, AOME_SET_TUNING, encoder->tune);
958
959
0
  if (encoder->lossless || (input_class == heif_image_input_class_alpha && encoder->lossless_alpha)) {
960
0
    aom_codec_control(&codec, AV1E_SET_LOSSLESS, 1);
961
0
  }
962
963
0
#if defined(HAVE_AOM_CODEC_SET_OPTION)
964
  // Apply the custom AOM encoder options.
965
  // These should always be applied last as they can override the values that were set above.
966
0
  for (const auto& p : encoder->custom_options) {
967
0
    aom_codec_set_option(&codec, p.name.c_str(), p.value.c_str());
968
0
  }
969
0
#endif
970
971
  // --- encode frame
972
973
0
  err = encode_frame(&codec, &input_image); //, frame_count++, flags, writer);
974
0
  if (err.code != heif_error_Ok) {
975
0
    return err;
976
0
  }
977
978
0
  encoder->compressedData.clear();
979
0
  const aom_codec_cx_pkt_t* pkt = NULL;
980
0
  aom_codec_iter_t iter = NULL; // for extracting the compressed packets
981
982
0
  while ((pkt = aom_codec_get_cx_data(&codec, &iter)) != NULL) {
983
984
0
    if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
985
      //std::cerr.write((char*)pkt->data.frame.buf, pkt->data.frame.sz);
986
987
      //printf("packet of size: %d\n",(int)pkt->data.frame.sz);
988
989
990
      // TODO: split the received data into separate OBUs
991
      // This allows libheif to easily extract the sequence header for the av1C header
992
993
0
      size_t n = pkt->data.frame.sz;
994
0
      size_t oldSize = encoder->compressedData.size();
995
0
      encoder->compressedData.resize(oldSize + n);
996
997
0
      memcpy(encoder->compressedData.data() + oldSize,
998
0
             pkt->data.frame.buf,
999
0
             n);
1000
1001
0
      encoder->data_read = false;
1002
0
    }
1003
0
  }
1004
1005
0
  int flags = 0;
1006
0
  res = aom_codec_encode(&codec, NULL, -1, 0, flags);
1007
0
  if (res != AOM_CODEC_OK) {
1008
0
    err = {heif_error_Encoder_plugin_error,
1009
0
           heif_suberror_Unspecified,
1010
0
           kError_encode_frame};
1011
0
    return err;
1012
0
  }
1013
1014
0
  iter = NULL;
1015
1016
0
  while ((pkt = aom_codec_get_cx_data(&codec, &iter)) != NULL) {
1017
1018
0
    if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) {
1019
      //std::cerr.write((char*)pkt->data.frame.buf, pkt->data.frame.sz);
1020
1021
      //printf("packet of size: %d\n",(int)pkt->data.frame.sz);
1022
1023
1024
      // TODO: split the received data into separate OBUs
1025
      // This allows libheif to easily extract the sequence header for the av1C header
1026
1027
0
      size_t n = pkt->data.frame.sz;
1028
0
      size_t oldSize = encoder->compressedData.size();
1029
0
      encoder->compressedData.resize(oldSize + n);
1030
1031
0
      memcpy(encoder->compressedData.data() + oldSize,
1032
0
             pkt->data.frame.buf,
1033
0
             n);
1034
1035
0
      encoder->data_read = false;
1036
0
    }
1037
0
  }
1038
1039
1040
  // --- clean up
1041
1042
0
  aom_img_free(&input_image);
1043
1044
0
  if (aom_codec_destroy(&codec)) {
1045
0
    err = {heif_error_Encoder_plugin_error,
1046
0
           heif_suberror_Unspecified,
1047
0
           "Failed to destroy codec"};
1048
0
    return err;
1049
0
  }
1050
1051
0
  return heif_error_ok;
1052
0
}
1053
1054
1055
struct heif_error aom_get_compressed_data(void* encoder_raw, uint8_t** data, int* size,
1056
                                          enum heif_encoded_data_type* type)
1057
0
{
1058
0
  struct encoder_struct_aom* encoder = (struct encoder_struct_aom*) encoder_raw;
1059
1060
0
  if (encoder->data_read) {
1061
0
    *size = 0;
1062
0
    *data = nullptr;
1063
0
  }
1064
0
  else {
1065
0
    *size = (int) encoder->compressedData.size();
1066
0
    *data = encoder->compressedData.data();
1067
0
    encoder->data_read = true;
1068
0
  }
1069
1070
0
  return heif_error_ok;
1071
0
}
1072
1073
1074
static const struct heif_encoder_plugin encoder_plugin_aom
1075
    {
1076
        /* plugin_api_version */ 3,
1077
        /* compression_format */ heif_compression_AV1,
1078
        /* id_name */ "aom",
1079
        /* priority */ AOM_PLUGIN_PRIORITY,
1080
        /* supports_lossy_compression */ true,
1081
        /* supports_lossless_compression */ true,
1082
        /* get_plugin_name */ aom_plugin_name,
1083
        /* init_plugin */ aom_init_plugin,
1084
        /* cleanup_plugin */ aom_cleanup_plugin,
1085
        /* new_encoder */ aom_new_encoder,
1086
        /* free_encoder */ aom_free_encoder,
1087
        /* set_parameter_quality */ aom_set_parameter_quality,
1088
        /* get_parameter_quality */ aom_get_parameter_quality,
1089
        /* set_parameter_lossless */ aom_set_parameter_lossless,
1090
        /* get_parameter_lossless */ aom_get_parameter_lossless,
1091
        /* set_parameter_logging_level */ aom_set_parameter_logging_level,
1092
        /* get_parameter_logging_level */ aom_get_parameter_logging_level,
1093
        /* list_parameters */ aom_list_parameters,
1094
        /* set_parameter_integer */ aom_set_parameter_integer,
1095
        /* get_parameter_integer */ aom_get_parameter_integer,
1096
        /* set_parameter_boolean */ aom_set_parameter_boolean,
1097
        /* get_parameter_boolean */ aom_get_parameter_boolean,
1098
        /* set_parameter_string */ aom_set_parameter_string,
1099
        /* get_parameter_string */ aom_get_parameter_string,
1100
        /* query_input_colorspace */ aom_query_input_colorspace,
1101
        /* encode_image */ aom_encode_image,
1102
        /* get_compressed_data */ aom_get_compressed_data,
1103
        /* query_input_colorspace (v2) */ aom_query_input_colorspace2,
1104
        /* query_encoded_size (v3) */ aom_query_encoded_size
1105
    };
1106
1107
const struct heif_encoder_plugin* get_encoder_plugin_aom()
1108
2
{
1109
2
  return &encoder_plugin_aom;
1110
2
}