Coverage Report

Created: 2026-09-02 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/plugins/encoder_svt.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2022 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 "encoder_svt.h"
24
#include <vector>
25
#include <cstring>
26
#include <cassert>
27
#include <algorithm>
28
#include <deque>
29
#include <memory>
30
#include <limits>
31
#include <cmath>
32
#include <cstdio>
33
34
#include "svt-av1/EbSvtAv1.h"
35
#include "svt-av1/EbSvtAv1Enc.h"
36
#include <utility>
37
38
// TODO: for some reason, the SVT encoder outputs only keyframes for me.
39
//       It appears to work in libavif, but I didn't find the difference yet.
40
41
struct encoder_struct_svt
42
{
43
  ~encoder_struct_svt()
44
0
  {
45
0
    if (svt_encoder) {
46
0
      svt_av1_enc_deinit(svt_encoder);
47
0
      svt_av1_enc_deinit_handle(svt_encoder);
48
0
    }
49
0
  }
50
51
  int speed = 12; // 0-13
52
53
  int quality;
54
55
  int min_q = 0;
56
  int max_q = 63;
57
  int qp = -1;
58
  bool qp_set = false;
59
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
60
  bool lossless = false;
61
#endif
62
63
  int threads = 4;
64
65
  int tile_rows = 1; // 1,2,4,8,16,32,64
66
  int tile_cols = 1; // 1,2,4,8,16,32,64
67
68
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
69
  enum Tune
70
  {
71
    Tune_VQ = 0,
72
    Tune_PSNR = 1,
73
    Tune_SSIM = 2,
74
#if SVT_AV1_CHECK_VERSION(4, 0, 0)
75
    Tune_IQ = 3,
76
    Tune_MS_SSIM = 4
77
#endif
78
  };
79
80
  uint8_t tune = Tune_PSNR;
81
#endif
82
83
  heif_chroma chroma = heif_chroma_420; // SVT-AV1 only supports 4:2:0 as of v3.0.0
84
  heif_image_input_class input_class = heif_image_input_class_normal;
85
86
  // int keyframe_interval = 0; TODO: this does not seem to work (see all [1])
87
88
  // --- Encoder
89
90
  EbComponentType* svt_encoder = nullptr;
91
  EbSvtAv1EncConfiguration svt_config;
92
  EbBufferHeaderType input_buffer;
93
94
  bool still_image_mode = false;
95
  bool low_delay_mode = false;
96
97
  // --- output
98
99
  struct Packet
100
  {
101
    std::vector<uint8_t> compressedData;
102
    uintptr_t frameNr = 0;
103
    bool is_keyframe = false;
104
  };
105
106
  std::deque<Packet> output_packets;
107
  std::vector<uint8_t> active_output_data;
108
109
  //std::vector<uint8_t> compressed_data;
110
  //bool data_read = false;
111
};
112
113
//static const char* kError_out_of_memory = "Out of memory";
114
115
static const char* kParam_min_q = "min-q";
116
static const char* kParam_max_q = "max-q";
117
static const char* kParam_qp = "qp";
118
static const char* kParam_threads = "threads";
119
static const char* kParam_speed = "speed";
120
121
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
122
static const char* kParam_tune = "tune";
123
#if SVT_AV1_CHECK_VERSION(4, 0, 0)
124
static const char* const kParam_tune_valid_values[] = {"vq", "psnr", "ssim", "iq", "ms-ssim", nullptr};
125
#else
126
static const char* const kParam_tune_valid_values[] = {"vq", "psnr", "ssim", nullptr};
127
#endif
128
#endif
129
130
/*
131
static const char* kParam_chroma = "chroma";
132
static const char* const kParam_chroma_valid_values[] = {
133
    "420", "422", "444", nullptr
134
};
135
*/
136
137
static int valid_tile_num_values[] = {1, 2, 4, 8, 16, 32, 64};
138
139
static heif_error heif_error_codec_library_error = {
140
  heif_error_Encoder_plugin_error,
141
  heif_suberror_Unspecified,
142
  "SVT-AV1 error"
143
};
144
145
static const int SVT_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 svt_set_default_parameters(void* encoder);
153
154
155
static const char* svt_plugin_name()
156
0
{
157
0
  plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
158
0
  snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "SVT-AV1 encoder %s", svt_av1_get_version());
159
160
0
  return plugin_name;
