Coverage Report

Created: 2026-06-15 06:25

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