Coverage Report

Created: 2026-04-01 07:49

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