161
0
}
162
163
int int_log2(int pow2_value)
164
0
{
165
0
  int input_value = pow2_value;
166
0
  (void) input_value;
167
168
0
  int v = 0;
169
0
  while (pow2_value > 1) {
170
0
    pow2_value >>= 1;
171
0
    v++;
172
0
  }
173
174
  // check that computation is correct
175
0
  assert(input_value == (1 << v));
176
177
0
  return v;
178
0
}
179
180
#define MAX_NPARAMETERS 11
181
182
static heif_encoder_parameter svt_encoder_params[MAX_NPARAMETERS];
183
static const heif_encoder_parameter* svt_encoder_parameter_ptrs[MAX_NPARAMETERS + 1];
184
185
static void svt_init_parameters()
186
3
{
187
3
  heif_encoder_parameter* p = svt_encoder_params;
188
3
  const heif_encoder_parameter** d = svt_encoder_parameter_ptrs;
189
3
  int i = 0;
190
191
3
  assert(i < MAX_NPARAMETERS);
192
3
  p->version = 2;
193
3
  p->name = kParam_speed;
194
3
  p->type = heif_encoder_parameter_type_integer;
195
3
  p->integer.default_value = 12;
196
3
  p->has_default = true;
197
3
  p->integer.have_minimum_maximum = true;
198
3
  p->integer.minimum = 0;
199
3
  p->integer.maximum = 13;
200
3
  p->integer.valid_values = nullptr;
201
3
  p->integer.num_valid_values = 0;
202
3
  d[i++] = p++;
203
204
3
  assert(i < MAX_NPARAMETERS);
205
3
  p->version = 2;
206
3
  p->name = kParam_threads;
207
3
  p->type = heif_encoder_parameter_type_integer;
208
3
  p->integer.default_value = 4;
209
3
  p->has_default = true;
210
3
  p->integer.have_minimum_maximum = true;
211
3
  p->integer.minimum = 1;
212
3
  p->integer.maximum = 16;
213
3
  p->integer.valid_values = nullptr;
214
3
  p->integer.num_valid_values = 0;
215
3
  d[i++] = p++;
216
217
3
  assert(i < MAX_NPARAMETERS);
218
3
  p->version = 2;
219
3
  p->name = "tile-rows";
220
3
  p->type = heif_encoder_parameter_type_integer;
221
3
  p->integer.default_value = 4;
222
3
  p->has_default = true;
223
3
  p->integer.have_minimum_maximum = false;
224
3
  p->integer.valid_values = valid_tile_num_values;
225
3
  p->integer.num_valid_values = 7;
226
3
  d[i++] = p++;
227
228
3
  assert(i < MAX_NPARAMETERS);
229
3
  p->version = 2;
230
3
  p->name = "tile-cols";
231
3
  p->type = heif_encoder_parameter_type_integer;
232
3
  p->integer.default_value = 4;
233
3
  p->has_default = true;
234
3
  p->integer.have_minimum_maximum = false;
235
3
  p->integer.valid_values = valid_tile_num_values;
236
3
  p->integer.num_valid_values = 7;
237
3
  d[i++] = p++;
238
239
240
3
  assert(i < MAX_NPARAMETERS);
241
3
  p->version = 2;
242
3
  p->name = heif_encoder_parameter_name_quality;
243
3
  p->type = heif_encoder_parameter_type_integer;
244
3
  p->integer.default_value = 50;
245
3
  p->has_default = true;
246
3
  p->integer.have_minimum_maximum = true;
247
3
  p->integer.minimum = 0;
248
3
  p->integer.maximum = 100;
249
3
  p->integer.valid_values = NULL;
250
3
  p->integer.num_valid_values = 0;
251
3
  d[i++] = p++;
252
253
3
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
254
3
  assert(i < MAX_NPARAMETERS);
255
3
  p->version = 2;
256
3
  p->name = heif_encoder_parameter_name_lossless;
257
3
  p->type = heif_encoder_parameter_type_boolean;
258
3
  p->boolean.default_value = false;
259
3
  p->has_default = true;
260
3
  d[i++] = p++;
261
3
#endif
262
263
#if 0
264
  assert(i < MAX_NPARAMETERS);
265
  p->version = 2;
266
  p->name = kParam_chroma;
267
  p->type = heif_encoder_parameter_type_string;
268
  p->string.default_value = "420";
269
  p->has_default = true;
270
  p->string.valid_values = kParam_chroma_valid_values;
271
  d[i++] = p++;
272
#endif
273
274
3
  assert(i < MAX_NPARAMETERS);
275
3
  p->version = 2;
276
3
  p->name = kParam_qp;
277
3
  p->type = heif_encoder_parameter_type_integer;
278
3
  p->integer.default_value = 50;
279
3
  p->has_default = false;
280
3
  p->integer.have_minimum_maximum = true;
281
3
  p->integer.minimum = 0;
282
3
  p->integer.maximum = 63;
283
3
  p->integer.valid_values = nullptr;
284
3
  p->integer.num_valid_values = 0;
285
3
  d[i++] = p++;
286
287
3
  assert(i < MAX_NPARAMETERS);
288
3
  p->version = 2;
289
3
  p->name = kParam_min_q;
290
3
  p->type = heif_encoder_parameter_type_integer;
291
3
  p->integer.default_value = 0;
292
3
  p->has_default = true;
293
3
  p->integer.have_minimum_maximum = true;
294
3
  p->integer.minimum = 0;
295
3
  p->integer.maximum = 63;
296
3
  p->integer.valid_values = nullptr;
297
3
  p->integer.num_valid_values = 0;
298
3
  d[i++] = p++;
299
300
3
  assert(i < MAX_NPARAMETERS);
301
3
  p->version = 2;
302
3
  p->name = kParam_max_q;
303
3
  p->type = heif_encoder_parameter_type_integer;
304
3
  p->integer.default_value = 63;
305
3
  p->has_default = true;
306
3
  p->integer.have_minimum_maximum = true;
307
3
  p->integer.minimum = 0;
308
3
  p->integer.maximum = 63;
309
3
  p->integer.valid_values = nullptr;
310
3
  p->integer.num_valid_values = 0;
311
3
  d[i++] = p++;
312
313
3
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
314
3
  assert(i < MAX_NPARAMETERS);
315
3
  p->version = 2;
316
3
  p->name = kParam_tune;
317
3
  p->type = heif_encoder_parameter_type_string;
318
3
  p->string.default_value = "psnr";
319
3
  p->has_default = true;
320
3
  p->string.valid_values = kParam_tune_valid_values;
321
3
  d[i++] = p++;
322
3
#endif
323
324
3
  d[i++] = nullptr;
325
3
}
326
327
328
const heif_encoder_parameter** svt_list_parameters(void* encoder)
329
0
{
330
0
  return svt_encoder_parameter_ptrs;
331
0
}
332
333
static void svt_init_plugin()
334
3
{
335
3
  svt_init_parameters();
336
3
}
337
338
339
static void svt_cleanup_plugin()
340
1
{
341
1
}
342
343
heif_error svt_new_encoder(void** enc)
344
0
{
345
0
  auto* encoder = new encoder_struct_svt();
346
0
  struct heif_error err = heif_error_ok;
347
348
0
  *enc = encoder;
349
350
  // set default parameters
351
352
0
  svt_set_default_parameters(encoder);
353
354
0
  return err;
355
0
}
356
357
void svt_free_encoder(void* encoder_raw)
358
0
{
359
0
  auto* encoder = (struct encoder_struct_svt*) encoder_raw;
360
361
0
  delete encoder;
362
0
}
363
364
365
heif_error svt_set_parameter_quality(void* encoder_raw, int quality)
366
0
{
367
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
368
369
0
  if (quality < 0 || quality > 100) {
370
0
    return heif_error_invalid_parameter_value;
371
0
  }
372
373
0
  encoder->quality = quality;
374
375
0
  return heif_error_ok;
376
0
}
377
378
heif_error svt_get_parameter_quality(void* encoder_raw, int* quality)
379
0
{
380
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
381
382
0
  *quality = encoder->quality;
383
384
0
  return heif_error_ok;
385
0
}
386
387
heif_error svt_set_parameter_lossless(void* encoder_raw, int enable)
388
0
{
389
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
390
391
0
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
392
0
  encoder->lossless = enable;
393
#else
394
  if (enable) {
395
    encoder->min_q = 0;
396
    encoder->max_q = 0;
397
    encoder->qp = 0;
398
    encoder->qp_set = true;
399
    encoder->quality = 100; // not really required, but to be consistent
400
  }
401
#endif
402
403
0
  return heif_error_ok;
404
0
}
405
406
heif_error svt_get_parameter_lossless(void* encoder_raw, int* enable)
407
0
{
408
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
409
410
0
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
411
0
  *enable = encoder->lossless;
412
#else
413
  *enable = (encoder->min_q == 0 && encoder->max_q == 0 &&
414
             ((encoder->qp_set && encoder->qp == 0) || encoder->quality == 100));
415
#endif
416
417
0
  return heif_error_ok;
418
0
}
419
420
heif_error svt_set_parameter_logging_level(void* encoder_raw, int logging)
421
0
{
422
#if 0
423
  struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
424
425
  if (logging<0 || logging>4) {
426
    return heif_error_invalid_parameter_value;
427
  }
428
429
  encoder->logLevel = logging;
430
#endif
431
432
0
  return heif_error_ok;
433
0
}
434
435
heif_error svt_get_parameter_logging_level(void* encoder_raw, int* loglevel)
436
0
{
437
#if 0
438
  struct encoder_struct_x265* encoder = (struct encoder_struct_x265*)encoder_raw;
439
440
  *loglevel = encoder->logLevel;
441
#else
442
0
  *loglevel = 0;
443
0
#endif
444
445
0
  return heif_error_ok;
446
0
}
447
448
0
#define set_value(paramname, paramvar) if (strcmp(name, paramname)==0) { encoder->paramvar = value; return heif_error_ok; }
449
0
#define get_value(paramname, paramvar) if (strcmp(name, paramname)==0) { *value = encoder->paramvar; return heif_error_ok; }
450
451
452
heif_error svt_set_parameter_integer(void* encoder_raw, const char* name, int value)
453
0
{
454
0
  encoder_struct_svt* encoder = (encoder_struct_svt*) encoder_raw;
455
456
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
457
0
    return svt_set_parameter_quality(encoder, value);
458
0
  }
459
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
460
0
    return svt_set_parameter_lossless(encoder, value);
461
0
  }
462
0
  else if (strcmp(name, kParam_qp) == 0) {
463
0
    encoder->qp = value;
464
0
    encoder->qp_set = true;
465
0
    return heif_error_ok;
466
0
  }
467
468
0
  set_value(kParam_min_q, min_q);
469
0
  set_value(kParam_max_q, max_q);
470
0
  set_value(kParam_threads, threads);
471
0
  set_value(kParam_speed, speed);
472
0
  set_value("tile-rows", tile_rows);
473
0
  set_value("tile-cols", tile_cols);
474
475
0
  return heif_error_unsupported_parameter;
476
0
}
477
478
heif_error svt_get_parameter_integer(void* encoder_raw, const char* name, int* value)
479
0
{
480
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
481
482
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
483
0
    return svt_get_parameter_quality(encoder, value);
484
0
  }
485
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
486
0
    return svt_get_parameter_lossless(encoder, value);
487
0
  }
488
0
  else if (strcmp(name, kParam_qp) == 0) {
489
0
    if (!encoder->qp_set) {
490
0
      return heif_error_unsupported_parameter;
491
0
    }
492
0
    *value = encoder->qp;
493
0
    return heif_error_ok;
494
0
  }
495
496
0
  get_value(kParam_min_q, min_q);
497
0
  get_value(kParam_max_q, max_q);
498
0
  get_value(kParam_threads, threads);
499
0
  get_value(kParam_speed, speed);
500
0
  get_value("tile-rows", tile_rows);
501
0
  get_value("tile-cols", tile_cols);
502
503
0
  return heif_error_unsupported_parameter;
504
0
}
505
506
507
heif_error svt_set_parameter_boolean(void* encoder_raw, const char* name, int value)
508
0
{
509
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
510
511
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
512
0
    return svt_set_parameter_lossless(encoder, value);
513
0
  }
514
515
  //set_value(kParam_realtime, realtime_mode);
516
517
0
  return heif_error_unsupported_parameter;
518
0
}
519
520
heif_error svt_get_parameter_boolean(void* encoder_raw, const char* name, int* value)
521
0
{
522
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
523
524
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
525
0
    return svt_get_parameter_lossless(encoder, value);
526
0
  }
527
528
  //get_value(kParam_realtime, realtime_mode);
529
530
0
  return heif_error_unsupported_parameter;
531
0
}
532
533
534
heif_error svt_set_parameter_string(void* encoder_raw, const char* name, const char* value)
535
0
{
536
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
537
0
  (void) encoder;
538
539
#if 0
540
  if (strcmp(name, kParam_chroma) == 0) {
541
    if (strcmp(value, "420") == 0) {
542
      encoder->chroma = heif_chroma_420;
543
      return heif_error_ok;
544
    }
545
    else if (strcmp(value, "422") == 0) {
546
      encoder->chroma = heif_chroma_422;
547
      return heif_error_ok;
548
    }
549
    else if (strcmp(value, "444") == 0) {
550
      encoder->chroma = heif_chroma_444;
551
      return heif_error_ok;
552
    }
553
    else {
554
      return heif_error_invalid_parameter_value;
555
    }
556
  }
557
#endif
558
559
0
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
560
0
  if (strcmp(name, kParam_tune) == 0) {
561
0
    if (strcmp(value, "vq") == 0) {
562
0
      encoder->tune = encoder_struct_svt::Tune_VQ;
563
0
      return heif_error_ok;
564
0
    }
565
0
    else if (strcmp(value, "psnr") == 0) {
566
0
      encoder->tune = encoder_struct_svt::Tune_PSNR;
567
0
      return heif_error_ok;
568
0
    }
569
0
    else if (strcmp(value, "ssim") == 0) {
570
0
      encoder->tune = encoder_struct_svt::Tune_SSIM;
571
0
      return heif_error_ok;
572
0
    }
573
0
#if SVT_AV1_CHECK_VERSION(4, 0, 0)
574
0
    else if (strcmp(value, "iq") == 0) {
575
0
      encoder->tune = encoder_struct_svt::Tune_IQ;
576
0
      return heif_error_ok;
577
0
    }
578
0
    else if (strcmp(value, "ms-ssim") == 0) {
579
0
      encoder->tune = encoder_struct_svt::Tune_MS_SSIM;
580
0
      return heif_error_ok;
581
0
    }
582
0
#endif
583
0
  }
584
0
#endif
585
586
0
  return heif_error_unsupported_parameter;
587
0
}
588
589
590
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
591
static void save_strcpy(char* dst, int dst_size, const char* src)
592
0
{
593
0
  strncpy(dst, src, dst_size - 1);
594
0
  dst[dst_size - 1] = 0;
595
0
}
596
#endif
597
598
599
heif_error svt_get_parameter_string(void* encoder_raw, const char* name,
600
                                    char* value, int value_size)
601
0
{
602
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
603
0
  (void) encoder;
604
605
#if 0
606
  if (strcmp(name, kParam_chroma) == 0) {
607
    switch (encoder->chroma) {
608
      case heif_chroma_420:
609
        save_strcpy(value, value_size, "420");
610
        break;
611
      case heif_chroma_422:
612
        save_strcpy(value, value_size, "422");
613
        break;
614
      case heif_chroma_444:
615
        save_strcpy(value, value_size, "444");
616
        break;
617
      default:
618
        assert(false);
619
        return heif_error_invalid_parameter_value;
620
    }
621
    return heif_error_ok;
622
  }
623
#endif
624
625
0
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
626
0
  if (strcmp(name, kParam_tune) == 0) {
627
0
    switch (encoder->tune) {
628
0
      case encoder_struct_svt::Tune_VQ:
629
0
        save_strcpy(value, value_size, "vq");
630
0
        break;
631
0
      case encoder_struct_svt::Tune_PSNR:
632
0
        save_strcpy(value, value_size, "psnr");
633
0
        break;
634
0
      case encoder_struct_svt::Tune_SSIM:
635
0
        save_strcpy(value, value_size, "ssim");
636
0
        break;
637
0
#if SVT_AV1_CHECK_VERSION(4, 0, 0)
638
0
      case encoder_struct_svt::Tune_IQ:
639
0
        save_strcpy(value, value_size, "iq");
640
0
        break;
641
0
      case encoder_struct_svt::Tune_MS_SSIM:
642
0
        save_strcpy(value, value_size, "ms-ssim");
643
0
        break;
644
0
#endif
645
0
      default:
646
0
        assert(false);
647
648
0
        return heif_error_invalid_parameter_value;
649
0
    }
650
0
    return heif_error_ok;
651
0
  }
652
0
#endif
653
654
0
  return heif_error_unsupported_parameter;
655
0
}
656
657
658
static void svt_set_default_parameters(void* encoder)
659
0
{
660
0
  for (const heif_encoder_parameter** p = svt_encoder_parameter_ptrs; *p; p++) {
661
0
    const heif_encoder_parameter* param = *p;
662
663
0
    if (param->has_default) {
664
0
      switch (param->type) {
665
0
        case heif_encoder_parameter_type_integer:
666
0
          svt_set_parameter_integer(encoder, param->name, param->integer.default_value);
667
0
          break;
668
0
        case heif_encoder_parameter_type_boolean:
669
0
          svt_set_parameter_boolean(encoder, param->name, param->boolean.default_value);
670
0
          break;
671
0
        case heif_encoder_parameter_type_string:
672
          // NOLINTNEXTLINE(build/include_what_you_use)
673
0
          svt_set_parameter_string(encoder, param->name, param->string.default_value);
674
0
          break;
675
0
      }
676
0
    }
677
0
  }
678
0
}
679
680
681
void svt_query_input_colorspace(heif_colorspace* colorspace, heif_chroma* chroma)
682
0
{
683
0
  *colorspace = heif_colorspace_YCbCr;
684
0
  *chroma = heif_chroma_420;
685
0
}
686
687
688
void svt_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace, heif_chroma* chroma)
689
0
{
690
  //auto* encoder = (struct encoder_struct_svt*) encoder_raw;
691
692
0
  *colorspace = heif_colorspace_YCbCr;
693
0
  *chroma = heif_chroma_420;
694
0
}
695
696
697
void svt_query_encoded_size(void* encoder_raw, uint32_t input_width, uint32_t input_height,
698
                            uint32_t* encoded_width, uint32_t* encoded_height)
699
0
{
700
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
701
702
  // SVT-AV1 (as of version 1.2.1) can only create image sizes matching the chroma format. Add padding if necessary.
703
704
0
  if (input_width < 64) {
705
0
    *encoded_width = 64;
706
0
  }
707
0
  else if (encoder->chroma == heif_chroma_420 && (input_width & 1) == 1) {
708
0
    *encoded_width = input_width + 1;
709
0
  }
710
0
  else {
711
0
    *encoded_width = input_width;
712
0
  }
713
714
0
  if (input_height < 64) {
715
0
    *encoded_height = 64;
716
0
  }
717
0
  else if (encoder->chroma != heif_chroma_444 && (input_height & 1) == 1) {
718
0
    *encoded_height = input_height + 1;
719
0
  }
720
0
  else {
721
0
    *encoded_height = input_height;
722
0
  }
723
0
}
724
725
726
static heif_error svt_start_sequence_encoding_intern(void* encoder_raw, const heif_image* image,
727
                                                     enum heif_image_input_class input_class,
728
                                                     uint32_t framerate_num, uint32_t framerate_denom,
729
                                                     const heif_sequence_encoding_options* options,
730
                                                     bool image_sequence)
731
0
{
732
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
733
0
  encoder->input_class = input_class;
734
0
  EbErrorType res = EB_ErrorNone;
735
0
  heif_error err;
736
737
  // deinit the encoder if it was already initialized
738
  // (e.g. when the encoder is reused for alpha encoding after being used for YUV encoding)
739
0
  if (encoder->svt_encoder) {
740
0
    svt_av1_enc_deinit(encoder->svt_encoder);
741
0
    svt_av1_enc_deinit_handle(encoder->svt_encoder);
742
0
    encoder->svt_encoder = nullptr;
743
0
  }
744
745
  // encoder->compressed_data.clear();
746
747
0
  int w = heif_image_get_width(image, heif_channel_Y);
748
0
  int h = heif_image_get_height(image, heif_channel_Y);
749
750
0
  uint32_t encoded_width, encoded_height;
751
0
  svt_query_encoded_size(encoder_raw, w, h, &encoded_width, &encoded_height);
752
753
0
  const heif_chroma chroma = heif_image_get_chroma_format(image);
754
0
  int bitdepth_y = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
755
756
0
  EbColorFormat color_format = EB_YUV420;
757
758
0
  if (input_class == heif_image_input_class_alpha) {
759
0
    color_format = EB_YUV420;
760
    //chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN;
761
0
  }
762
0
  else {
763
0
    switch (chroma) {
764
0
      case heif_chroma_444:
765
0
        color_format = EB_YUV444;
766
        //chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
767
0
        break;
768
0
      case heif_chroma_422:
769
0
        color_format = EB_YUV422;
770
        //chromaPosition = RA_CHROMA_SAMPLE_POSITION_COLOCATED;
771
0
        break;
772
0
      case heif_chroma_420:
773
0
        color_format = EB_YUV420;
774
        //chromaPosition = RA_CHROMA_SAMPLE_POSITION_UNKNOWN; // TODO: set to CENTER when AV1 and svt supports this
775
0
        break;
776
0
      default:
777
0
        return heif_error_codec_library_error;
778
0
    }
779
0
  }
780
781
782
  // --- initialize the encoder
783
784
0
  EbComponentType*& svt_encoder = encoder->svt_encoder;
785
0
  EbSvtAv1EncConfiguration& svt_config = encoder->svt_config;
786
0
  memset(&svt_config, 0, sizeof(EbSvtAv1EncConfiguration));
787
788
0
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
789
0
  res = svt_av1_enc_init_handle(&svt_encoder, &svt_config);
790
#else
791
  res = svt_av1_enc_init_handle(&svt_encoder, nullptr, &svt_config);
792
#endif
793
0
  if (res != EB_ErrorNone) {
794
    //goto cleanup;
795
0
    return heif_error_codec_library_error;
796
0
  }
797
798
0
  svt_config.encoder_color_format = color_format;
799
0
  svt_config.encoder_bit_depth = (uint8_t) bitdepth_y;
800
  //svt_config.is_16bit_pipeline = bitdepth_y > 8;
801
802
0
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
803
0
  svt_config.lossless = encoder->lossless;
804
0
#endif
805
806
0
  heif_color_profile_nclx* nclx = nullptr;
807
0
  err = heif_image_get_nclx_color_profile(image, &nclx);
808
0
  if (err.code != heif_error_Ok) {
809
0
    nclx = nullptr;
810
0
  }
811
812
  // make sure NCLX profile is deleted at end of function
813
0
  auto nclx_deleter = std::unique_ptr<heif_color_profile_nclx, void (*)(heif_color_profile_nclx*)>(nclx, heif_nclx_color_profile_free);
814
815
0
  if (nclx) {
816
#if !SVT_AV1_CHECK_VERSION(3, 0, 0)
817
    svt_config.color_description_present_flag = true;
818
#endif
819
0
#if SVT_AV1_VERSION_MAJOR >= 1
820
0
    svt_config.color_primaries = static_cast<EbColorPrimaries>(nclx->color_primaries);
821
0
    svt_config.transfer_characteristics = static_cast<EbTransferCharacteristics>(nclx->transfer_characteristics);
822
0
    svt_config.matrix_coefficients = static_cast<EbMatrixCoefficients>(nclx->matrix_coefficients);
823
0
    svt_config.color_range = nclx->full_range_flag ? EB_CR_FULL_RANGE : EB_CR_STUDIO_RANGE;
824
#else
825
    svt_config.color_primaries = static_cast<uint8_t>(nclx->color_primaries);
826
    svt_config.transfer_characteristics = static_cast<uint8_t>(nclx->transfer_characteristics);
827
    svt_config.matrix_coefficients = static_cast<uint8_t>(nclx->matrix_coefficients);
828
    svt_config.color_range = nclx->full_range_flag ? 1 : 0;
829
#endif
830
831
832
#if !SVT_AV1_CHECK_VERSION(3, 0, 0)
833
    // Follow comment in svt header: set if input is HDR10 BT2020 using SMPTE ST2084.
834
    svt_config.high_dynamic_range_input = (bitdepth_y == 10 && // TODO: should this be >8 ?
835
                                           nclx->color_primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0 &&
836
                                           nclx->transfer_characteristics == heif_transfer_characteristic_ITU_R_BT_2100_0_PQ &&
837
                                           nclx->matrix_coefficients == heif_matrix_coefficients_ITU_R_BT_2020_2_non_constant_luminance);
838
#endif
839
0
  }
840
0
  else {
841
#if !SVT_AV1_CHECK_VERSION(3, 0, 0)
842
    svt_config.color_description_present_flag = false;
843
#endif
844
0
  }
845
846
847
0
  svt_config.source_width = encoded_width;
848
0
  svt_config.source_height = encoded_height;
849
0
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
850
0
  svt_config.level_of_parallelism = encoder->threads;
851
#else
852
  svt_config.logical_processors = encoder->threads;
853
#endif
854
855
  // disable 2-pass
856
0
  svt_config.rc_stats_buffer = SvtAv1FixedBuf{nullptr, 0};
857
858
0
  svt_config.rate_control_mode = 0; // constant rate factor
859
  //svt_config.enable_adaptive_quantization = 0;   // 2 is CRF (the default), 0 would be CQP
860
0
  float qp;
861
0
  if (encoder->qp_set) {
862
0
    qp = (float) encoder->qp;
863
0
  }
864
0
  else {
865
0
    qp = (float) (100 - encoder->quality) * 63.0f / 100.0f;
866
0
  }
867
0
#if SVT_AV1_CHECK_VERSION(4, 0, 0)
868
  // Use quarter-QP precision. The fractional QP part is passed in 'extended_crf_qindex_offset'
869
  // in units of quarter QP steps (one AV1 qindex step).
870
  // We set these configuration fields directly instead of using
871
  // svt_av1_enc_parse_parameter(&svt_config, "cqp", ...), because that would also
872
  // set aq_mode=0, which switches the rate control from CRF to plain CQP.
873
0
  uint32_t quarter_qp_steps = (uint32_t) std::lround(qp * 4.0f);
874
0
  svt_config.qp = quarter_qp_steps / 4;
875
0
  svt_config.extended_crf_qindex_offset = (uint8_t) (quarter_qp_steps % 4);
876
#else
877
  svt_config.qp = (uint32_t) std::lround(qp);
878
#endif
879
0
  svt_config.min_qp_allowed = encoder->min_q;
880
0
  svt_config.max_qp_allowed = encoder->max_q;
881
882
0
  svt_config.tile_rows = int_log2(encoder->tile_rows);
883
0
  svt_config.tile_columns = int_log2(encoder->tile_cols);
884
885
0
#if SVT_AV1_CHECK_VERSION(0, 9, 1)
886
0
  svt_config.tune = encoder->tune;
887
0
#endif
888
889
0
  svt_config.enc_mode = (int8_t) encoder->speed;
890
891
0
  if (image_sequence) {
892
    // svt_config.force_key_frames = true; TODO: this does not seem to work (see all [1]), tested with SVT 3.0.1
893
894
0
#if 1
895
0
#if SVT_AV1_CHECK_VERSION(4, 1, 0)
896
0
    switch (options->gop_structure) {
897
0
      case heif_sequence_gop_structure_intra_only:
898
0
        svt_config.pred_structure = PredStructure::ALL_INTRA;
899
0
        break;
900
0
      case heif_sequence_gop_structure_lowdelay:
901
0
        svt_config.pred_structure = PredStructure::LOW_DELAY;
902
0
        encoder->low_delay_mode = true;
903
0
        break;
904
0
      case heif_sequence_gop_structure_unrestricted:
905
0
        svt_config.pred_structure = PredStructure::RANDOM_ACCESS;
906
0
        break;
907
0
    }
908
#elif SVT_AV1_CHECK_VERSION(4, 0, 0)
909
    switch (options->gop_structure) {
910
      case heif_sequence_gop_structure_intra_only:
911
      case heif_sequence_gop_structure_lowdelay:
912
        svt_config.pred_structure = 1; // LOW_DELAY
913
        encoder->low_delay_mode = true;
914
        break;
915
      case heif_sequence_gop_structure_unrestricted:
916
        svt_config.pred_structure = 2; // RANDOM_ACCESS
917
        break;
918
    }
919
#else
920
    // TODO: setting pref_structure to SVT_AV1_PRED_LOW_DELAY_B hangs the encoder
921
    switch (options->gop_structure) {
922
      case heif_sequence_gop_structure_intra_only:
923
      case heif_sequence_gop_structure_lowdelay:
924
        svt_config.pred_structure = SvtAv1PredStructure::SVT_AV1_PRED_LOW_DELAY_B;
925
        encoder->low_delay_mode = true;
926
        break;
927
      case heif_sequence_gop_structure_unrestricted:
928
        svt_config.pred_structure = SvtAv1PredStructure::SVT_AV1_PRED_RANDOM_ACCESS;
929
        break;
930
    }
931
#endif
932
933
0
    if (options->keyframe_distance_max) {
934
0
      svt_config.intra_period_length = options->keyframe_distance_max - 1;
935
0
    }
936
0
#endif
937
0
  }
938
0
  else {
939
0
#if SVT_AV1_CHECK_VERSION(4, 0, 0)
940
0
    svt_config.avif = true;
941
0
    encoder->still_image_mode = true;
942
0
#endif
943
0
  }
944
945
0
  if (color_format == EB_YUV422 || bitdepth_y > 10) {
946
0
    svt_config.profile = PROFESSIONAL_PROFILE;
947
0
  }
948
0
  else if (color_format == EB_YUV444) {
949
0
    svt_config.profile = HIGH_PROFILE;
950
0
  }
951
952
0
  svt_config.frame_rate_numerator = framerate_num;
953
0
  svt_config.frame_rate_denominator = framerate_denom;
954
955
0
  res = svt_av1_enc_set_parameter(svt_encoder, &svt_config);
956
0
  if (res == EB_ErrorBadParameter) {
957
0
    return heif_error_codec_library_error;
958
0
  }
959
960
0
  res = svt_av1_enc_init(svt_encoder);
961
0
  if (res != EB_ErrorNone) {
962
0
    return heif_error_codec_library_error;
963
0
  }
964
965
0
  return {};
966
0
}
967
968
969
static heif_error read_encoder_output_packets(void* encoder_raw, bool done_sending_pics)
970
0
{
971
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
972
0
  EbComponentType*& svt_encoder = encoder->svt_encoder;
973
0
  EbErrorType res = EB_ErrorNone;
974
975
976
  // --- read compressed picture
977
978
0
  int encode_at_eos = 0;
979
980
0
  do {
981
0
    EbBufferHeaderType* output_buf = nullptr;
982
983
0
    res = svt_av1_enc_get_packet(svt_encoder, &output_buf, (uint8_t) done_sending_pics);
984
0
    if (output_buf != nullptr) {
985
0
      encode_at_eos = ((output_buf->flags & EB_BUFFERFLAG_EOS) == EB_BUFFERFLAG_EOS);
986
0
      if (output_buf->p_buffer && (output_buf->n_filled_len > 0)) {
987
0
        uint8_t* data = output_buf->p_buffer;
988
0
        uint32_t n = output_buf->n_filled_len;
989
990
0
        encoder_struct_svt::Packet output_packet;
991
0
        output_packet.frameNr = output_buf->pts;
992
0
        output_packet.is_keyframe = (output_buf->pic_type == EbAv1PictureType::EB_AV1_KEY_PICTURE);
993
994
0
        encoder->output_packets.emplace_back(output_packet);
995
0
        encoder->output_packets.back().compressedData.resize(n);
996
997
0
        memcpy(encoder->output_packets.back().compressedData.data(),
998
0
               data, n);
999
0
      }
1000
0
      svt_av1_enc_release_out_buffer(&output_buf);
1001
1002
      // In LOW_DELAY mode, svt_av1_enc_get_packet() is always a blocking call
1003
      // (enforcing a "picture in, picture out" model). We must exit after
1004
      // retrieving one packet per frame, otherwise the next call blocks forever.
1005
0
      if (encoder->low_delay_mode && !done_sending_pics) {
1006
0
        break;
1007
0
      }
1008
0
    }
1009
0
  } while (res == EB_ErrorNone && !encode_at_eos);
1010
1011
1012
  // delete input_buffer.p_buffer;
1013
1014
0
  if (!done_sending_pics && ((res == EB_ErrorNone) || (res == EB_NoErrorEmptyQueue))) {
1015
0
    return heif_error_ok;
1016
0
  }
1017
0
  else {
1018
0
    return (res == EB_ErrorNone ? heif_error_ok : heif_error_codec_library_error);
1019
0
  }
1020
0
}
1021
1022
1023
static heif_error svt_encode_sequence_frame(void* encoder_raw, const heif_image* image, uintptr_t frame_nr)
1024
0
{
1025
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
1026
0
  EbComponentType*& svt_encoder = encoder->svt_encoder;
1027
0
  EbErrorType res = EB_ErrorNone;
1028
1029
0
  int w = heif_image_get_width(image, heif_channel_Y);
1030
0
  int h = heif_image_get_height(image, heif_channel_Y);
1031
0
  const heif_chroma chroma = heif_image_get_chroma_format(image);
1032
0
  int bitdepth_y = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
1033
1034
0
  uint32_t encoded_width, encoded_height;
1035
0
  svt_query_encoded_size(encoder_raw, w, h, &encoded_width, &encoded_height);
1036
1037
  // Note: it is ok to cast away the const, as the image content is not changed.
1038
  // However, we have to guarantee that there are no plane pointers or stride values kept over calling the svt_encode_image() function.
1039
0
  heif_error err = heif_image_extend_padding_to_size(const_cast<struct heif_image*>(image),
1040
0
                                                     (int) encoded_width,
1041
0
                                                     (int) encoded_height);
1042
0
  if (err.code) {
1043
0
    return err;
1044
0
  }
1045
1046
1047
0
  uint8_t yShift = 0;
1048
1049
0
  if (encoder->input_class == heif_image_input_class_alpha) {
1050
0
    yShift = 1;
1051
0
  }
1052
0
  else if (chroma == heif_chroma_420) {
1053
0
    yShift = 1;
1054
0
  }
1055
1056
1057
  // --- copy libheif image to svt image
1058
1059
0
  EbBufferHeaderType& input_buffer = encoder->input_buffer;
1060
1061
0
  auto svt_io_format = std::make_unique<EbSvtIOFormat>();
1062
0
  input_buffer.p_buffer = (uint8_t*) svt_io_format.get();
1063
0
  memset(input_buffer.p_buffer, 0, sizeof(EbSvtIOFormat));
1064
1065
0
  input_buffer.size = sizeof(EbBufferHeaderType);
1066
0
  input_buffer.p_app_private = nullptr;
1067
0
  input_buffer.pic_type = EB_AV1_INVALID_PICTURE;
1068
0
  input_buffer.metadata = nullptr;
1069
0
  input_buffer.pts = frame_nr;
1070
1071
  /* TODO: this does not seem to work (see all [1])
1072
  if (encoder->keyframe_interval) {
1073
    if (frame_nr % encoder->keyframe_interval == 0) {
1074
      input_buffer.pic_type = EB_AV1_KEY_PICTURE;
1075
    }
1076
  }
1077
  */
1078
1079
0
  auto* input_picture_buffer = (EbSvtIOFormat*) input_buffer.p_buffer;
1080
1081
0
  int bytesPerPixel = bitdepth_y > 8 ? 2 : 1;
1082
0
  std::vector<uint8_t> dummy_color_plane;
1083
0
  if (encoder->input_class == heif_image_input_class_alpha) {
1084
0
    size_t stride64;
1085
0
    input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Y, &stride64);
1086
1087
0
    uint32_t stride32;
1088
0
    if (stride64 > std::numeric_limits<uint32_t>::max()) {
1089
0
      return {
1090
0
        heif_error_Encoder_plugin_error,
1091
0
        heif_suberror_Unspecified,
1092
0
        "Image too wide for encoder"
1093
0
      };
1094
0
    }
1095
0
    else {
1096
0
      stride32 = static_cast<uint32_t>(stride64);
1097
0
    }
1098
1099
0
    input_picture_buffer->y_stride = stride32 / bytesPerPixel;
1100
0
    input_buffer.n_filled_len = stride32 * encoded_height;
1101
1102
0
    uint32_t uvWidth = get_subsampled_size_h(encoded_width, heif_channel_Cb, heif_chroma_420, scaling_mode::round_up);
1103
0
    uint32_t uvHeight = get_subsampled_size_v(encoded_height, heif_channel_Cb, heif_chroma_420, scaling_mode::round_up);
1104
0
    dummy_color_plane.resize(uvWidth * uvHeight * bytesPerPixel);
1105
1106
0
    if (bitdepth_y <= 8) {
1107
0
      uint8_t val = static_cast<uint8_t>(1 << (bitdepth_y - 1));
1108
0
      memset(dummy_color_plane.data(), val, uvWidth * uvHeight * bytesPerPixel);
1109
0
    }
1110
0
    else {
1111
0
      assert(bitdepth_y > 8 && bitdepth_y <= 16);
1112
0
      uint16_t val = static_cast<uint16_t>(1 << (bitdepth_y - 1));
1113
0
      uint8_t high = static_cast<uint8_t>((val >> 8) & 0xFF);
1114
0
      uint8_t low = static_cast<uint8_t>(val & 0xFF);
1115
1116
0
      for (uint32_t i = 0; i < uvWidth * uvHeight; i++) {
1117
0
        dummy_color_plane[2 * i] = low;
1118
0
        dummy_color_plane[2 * i + 1] = high;
1119
0
      }
1120
0
    }
1121
1122
0
    input_buffer.n_filled_len += 2 * uvWidth * uvHeight;
1123
0
    input_picture_buffer->cb_stride = uvWidth;
1124
0
    input_picture_buffer->cr_stride = uvWidth;
1125
1126
0
    input_picture_buffer->cb = dummy_color_plane.data();
1127
0
    input_picture_buffer->cr = dummy_color_plane.data();
1128
0
  }
1129
0
  else {
1130
0
    size_t stride64;
1131
0
    input_picture_buffer->luma = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Y, &stride64);
1132
1133
0
    uint32_t stride32;
1134
0
    if (stride64 > std::numeric_limits<uint32_t>::max()) {
1135
0
      return {
1136
0
        heif_error_Encoder_plugin_error,
1137
0
        heif_suberror_Unspecified,
1138
0
        "Image too wide for encoder"
1139
0
      };
1140
0
    }
1141
0
    else {
1142
0
      stride32 = static_cast<uint32_t>(stride64);
1143
0
    }
1144
1145
0
    input_picture_buffer->y_stride = stride32 / bytesPerPixel;
1146
0
    input_buffer.n_filled_len = stride32 * encoded_height;
1147
1148
0
    uint32_t uvHeight = (encoded_height + yShift) >> yShift;
1149
0
    input_picture_buffer->cb = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Cb, &stride64);
1150
0
    stride32 = static_cast<uint32_t>(stride64); // chroma stride should always be smaller than luma stride
1151
0
    input_buffer.n_filled_len += stride32 * uvHeight;
1152
0
    input_picture_buffer->cb_stride = stride32 / bytesPerPixel;
1153
1154
0
    input_picture_buffer->cr = (uint8_t*) heif_image_get_plane_readonly2(image, heif_channel_Cr, &stride64);
1155
0
    stride32 = static_cast<uint32_t>(stride64); // chroma stride should always be smaller than luma stride
1156
0
    input_buffer.n_filled_len += stride32 * uvHeight;
1157
0
    input_picture_buffer->cr_stride = stride32 / bytesPerPixel;
1158
0
  }
1159
1160
0
  input_buffer.flags = 0;
1161
  // input_buffer.pts = 0;
1162
1163
  //EbAv1PictureType frame_type = EB_AV1_KEY_PICTURE;
1164
  //input_buffer.pic_type = frame_type;
1165
1166
0
  res = svt_av1_enc_send_picture(svt_encoder, &input_buffer);
1167
0
  if (res != EB_ErrorNone) {
1168
    // input_buffer.p_buffer is owned by the svt_io_format unique_ptr, which frees it on return.
1169
0
    return heif_error_codec_library_error;
1170
0
  }
1171
1172
0
  return read_encoder_output_packets(encoder_raw, false);
1173
0
}
1174
1175
1176
static heif_error svt_end_sequence_encoding(void* encoder_raw)
1177
0
{
1178
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
1179
1180
  // In LOW_DELAY mode, all packets have already been retrieved during per-frame
1181
  // encoding (picture-in, picture-out model). No flush is needed, and sending an
1182
  // EOS buffer would deadlock because SVT-AV1's LOW_DELAY path does not release
1183
  // the internal resources allocated for the EOS picture.
1184
0
  if (encoder->low_delay_mode) {
1185
0
    return heif_error_ok;
1186
0
  }
1187
1188
0
  EbComponentType*& svt_encoder = encoder->svt_encoder;
1189
1190
  // --- flush encoder
1191
1192
0
  EbErrorType ret = EB_ErrorNone;
1193
1194
0
  EbBufferHeaderType flush_input_buffer;
1195
0
  flush_input_buffer.n_alloc_len = 0;
1196
0
  flush_input_buffer.n_filled_len = 0;
1197
0
  flush_input_buffer.n_tick_count = 0;
1198
0
  flush_input_buffer.p_app_private = nullptr;
1199
0
  flush_input_buffer.flags = EB_BUFFERFLAG_EOS;
1200
0
  flush_input_buffer.p_buffer = nullptr;
1201
0
  flush_input_buffer.metadata = nullptr;
1202
1203
0
  ret = svt_av1_enc_send_picture(svt_encoder, &flush_input_buffer);
1204
1205
0
  if (ret != EB_ErrorNone) {
1206
    // delete input_buffer.p_buffer;
1207
0
    return heif_error_codec_library_error;
1208
0
  }
1209
1210
0
  return read_encoder_output_packets(encoder_raw, true);
1211
0
}
1212
1213
1214
heif_error svt_get_compressed_data2(void* encoder_raw, uint8_t** data, int* size,
1215
                                    uintptr_t* out_framenr, int* out_is_keyframe,
1216
                                    int* more_frame_packets)
1217
0
{
1218
0
  auto* encoder = (encoder_struct_svt*) encoder_raw;
1219
1220
0
  encoder->active_output_data.clear();
1221
1222
0
  if (encoder->output_packets.empty()) {
1223
0
    *size = 0;
1224
0
    *data = nullptr;
1225
0
  }
1226
0
  else {
1227
0
    encoder->active_output_data = std::move(encoder->output_packets.front().compressedData);
1228
0
    if (out_framenr) {
1229
0
      *out_framenr = encoder->output_packets.front().frameNr;
1230
0
    }
1231
1232
0
    if (out_is_keyframe) {
1233
0
      *out_is_keyframe = encoder->output_packets.front().is_keyframe;
1234
0
    }
1235
1236
0
    encoder->output_packets.pop_front();
1237
1238
0
    *size = (int) encoder->active_output_data.size();
1239
0
    *data = encoder->active_output_data.data();
1240
0
  }
1241
1242
0
  return heif_error_ok;
1243
0
}
1244
1245
1246
heif_error svt_get_compressed_data(void* encoder_raw, uint8_t** data, int* size,
1247
                                   heif_encoded_data_type* type)
1248
0
{
1249
0
  return svt_get_compressed_data2(encoder_raw, data, size, nullptr, nullptr, nullptr);
1250
0
}
1251
1252
1253
static heif_error svt_start_sequence_encoding(void* encoder_raw, const heif_image* image,
1254
                                              enum heif_image_input_class input_class,
1255
                                              uint32_t framerate_num, uint32_t framerate_denom,
1256
                                              const heif_sequence_encoding_options* options)
1257
0
{
1258
0
  return svt_start_sequence_encoding_intern(encoder_raw, image, input_class,
1259
0
                                            framerate_num, framerate_denom, options, true);
1260
0
}
1261
1262
1263
heif_error svt_encode_image(void* encoder_raw, const heif_image* image,
1264
                            heif_image_input_class input_class)
1265
0
{
1266
0
  heif_error err;
1267
0
  err = svt_start_sequence_encoding_intern(encoder_raw, image, input_class, 1,25, nullptr, false);
1268
0
  if (err.code) {
1269
0
    return err;
1270
0
  }
1271
1272
0
  err = svt_encode_sequence_frame(encoder_raw, image, 0);
1273
0
  if (err.code) {
1274
0
    return err;
1275
0
  }
1276
1277
0
  err = svt_end_sequence_encoding(encoder_raw);
1278
0
  if (err.code) {
1279
0
    return err;
1280
0
  }
1281
1282
0
  return {};
1283
0
}
1284
1285
1286
static const heif_encoder_plugin encoder_plugin_svt
1287
{
1288
  /* plugin_api_version */ 4,
1289
                           /* compression_format */ heif_compression_AV1,
1290
                           /* id_name */ "svt",
1291
                           /* priority */ SVT_PLUGIN_PRIORITY,
1292
                           /* supports_lossy_compression */ true,
1293
#if SVT_AV1_CHECK_VERSION(3, 0, 0)
1294
                           /* supports_lossless_compression */ true,
1295
#else
1296
        /* supports_lossless_compression */ false,
1297
#endif
1298
                           /* get_plugin_name */ svt_plugin_name,
1299
                           /* init_plugin */ svt_init_plugin,
1300
                           /* cleanup_plugin */ svt_cleanup_plugin,
1301
                           /* new_encoder */ svt_new_encoder,
1302
                           /* free_encoder */ svt_free_encoder,
1303
                           /* set_parameter_quality */ svt_set_parameter_quality,
1304
                           /* get_parameter_quality */ svt_get_parameter_quality,
1305
                           /* set_parameter_lossless */ svt_set_parameter_lossless,
1306
                           /* get_parameter_lossless */ svt_get_parameter_lossless,
1307
                           /* set_parameter_logging_level */ svt_set_parameter_logging_level,
1308
                           /* get_parameter_logging_level */ svt_get_parameter_logging_level,
1309
                           /* list_parameters */ svt_list_parameters,
1310
                           /* set_parameter_integer */ svt_set_parameter_integer,
1311
                           /* get_parameter_integer */ svt_get_parameter_integer,
1312
                           /* set_parameter_boolean */ svt_set_parameter_boolean,
1313
                           /* get_parameter_boolean */ svt_get_parameter_boolean,
1314
                           /* set_parameter_string */ svt_set_parameter_string,
1315
                           /* get_parameter_string */ svt_get_parameter_string,
1316
                           /* query_input_colorspace */ svt_query_input_colorspace,
1317
                           /* encode_image */ svt_encode_image,
1318
                           /* get_compressed_data */ svt_get_compressed_data,
1319
                           /* query_input_colorspace (v2) */ svt_query_input_colorspace2,
1320
                           /* query_encoded_size (v3) */ svt_query_encoded_size,
1321
                           /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,21,0),
1322
                           /* start_sequence_encoding (v4) */ svt_start_sequence_encoding,
1323
                           /* encode_sequence_frame (v4) */ svt_encode_sequence_frame,
1324
                           /* end_sequence_encoding (v4) */ svt_end_sequence_encoding,
1325
                           /* get_compressed_data2 (v4) */ svt_get_compressed_data2,
1326
                           /* does_indicate_keyframes (v4) */ 1
1327
};
1328
1329
const heif_encoder_plugin* get_encoder_plugin_svt()
1330
3
{
1331
3
  return &encoder_plugin_svt;
1332
3
}
1333
1334
1335
#if PLUGIN_SvtEnc
1336
heif_plugin_info plugin_info{
1337
    1,
1338
    heif_plugin_type_encoder,
1339
    &encoder_plugin_svt
1340
};
1341
#endif