Coverage Report

Created: 2026-10-28 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/plugins/encoder_x265.cc
Line
Count
Source
1
/*
2
 * HEIF codec.
3
 * Copyright (c) 2017 Dirk Farin <dirk.farin@gmail.com>
4
 *
5
 * This file is part of libheif.
6
 *
7
 * libheif is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Lesser General Public License as
9
 * published by the Free Software Foundation, either version 3 of
10
 * the License, or (at your option) any later version.
11
 *
12
 * libheif is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public License
18
 * along with libheif.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#include "libheif/heif.h"
22
#include "libheif/heif_plugin.h"
23
#include "encoder_x265.h"
24
#include "encoder_input_check.h"
25
#include <memory>
26
#include <sstream>
27
#include <string>
28
#include <cstring>
29
#include <cstdio>
30
#include <cassert>
31
#include <deque>
32
#include <vector>
33
#include <utility>
34
35
extern "C" {
36
#include <x265.h>
37
}
38
39
40
static const char* kError_unsupported_bit_depth = "Bit depth not supported by x265";
41
static const char* kError_unsupported_image_size = "Images smaller than 16 pixels are not supported";
42
static const char* kError_unsupported_ctu_size = "Unsupported CTU size";
43
44
45
enum parameter_type
46
{
47
  UndefinedType, Int, Bool, String
48
};
49
50
struct parameter
51
{
52
  parameter_type type = UndefinedType;
53
  std::string name;
54
55
  int value_int = 0; // also used for boolean
56
  std::string value_string;
57
};
58
59
60
struct encoder_struct_x265
61
{
62
  x265_encoder* encoder = nullptr;
63
  const x265_api* api = nullptr;
64
  x265_param* param = nullptr;
65
66
  int bit_depth = 0;
67
  int input_width = 0;
68
  int input_height = 0;
69
70
  heif_chroma chroma;
71
72
73
  // --- output
74
75
  struct Packet
76
  {
77
    std::vector<uint8_t> data;
78
    uintptr_t frameNr = 0;
79
  };
80
81
  std::deque<Packet> output_data;
82
  std::vector<uint8_t> active_output_nal;
83
84
  void append_nal_packets(x265_nal* nals, uint32_t num_nals, uintptr_t frameNr);
85
86
  // --- parameters
87
88
  std::vector<parameter> parameters;
89
90
  void add_param(const parameter&);
91
92
  void add_param(const std::string& name, int value);
93
94
  void add_param(const std::string& name, bool value);
95
96
  void add_param(const std::string& name, const std::string& value);
97
98
  parameter get_param(const std::string& name) const;
99
100
  std::string preset;
101
  std::string tune;
102
103
  int logLevel = X265_LOG_NONE;
104
105
  std::string last_error_message;
106
};
107
108
109
void encoder_struct_x265::add_param(const parameter& p)
110
0
{
111
  // if there is already a parameter of that name, remove it from list
112
113
0
  for (size_t i = 0; i < parameters.size(); i++) {
114
0
    if (parameters[i].name == p.name) {
115
0
      for (size_t k = i + 1; k < parameters.size(); k++) {
116
0
        parameters[k - 1] = parameters[k];
117
0
      }
118
0
      parameters.pop_back();
119
0
      break;
120
0
    }
121
0
  }
122
123
  // and add the new parameter at the end of the list
124
125
0
  parameters.push_back(p);
126
0
}
127
128
129
void encoder_struct_x265::add_param(const std::string& name, int value)
130
0
{
131
0
  parameter p;
132
0
  p.type = Int;
133
0
  p.name = name;
134
0
  p.value_int = value;
135
0
  add_param(p);
136
0
}
137
138
void encoder_struct_x265::add_param(const std::string& name, bool value)
139
0
{
140
0
  parameter p;
141
0
  p.type = Bool;
142
0
  p.name = name;
143
0
  p.value_int = value;
144
0
  add_param(p);
145
0
}
146
147
void encoder_struct_x265::add_param(const std::string& name, const std::string& value)
148
0
{
149
0
  parameter p;
150
0
  p.type = String;
151
0
  p.name = name;
152
0
  p.value_string = value;
153
0
  add_param(p);
154
0
}
155
156
157
parameter encoder_struct_x265::get_param(const std::string& name) const
158
0
{
159
0
  for (size_t i = 0; i < parameters.size(); i++) {
160
0
    if (parameters[i].name == name) {
161
0
      return parameters[i];
162
0
    }
163
0
  }
164
165
0
  return parameter();
166
0
}
167
168
169
static const char* kParam_preset = "preset";
170
static const char* kParam_tune = "tune";
171
static const char* kParam_TU_intra_depth = "tu-intra-depth";
172
static const char* kParam_complexity = "complexity";
173
174
static const char* const kParam_preset_valid_values[] = {
175
    "ultrafast", "superfast", "veryfast", "faster", "fast", "medium",
176
    "slow", "slower", "veryslow", "placebo", nullptr
177
};
178
179
static const char* const kParam_tune_valid_values[] = {
180
    "psnr", "ssim", "grain", "fastdecode", nullptr
181
    // note: zerolatency is missing, because we do not need it for single images
182
};
183
184
static const char* kParam_chroma = "chroma";
185
static const char* const kParam_chroma_valid_values[] = {
186
    "420", "422", "444", nullptr
187
};
188
189
190
static const int X265_PLUGIN_PRIORITY = 100;
191
192
0
#define MAX_PLUGIN_NAME_LENGTH 80
193
194
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
195
196
197
static void x265_set_default_parameters(void* encoder);
198
199
200
static const char* x265_plugin_name()
201
0
{
202
0
  strcpy(plugin_name, "x265 HEVC encoder");
203
204
0
  const x265_api* api = x265_api_get(0);
205
0
  const char* x265_version = ((api != nullptr && api->version_str != nullptr) ? api->version_str : "null");
206
207
0
  if (strlen(x265_version) + strlen(plugin_name) + 4 < MAX_PLUGIN_NAME_LENGTH) {
208
0
    strcat(plugin_name, " (");
209
0
    strcat(plugin_name, x265_version);
210
0
    strcat(plugin_name, ")");
211
0
  }
212
213
0
  return plugin_name;
214
0
}
215
216
217
#define MAX_NPARAMETERS 10
218
219
static heif_encoder_parameter x265_encoder_params[MAX_NPARAMETERS];
220
static const heif_encoder_parameter* x265_encoder_parameter_ptrs[MAX_NPARAMETERS + 1];
221
222
static void x265_init_parameters()
223
254
{
224
254
  heif_encoder_parameter* p = x265_encoder_params;
225
254
  const heif_encoder_parameter** d = x265_encoder_parameter_ptrs;
226
254
  int i = 0;
227
228
254
  assert(i < MAX_NPARAMETERS);
229
254
  p->version = 2;
230
254
  p->name = heif_encoder_parameter_name_quality;
231
254
  p->type = heif_encoder_parameter_type_integer;
232
254
  p->integer.default_value = 50;
233
254
  p->has_default = true;
234
254
  p->integer.have_minimum_maximum = true;
235
254
  p->integer.minimum = 0;
236
254
  p->integer.maximum = 100;
237
254
  p->integer.valid_values = NULL;
238
254
  p->integer.num_valid_values = 0;
239
254
  d[i++] = p++;
240
241
254
  assert(i < MAX_NPARAMETERS);
242
254
  p->version = 2;
243
254
  p->name = heif_encoder_parameter_name_lossless;
244
254
  p->type = heif_encoder_parameter_type_boolean;
245
254
  p->boolean.default_value = false;
246
254
  p->has_default = true;
247
254
  d[i++] = p++;
248
249
254
  assert(i < MAX_NPARAMETERS);
250
254
  p->version = 2;
251
254
  p->name = kParam_preset;
252
254
  p->type = heif_encoder_parameter_type_string;
253
254
  p->string.default_value = "slow";  // increases computation time
254
254
  p->has_default = true;
255
254
  p->string.valid_values = kParam_preset_valid_values;
256
254
  d[i++] = p++;
257
258
254
  assert(i < MAX_NPARAMETERS);
259
254
  p->version = 2;
260
254
  p->name = kParam_tune;
261
254
  p->type = heif_encoder_parameter_type_string;
262
254
  p->string.default_value = "ssim";
263
254
  p->has_default = true;
264
254
  p->string.valid_values = kParam_tune_valid_values;
265
254
  d[i++] = p++;
266
267
254
  assert(i < MAX_NPARAMETERS);
268
254
  p->version = 2;
269
254
  p->name = kParam_TU_intra_depth;
270
254
  p->type = heif_encoder_parameter_type_integer;
271
254
  p->integer.default_value = 2;  // increases computation time
272
254
  p->has_default = true;
273
254
  p->integer.have_minimum_maximum = true;
274
254
  p->integer.minimum = 1;
275
254
  p->integer.maximum = 4;
276
254
  p->integer.valid_values = NULL;
277
254
  p->integer.num_valid_values = 0;
278
254
  d[i++] = p++;
279
280
254
  assert(i < MAX_NPARAMETERS);
281
254
  p->version = 2;
282
254
  p->name = kParam_complexity;
283
254
  p->type = heif_encoder_parameter_type_integer;
284
254
  p->integer.default_value = 50;
285
254
  p->has_default = false;
286
254
  p->integer.have_minimum_maximum = true;
287
254
  p->integer.minimum = 0;
288
254
  p->integer.maximum = 100;
289
254
  p->integer.valid_values = NULL;
290
254
  p->integer.num_valid_values = 0;
291
254
  d[i++] = p++;
292
293
254
  assert(i < MAX_NPARAMETERS);
294
254
  p->version = 2;
295
254
  p->name = kParam_chroma;
296
254
  p->type = heif_encoder_parameter_type_string;
297
254
  p->string.default_value = "420";
298
254
  p->has_default = true;
299
254
  p->string.valid_values = kParam_chroma_valid_values;
300
254
  d[i++] = p++;
301
302
254
  d[i++] = nullptr;
303
254
}
304
305
306
const heif_encoder_parameter** x265_list_parameters(void* encoder)
307
0
{
308
0
  return x265_encoder_parameter_ptrs;
309
0
}
310
311
312
static void x265_init_plugin()
313
254
{
314
254
  x265_init_parameters();
315
254
}
316
317
318
static void x265_cleanup_plugin()
319
0
{
320
0
  x265_cleanup();
321
0
}
322
323
324
static heif_error x265_new_encoder(void** enc)
325
0
{
326
0
  encoder_struct_x265* encoder = new encoder_struct_x265();
327
0
  heif_error err = heif_error_ok;
328
329
330
  // encoder has to be allocated in x265_encode_image, because it needs to know the image size
331
0
  encoder->encoder = nullptr;
332
333
0
  encoder->bit_depth = 8;
334
335
0
  *enc = encoder;
336
337
338
  // set default parameters
339
340
0
  x265_set_default_parameters(encoder);
341
342
0
  return err;
343
0
}
344
345
static void x265_free_encoder(void* encoder_raw)
346
0
{
347
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
348
349
0
  if (encoder->encoder) {
350
0
    const x265_api* api = x265_api_get(encoder->bit_depth);
351
0
    api->encoder_close(encoder->encoder);
352
0
  }
353
354
0
  if (encoder->param && encoder->api) {
355
0
    encoder->api->param_free(encoder->param);
356
0
  }
357
358
0
  delete encoder;
359
0
}
360
361
static heif_error x265_set_parameter_quality(void* encoder_raw, int quality)
362
0
{
363
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
364
365
0
  if (quality < 0 || quality > 100) {
366
0
    return heif_error_invalid_parameter_value;
367
0
  }
368
369
0
  encoder->add_param(heif_encoder_parameter_name_quality, quality);
370
371
0
  return heif_error_ok;
372
0
}
373
374
static heif_error x265_get_parameter_quality(void* encoder_raw, int* quality)
375
0
{
376
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
377
378
0
  parameter p = encoder->get_param(heif_encoder_parameter_name_quality);
379
0
  *quality = p.value_int;
380
381
0
  return heif_error_ok;
382
0
}
383
384
static heif_error x265_set_parameter_lossless(void* encoder_raw, int enable)
385
0
{
386
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
387
388
0
  encoder->add_param(heif_encoder_parameter_name_lossless, (bool) enable);
389
390
0
  return heif_error_ok;
391
0
}
392
393
static heif_error x265_get_parameter_lossless(void* encoder_raw, int* enable)
394
0
{
395
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
396
397
0
  parameter p = encoder->get_param(heif_encoder_parameter_name_lossless);
398
0
  *enable = p.value_int;
399
400
0
  return heif_error_ok;
401
0
}
402
403
static heif_error x265_set_parameter_logging_level(void* encoder_raw, int logging)
404
0
{
405
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
406
407
0
  if (logging < 0 || logging > 4) {
408
0
    return heif_error_invalid_parameter_value;
409
0
  }
410
411
0
  encoder->logLevel = logging;
412
413
0
  return heif_error_ok;
414
0
}
415
416
static heif_error x265_get_parameter_logging_level(void* encoder_raw, int* loglevel)
417
0
{
418
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
419
420
0
  *loglevel = encoder->logLevel;
421
422
0
  return heif_error_ok;
423
0
}
424
425
426
static heif_error x265_set_parameter_integer(void* encoder_raw, const char* name, int value)
427
0
{
428
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
429
430
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
431
0
    return x265_set_parameter_quality(encoder, value);
432
0
  }
433
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
434
0
    return x265_set_parameter_lossless(encoder, value);
435
0
  }
436
0
  else if (strcmp(name, kParam_TU_intra_depth) == 0) {
437
0
    if (value < 1 || value > 4) {
438
0
      return heif_error_invalid_parameter_value;
439
0
    }
440
441
0
    encoder->add_param(name, value);
442
0
    return heif_error_ok;
443
0
  }
444
0
  else if (strcmp(name, kParam_complexity) == 0) {
445
0
    if (value < 0 || value > 100) {
446
0
      return heif_error_invalid_parameter_value;
447
0
    }
448
449
0
    encoder->add_param(name, value);
450
0
    return heif_error_ok;
451
0
  }
452
453
0
  return heif_error_unsupported_parameter;
454
0
}
455
456
static heif_error x265_get_parameter_integer(void* encoder_raw, const char* name, int* value)
457
0
{
458
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
459
460
0
  if (strcmp(name, heif_encoder_parameter_name_quality) == 0) {
461
0
    return x265_get_parameter_quality(encoder, value);
462
0
  }
463
0
  else if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
464
0
    return x265_get_parameter_lossless(encoder, value);
465
0
  }
466
0
  else if (strcmp(name, kParam_TU_intra_depth) == 0) {
467
0
    *value = encoder->get_param(name).value_int;
468
0
    return heif_error_ok;
469
0
  }
470
0
  else if (strcmp(name, kParam_complexity) == 0) {
471
0
    *value = encoder->get_param(name).value_int;
472
0
    return heif_error_ok;
473
0
  }
474
475
0
  return heif_error_unsupported_parameter;
476
0
}
477
478
479
static heif_error x265_set_parameter_boolean(void* encoder, const char* name, int value)
480
0
{
481
0
  if (strcmp(name, heif_encoder_parameter_name_lossless) == 0) {
482
0
    return x265_set_parameter_lossless(encoder, value);
483
0
  }
484
485
0
  return heif_error_unsupported_parameter;
486
0
}
487
488
// Unused, will use "x265_get_parameter_integer" instead.
489
/*
490
static struct heif_error x265_get_parameter_boolean(void* encoder, const char* name, int* value)
491
{
492
  if (strcmp(name, heif_encoder_parameter_name_lossless)==0) {
493
    return x265_get_parameter_lossless(encoder,value);
494
  }
495
496
  return heif_error_unsupported_parameter;
497
}
498
*/
499
500
501
static bool string_list_contains(const char* const* values_list, const char* value)
502
0
{
503
0
  for (int i = 0; values_list[i]; i++) {
504
0
    if (strcmp(values_list[i], value) == 0) {
505
0
      return true;
506
0
    }
507
0
  }
508
509
0
  return false;
510
0
}
511
512
513
static heif_error x265_set_parameter_string(void* encoder_raw, const char* name, const char* value)
514
0
{
515
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
516
517
0
  if (strcmp(name, kParam_preset) == 0) {
518
0
    if (!string_list_contains(kParam_preset_valid_values, value)) {
519
0
      return heif_error_invalid_parameter_value;
520
0
    }
521
522
0
    encoder->preset = value;
523
0
    return heif_error_ok;
524
0
  }
525
0
  else if (strcmp(name, kParam_tune) == 0) {
526
0
    if (!string_list_contains(kParam_tune_valid_values, value)) {
527
0
      return heif_error_invalid_parameter_value;
528
0
    }
529
530
0
    encoder->tune = value;
531
0
    return heif_error_ok;
532
0
  }
533
0
  else if (strncmp(name, "x265:", 5) == 0) {
534
0
    encoder->add_param(name, std::string(value));
535
0
    return heif_error_ok;
536
0
  }
537
0
  else if (strcmp(name, kParam_chroma) == 0) {
538
0
    if (strcmp(value, "420") == 0) {
539
0
      encoder->chroma = heif_chroma_420;
540
0
      return heif_error_ok;
541
0
    }
542
0
    else if (strcmp(value, "422") == 0) {
543
0
      encoder->chroma = heif_chroma_422;
544
0
      return heif_error_ok;
545
0
    }
546
0
    else if (strcmp(value, "444") == 0) {
547
0
      encoder->chroma = heif_chroma_444;
548
0
      return heif_error_ok;
549
0
    }
550
0
    else {
551
0
      return heif_error_invalid_parameter_value;
552
0
    }
553
0
  }
554
555
0
  return heif_error_unsupported_parameter;
556
0
}
557
558
static void save_strcpy(char* dst, int dst_size, const char* src)
559
0
{
560
0
  strncpy(dst, src, dst_size - 1);
561
0
  dst[dst_size - 1] = 0;
562
0
}
563
564
static heif_error x265_get_parameter_string(void* encoder_raw, const char* name,
565
                                            char* value, int value_size)
566
0
{
567
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
568
569
0
  if (strcmp(name, kParam_preset) == 0) {
570
0
    save_strcpy(value, value_size, encoder->preset.c_str());
571
0
    return heif_error_ok;
572
0
  }
573
0
  else if (strcmp(name, kParam_tune) == 0) {
574
0
    save_strcpy(value, value_size, encoder->tune.c_str());
575
0
    return heif_error_ok;
576
0
  }
577
0
  else if (strcmp(name, kParam_chroma) == 0) {
578
0
    switch (encoder->chroma) {
579
0
      case heif_chroma_420:
580
0
        save_strcpy(value, value_size, "420");
581
0
        break;
582
0
      case heif_chroma_422:
583
0
        save_strcpy(value, value_size, "422");
584
0
        break;
585
0
      case heif_chroma_444:
586
0
        save_strcpy(value, value_size, "444");
587
0
        break;
588
0
      default:
589
0
        assert(false);
590
0
        return heif_error_invalid_parameter_value;
591
0
    }
592
0
    return heif_error_ok;
593
0
  }
594
595
0
  return heif_error_unsupported_parameter;
596
0
}
597
598
599
static void x265_set_default_parameters(void* encoder)
600
0
{
601
0
  for (const heif_encoder_parameter** p = x265_encoder_parameter_ptrs; *p; p++) {
602
0
    const heif_encoder_parameter* param = *p;
603
604
0
    if (param->has_default) {
605
0
      switch (param->type) {
606
0
        case heif_encoder_parameter_type_integer:
607
0
          x265_set_parameter_integer(encoder, param->name, param->integer.default_value);
608
0
          break;
609
0
        case heif_encoder_parameter_type_boolean:
610
0
          x265_set_parameter_boolean(encoder, param->name, param->boolean.default_value);
611
0
          break;
612
0
        case heif_encoder_parameter_type_string:
613
0
          x265_set_parameter_string(encoder, param->name, param->string.default_value);
614
0
          break;
615
0
      }
616
0
    }
617
0
  }
618
0
}
619
620
621
static void x265_query_input_colorspace(heif_colorspace* colorspace, heif_chroma* chroma)
622
0
{
623
0
  if (*colorspace == heif_colorspace_monochrome) {
624
0
    *colorspace = heif_colorspace_monochrome;
625
0
    *chroma = heif_chroma_monochrome;
626
0
  }
627
0
  else {
628
0
    *colorspace = heif_colorspace_YCbCr;
629
0
    *chroma = heif_chroma_420;
630
0
  }
631
0
}
632
633
634
static void x265_query_input_colorspace2(void* encoder_raw, heif_colorspace* colorspace, heif_chroma* chroma)
635
0
{
636
0
  auto* encoder = (encoder_struct_x265*) encoder_raw;
637
638
0
  if (*colorspace == heif_colorspace_monochrome) {
639
0
    *colorspace = heif_colorspace_monochrome;
640
0
    *chroma = heif_chroma_monochrome;
641
0
  }
642
0
  else {
643
0
    *colorspace = heif_colorspace_YCbCr;
644
0
    *chroma = encoder->chroma;
645
0
  }
646
0
}
647
648
649
static int rounded_size(int s)
650
0
{
651
0
  s = (s + 1) & ~1;
652
653
0
  if (s < 64) {
654
0
    s = 64;
655
0
  }
656
657
0
  return s;
658
0
}
659
660
#if 0
661
static const char* naltype_table[] = {
662
  /*  0 */ "TRAIL_N",
663
  /*  1 */ "TRAIL_R",
664
  /*  2 */ "TSA_N",
665
  /*  3 */ "TSA_R",
666
  /*  4 */ "STSA_N",
667
  /*  5 */ "STSA_R",
668
  /*  6 */ "RADL_N",
669
  /*  7 */ "RADL_R",
670
  /*  8 */ "RASL_N",
671
  /*  9 */ "RASL_R",
672
  /* 10 */ "RSV_VCL_N10",
673
  /* 11 */ "RSV_VCL_R11",
674
  /* 12 */ "RSV_VCL_N12",
675
  /* 13 */ "RSV_VCL_R13",
676
  /* 14 */ "RSV_VCL_N14",
677
  /* 15 */ "RSV_VCL_R15",
678
  /* 16 */ "BLA_W_LP",
679
  /* 17 */ "BLA_W_RADL",
680
  /* 18 */ "BLA_N_LP",
681
  /* 19 */ "IDR_W_RADL",
682
  /* 20 */ "IDR_N_LP",
683
  /* 21 */ "CRA_NUT",
684
  /* 22 */ "RSV_IRAP_VCL22",
685
  /* 23 */ "RSV_IRAP_VCL23",
686
  /* 24 */ "RSV_VCL24",
687
  /* 25 */ "RSV_VCL25",
688
  /* 26 */ "RSV_VCL26",
689
  /* 27 */ "RSV_VCL27",
690
  /* 28 */ "RSV_VCL28",
691
  /* 29 */ "RSV_VCL29",
692
  /* 30 */ "RSV_VCL30",
693
  /* 31 */ "RSV_VCL31",
694
  /* 32 */ "VPS_NUT",
695
  /* 33 */ "SPS_NUT",
696
  /* 34 */ "PPS_NUT",
697
  /* 35 */ "AUD_NUT",
698
  /* 36 */ "EOS_NUT",
699
  /* 37 */ "EOB_NUT",
700
  /* 38 */ "FD_NUT",
701
  /* 39 */ "PREFIX_SEI_NUT",
702
  /* 40 */ "SUFFIX_SEI_NUT"
703
};
704
705
706
static const char* naltype(uint8_t type)
707
{
708
  if (type <= 40) {
709
    return naltype_table[type];
710
  }
711
  else {
712
    return "reserved";
713
  }
714
}
715
#endif
716
717
void encoder_struct_x265::append_nal_packets(x265_nal* nals, uint32_t num_nals, uintptr_t frameNr)
718
0
{
719
0
  for (uint32_t nal_idx=0 ; nal_idx < num_nals; nal_idx++) {
720
0
    uint8_t* data = nals[nal_idx].payload;
721
0
    uint32_t size = nals[nal_idx].sizeBytes;
722
723
    // --- skip start code ---
724
725
    // skip '0' bytes
726
0
    while (*data == 0 && size > 0) {
727
0
      data++;
728
0
      size--;
729
0
    }
730
731
    // skip '1' byte
732
0
    data++;
733
0
    size--;
734
735
736
    // --- skip NALs with irrelevant data ---
737
738
0
    if (size >= 3 && data[0] == 0x4e && data[2] == 5) {
739
      // skip "unregistered user data SEI"
740
0
    }
741
0
    else {
742
      // output NAL
743
744
0
      Packet pkt;
745
0
      pkt.data.resize(size);
746
0
      memcpy(pkt.data.data(), data, size);
747
0
      pkt.frameNr = frameNr;
748
749
0
      output_data.push_back(pkt);
750
0
    }
751
0
  }
752
0
}
753
754
755
static heif_error x265_start_sequence_encoding_intern(void* encoder_raw, const heif_image* image,
756
                                       enum heif_image_input_class input_class,
757
                                       uint32_t framerate_num, uint32_t framerate_denom,
758
                                       const heif_sequence_encoding_options* options,
759
                                       bool image_sequence)
760
0
{
761
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
762
763
764
    // close previous encoder if there is still one hanging around
765
0
  if (encoder->encoder) {
766
0
    const x265_api* api = x265_api_get(encoder->bit_depth);
767
0
    api->encoder_close(encoder->encoder);
768
0
    encoder->encoder = nullptr;
769
0
  }
770
771
772
0
  int bit_depth = heif_image_get_bits_per_pixel_range(image, heif_channel_Y);
773
0
  bool isGreyscale = (heif_image_get_colorspace(image) == heif_colorspace_monochrome);
774
0
  heif_chroma chroma = heif_image_get_chroma_format(image);
775
776
0
  const x265_api* api = x265_api_get(bit_depth);
777
0
  encoder->api = api;
778
0
  if (api == nullptr) {
779
0
    struct heif_error err = {
780
0
        heif_error_Encoder_plugin_error,
781
0
        heif_suberror_Unsupported_bit_depth,
782
0
        kError_unsupported_bit_depth
783
0
    };
784
0
    return err;
785
0
  }
786
787
0
  x265_param* param = api->param_alloc();
788
0
  if (encoder->param) {
789
0
    api->param_free(encoder->param);
790
0
  }
791
0
  encoder->param = param;
792
793
0
  api->param_default_preset(param, encoder->preset.c_str(), encoder->tune.c_str());
794
795
0
  if (bit_depth == 8) {
796
0
    if (image_sequence) {
797
0
      api->param_apply_profile(param, "main");
798
0
    }
799
0
    else {
800
0
      api->param_apply_profile(param, "mainstillpicture");
801
0
    }
802
0
  }
803
0
  else if (bit_depth == 10) api->param_apply_profile(param, "main10-intra");
804
0
  else if (bit_depth == 12) api->param_apply_profile(param, "main12-intra");
805
0
  else {
806
0
    api->param_free(param);
807
0
    return heif_error_unsupported_parameter;
808
0
  }
809
810
811
  // x265 cannot encode images smaller than one CTU size
812
  // https://bitbucket.org/multicoreware/x265/issues/475/x265-does-not-allow-image-sizes-smaller
813
  // -> use smaller CTU sizes for very small images
814
0
  const char* ctu = NULL;
815
0
  int ctuSize = 64;
816
817
0
#if 1
818
0
  while (ctuSize > 16 &&
819
0
         (heif_image_get_width(image, heif_channel_Y) < ctuSize ||
820
0
          heif_image_get_height(image, heif_channel_Y) < ctuSize)) {
821
0
    ctuSize /= 2;
822
0
  }
823
824
0
  if (ctuSize < 16) {
825
0
    api->param_free(param);
826
0
    return {
827
0
        heif_error_Encoder_plugin_error,
828
0
        heif_suberror_Invalid_parameter_value,
829
0
        kError_unsupported_image_size
830
0
    };
831
0
  }
832
#else
833
  // TODO: There seems to be a bug in x265 where increasing the CTU size between
834
  // multiple encoding jobs causes a segmentation fault. E.g. encoding multiple
835
  // times with a CTU of 16 works, the next encoding with a CTU of 32 crashes.
836
  // Use hardcoded value of 64 and reject images that are too small.
837
838
  if (heif_image_get_width(image, heif_channel_Y) < ctuSize ||
839
      heif_image_get_height(image, heif_channel_Y) < ctuSize) {
840
    api->param_free(param);
841
    struct heif_error err = {
842
      heif_error_Encoder_plugin_error,
843
      heif_suberror_Invalid_parameter_value,
844
      kError_unsupported_image_size
845
    };
846
    return err;
847
  }
848
#endif
849
850
  // ctuSize should be a power of 2 in [16;64]
851
0
  switch (ctuSize) {
852
0
    case 64:
853
0
      ctu = "64";
854
0
      break;
855
0
    case 32:
856
0
      ctu = "32";
857
0
      break;
858
0
    case 16:
859
0
      ctu = "16";
860
0
      break;
861
0
    default:
862
0
      return {
863
0
          heif_error_Encoder_plugin_error,
864
0
          heif_suberror_Invalid_parameter_value,
865
0
          kError_unsupported_ctu_size
866
0
      };
867
0
  }
868
869
0
  if (options) {
870
0
    if (options->keyframe_distance_min) {
871
0
      param->keyframeMin = options->keyframe_distance_min;
872
0
    }
873
874
0
    if (options->keyframe_distance_max) {
875
0
      param->keyframeMax = options->keyframe_distance_max;
876
0
    }
877
878
0
    switch (options->gop_structure) {
879
0
      case heif_sequence_gop_structure_intra_only:
880
0
        param->bframes = 0;
881
0
        param->keyframeMin=1;
882
0
        param->keyframeMax=1;
883
0
        break;
884
0
      case heif_sequence_gop_structure_lowdelay:
885
0
        param->bframes = 0;
886
0
        break;
887
0
      case heif_sequence_gop_structure_unrestricted:
888
0
        param->bframes = 1;
889
0
        break;
890
0
    }
891
0
  }
892
893
  // BPG uses CQP. It does not seem to be better though.
894
  //  param->rc.rateControlMode = X265_RC_CQP;
895
  //  param->rc.qp = (100 - encoder->quality)/2;
896
0
  param->totalFrames = image_sequence ? 0 : 1; // TODO
897
898
0
  if (isGreyscale) {
899
0
    param->internalCsp = X265_CSP_I400;
900
0
  }
901
0
  else if (chroma == heif_chroma_420) {
902
0
    param->internalCsp = X265_CSP_I420;
903
0
  }
904
0
  else if (chroma == heif_chroma_422) {
905
0
    param->internalCsp = X265_CSP_I422;
906
0
  }
907
0
  else if (chroma == heif_chroma_444) {
908
0
    param->internalCsp = X265_CSP_I444;
909
0
  }
910
911
0
  if (chroma != heif_chroma_monochrome) {
912
0
    int w = heif_image_get_width(image, heif_channel_Y);
913
0
    int h = heif_image_get_height(image, heif_channel_Y);
914
0
    if (chroma != heif_chroma_444) { w = (w + 1) / 2; }
915
0
    if (chroma == heif_chroma_420) { h = (h + 1) / 2; }
916
917
0
    assert(heif_image_get_width(image, heif_channel_Cb)==w);
918
0
    assert(heif_image_get_width(image, heif_channel_Cr)==w);
919
0
    assert(heif_image_get_height(image, heif_channel_Cb)==h);
920
0
    assert(heif_image_get_height(image, heif_channel_Cr)==h);
921
0
    (void) w;
922
0
    (void) h;
923
0
  }
924
925
0
  api->param_parse(param, "info", "0");
926
0
  api->param_parse(param, "limit-modes", "0");
927
0
  api->param_parse(param, "limit-refs", "0");
928
0
  api->param_parse(param, "ctu", ctu);
929
0
  api->param_parse(param, "rskip", "0");
930
931
0
  api->param_parse(param, "rect", "1");
932
0
  api->param_parse(param, "amp", "1");
933
0
  api->param_parse(param, "aq-mode", "1");
934
0
  api->param_parse(param, "psy-rd", "1.0");
935
0
  api->param_parse(param, "psy-rdoq", "1.0");
936
937
0
  heif_color_profile_nclx* nclx = nullptr;
938
0
  heif_error err = heif_image_get_nclx_color_profile(image, &nclx);
939
0
  if (err.code != heif_error_Ok) {
940
0
    assert(nclx == nullptr);
941
0
  }
942
943
  // make sure NCLX profile is deleted at end of function
944
0
  auto nclx_deleter = std::unique_ptr<heif_color_profile_nclx, void (*)(heif_color_profile_nclx*)>(nclx, heif_nclx_color_profile_free);
945
946
0
  if (nclx) {
947
0
    api->param_parse(param, "range", nclx->full_range_flag ? "full" : "limited");
948
0
  }
949
0
  else {
950
0
    api->param_parse(param, "range", "full");
951
0
  }
952
953
0
  if (nclx &&
954
0
      (input_class == heif_image_input_class_normal ||
955
0
       input_class == heif_image_input_class_thumbnail)) {
956
957
0
    {
958
0
      std::stringstream sstr;
959
0
      sstr << nclx->color_primaries;
960
0
      api->param_parse(param, "colorprim", sstr.str().c_str());
961
0
    }
962
963
0
    {
964
0
      std::stringstream sstr;
965
0
      sstr << nclx->transfer_characteristics;
966
0
      api->param_parse(param, "transfer", sstr.str().c_str());
967
0
    }
968
969
0
    {
970
0
      std::stringstream sstr;
971
0
      sstr << nclx->matrix_coefficients;
972
0
      api->param_parse(param, "colormatrix", sstr.str().c_str());
973
0
    }
974
0
  }
975
976
  // Write the pixel aspect ratio into the VUI. Extended_SAR stores sar_width/sar_height
977
  // as u(16), so ratios that do not fit are only signalled through the pasp property.
978
  // A 1:1 ratio is omitted (VUI absence already means unspecified/square).
979
  // Set before the user parameters below so that an explicit "x265:sar" takes precedence.
980
981
0
  uint32_t aspect_h = 1, aspect_v = 1;
982
0
  heif_image_get_pixel_aspect_ratio(image, &aspect_h, &aspect_v);
983
0
  if ((input_class == heif_image_input_class_normal ||
984
0
       input_class == heif_image_input_class_thumbnail) &&
985
0
      aspect_h != aspect_v &&
986
0
      aspect_h > 0 && aspect_v > 0 &&
987
0
      aspect_h <= 0xFFFF && aspect_v <= 0xFFFF) {
988
0
    std::stringstream sstr;
989
0
    sstr << aspect_h << ':' << aspect_v;
990
0
    api->param_parse(param, "sar", sstr.str().c_str());
991
0
  }
992
993
0
  for (const auto& p : encoder->parameters) {
994
0
    if (p.name == heif_encoder_parameter_name_quality) {
995
      // quality=0   -> crf=50
996
      // quality=50  -> crf=25
997
      // quality=100 -> crf=0
998
999
0
      param->rc.rfConstant = (100 - p.value_int) / 2.0;
1000
0
    }
1001
0
    else if (p.name == heif_encoder_parameter_name_lossless) {
1002
0
      param->bLossless = p.value_int;
1003
0
    }
1004
0
    else if (p.name == kParam_TU_intra_depth) {
1005
0
      auto valString = std::to_string(p.value_int);
1006
0
      api->param_parse(param, "tu-intra-depth", valString.c_str());
1007
0
    }
1008
0
    else if (p.name == kParam_complexity) {
1009
0
      const int complexity = p.value_int;
1010
1011
0
      if (complexity >= 60) {
1012
0
        api->param_parse(param, "rd-refine", "1"); // increases computation time
1013
0
        api->param_parse(param, "rd", "6");
1014
0
      }
1015
1016
0
      if (complexity >= 70) {
1017
0
        api->param_parse(param, "cu-lossless", "1"); // increases computation time
1018
0
      }
1019
1020
0
      if (complexity >= 90) {
1021
0
        api->param_parse(param, "wpp", "0"); // setting to 0 significantly increases computation time
1022
0
      }
1023
0
    }
1024
0
    else if (strncmp(p.name.c_str(), "x265:", 5) == 0) {
1025
0
      std::string x265p = p.name.substr(5);
1026
0
      if (api->param_parse(param, x265p.c_str(), p.value_string.c_str()) < 0) {
1027
0
        encoder->last_error_message = std::string{"Unsupported x265 encoder parameter: "} + x265p;
1028
1029
0
        return {
1030
0
          .code = heif_error_Usage_error,
1031
0
          .subcode = heif_suberror_Unsupported_parameter,
1032
0
          .message = encoder->last_error_message.c_str()
1033
0
        };
1034
0
      }
1035
0
    }
1036
0
  }
1037
1038
0
  param->logLevel = encoder->logLevel;
1039
1040
1041
0
  param->sourceWidth = heif_image_get_width(image, heif_channel_Y);
1042
0
  param->sourceHeight = heif_image_get_height(image, heif_channel_Y);
1043
0
  encoder->input_width = param->sourceWidth;
1044
0
  encoder->input_height = param->sourceHeight;
1045
0
  param->internalBitDepth = bit_depth;
1046
1047
0
  param->sourceWidth = rounded_size(param->sourceWidth);
1048
0
  param->sourceHeight = rounded_size(param->sourceHeight);
1049
1050
  // x265 sizes some of its internal picture buffers with 32-bit arithmetic and, up to at least
1051
  // v3.5, neither validates the picture size nor the result of these allocations. Pictures of
1052
  // roughly 700 Mpixel or more make it crash in one of its worker threads (GHSA-2c3g-p585-8rpq).
1053
  // Newer x265 versions refuse pictures above the HEVC Level 7.2 maximum of 142,606,336 luma
1054
  // samples (16384x8704) in x265_check_params() unless non-conformance is explicitly allowed.
1055
  // Apply the same limit here, so that the behavior is the same with all x265 versions and the
1056
  // encoder is never opened with a picture it cannot handle.
1057
0
  const uint64_t max_luma_samples = 142606336;
1058
0
  if (static_cast<uint64_t>(param->sourceWidth) * static_cast<uint64_t>(param->sourceHeight) > max_luma_samples) {
1059
0
    return {heif_error_Encoding_error,
1060
0
            heif_suberror_Encoder_encoding,
1061
0
            "Image too large for x265: at most 142,606,336 luma samples (HEVC Level 7.2) are supported"};
1062
0
  }
1063
1064
0
  param->fpsNum = framerate_num;
1065
0
  param->fpsDenom = framerate_denom;
1066
1067
0
  encoder->bit_depth = bit_depth;
1068
1069
0
  encoder->encoder = api->encoder_open(param);
1070
0
  if (encoder->encoder == nullptr) {
1071
    // x265 rejected the parameters (e.g. newer versions refuse oversized pictures).
1072
0
    return {heif_error_Encoding_error,
1073
0
            heif_suberror_Encoder_initialization,
1074
0
            "x265 encoder could not be opened with the given parameters"};
1075
0
  }
1076
1077
0
  if (image_sequence) {
1078
0
    x265_nal* nals = nullptr;
1079
0
    uint32_t num_nals = 0;
1080
1081
0
    api->encoder_headers(encoder->encoder,
1082
0
                         &nals,
1083
0
                         &num_nals);
1084
1085
0
    encoder->append_nal_packets(nals, num_nals, 0);
1086
1087
0
    for (uint32_t i = 0; i < num_nals; i++) {
1088
      //std::cout << "dequeue header NAL : " << naltype(encoder->nals[i].type) << "\n";
1089
0
    }
1090
0
  }
1091
1092
0
  return heif_error_ok;
1093
0
}
1094
1095
1096
static heif_error x265_start_sequence_encoding(void* encoder_raw, const heif_image* image,
1097
                                       enum heif_image_input_class input_class,
1098
                                       uint32_t framerate_num, uint32_t framerate_denom,
1099
                                       const heif_sequence_encoding_options* options)
1100
0
{
1101
0
  return x265_start_sequence_encoding_intern(encoder_raw, image, input_class,
1102
0
                                             framerate_num, framerate_denom, options, true);
1103
0
}
1104
1105
1106
static heif_error x265_encode_sequence_frame(void* encoder_raw, const heif_image* image,
1107
                                             uintptr_t frame_nr)
1108
0
{
1109
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
1110
1111
0
  if (!encoder->api) {
1112
0
    return {
1113
0
      heif_error_Usage_error,
1114
0
      heif_suberror_Unspecified,
1115
0
      "called plugin encode_sequence_frame() without start_sequence_encoding()"
1116
0
    };
1117
0
  }
1118
1119
  // HEVC can signal different luma and chroma bit depths, but x265 has a
1120
  // single internal bit depth and cannot produce such a stream. Whether this
1121
  // build of libx265 has 10 or 12 bit support is checked separately via
1122
  // x265_api_get().
1123
0
  heif_error input_error = check_encoder_input_image(image, /*supports_monochrome=*/true,
1124
0
                                                    {8, 10, 12});
1125
0
  if (input_error.code != heif_error_Ok) {
1126
0
    return input_error;
1127
0
  }
1128
1129
  // pic->bitDepth below is the depth the encoder was opened with, while the plane
1130
  // pointers are this frame's. A deeper first frame would make libx265 read the
1131
  // planes of a shallower later frame at two bytes per sample.
1132
0
  input_error = check_sequence_frame_bit_depth(image, encoder->bit_depth);
1133
0
  if (input_error.code != heif_error_Ok) {
1134
0
    return input_error;
1135
0
  }
1136
1137
0
  input_error = check_sequence_frame_size(image, encoder->input_width, encoder->input_height);
1138
0
  if (input_error.code != heif_error_Ok) {
1139
0
    return input_error;
1140
0
  }
1141
1142
0
  const x265_api* api = encoder->api;
1143
1144
0
  heif_error err;
1145
1146
  // Note: it is ok to cast away the const, as the image content is not changed.
1147
  // However, we have to guarantee that there are no plane pointers or stride values kept over calling the svt_encode_image() function.
1148
0
  err = heif_image_extend_padding_to_size(const_cast<heif_image*>(image),
1149
0
                                          encoder->param->sourceWidth,
1150
0
                                          encoder->param->sourceHeight);
1151
0
  if (err.code) {
1152
0
    return err;
1153
0
  }
1154
1155
0
  x265_picture* pic = api->picture_alloc();
1156
0
  api->picture_init(encoder->param, pic);
1157
1158
  // TODO: check that all input images use the same color format
1159
1160
0
  bool isGreyscale = (heif_image_get_colorspace(image) == heif_colorspace_monochrome);
1161
1162
0
  if (isGreyscale) {
1163
0
    pic->planes[0] = (void*) heif_image_get_plane_readonly(image, heif_channel_Y, &pic->stride[0]);
1164
0
  }
1165
0
  else {
1166
0
    pic->planes[0] = (void*) heif_image_get_plane_readonly(image, heif_channel_Y, &pic->stride[0]);
1167
0
    pic->planes[1] = (void*) heif_image_get_plane_readonly(image, heif_channel_Cb, &pic->stride[1]);
1168
0
    pic->planes[2] = (void*) heif_image_get_plane_readonly(image, heif_channel_Cr, &pic->stride[2]);
1169
0
  }
1170
1171
0
  pic->bitDepth = encoder->bit_depth;
1172
  // The codec hands this opaque pointer back unchanged; it carries an integer, not an address.
1173
0
  pic->userData = reinterpret_cast<void*>(frame_nr); // NOLINT(performance-no-int-to-ptr)
1174
1175
0
  x265_nal* nals = nullptr;
1176
0
  uint32_t num_nals = 0;
1177
1178
#if X265_BUILD == 212
1179
  // In x265 build version 212, the signature of the encoder_encode() function was changed. But it was changed back in version 213.
1180
  // https://bitbucket.org/multicoreware/x265_git/issues/952/crash-in-libheif-tests
1181
  x265_picture* out_pic = NULL;
1182
  api->encoder_encode(encoder->encoder,
1183
                      &nals,
1184
                      &num_nals,
1185
                      pic,
1186
                      &out_pic);
1187
  if (num_nals > 0 && out_pic) {
1188
    uintptr_t frameNr = reinterpret_cast<uintptr_t>(out_pic->userData);
1189
    encoder->append_nal_packets(nals, num_nals, frameNr);
1190
  }
1191
#else
1192
0
  x265_picture out_pic;
1193
0
  api->encoder_encode(encoder->encoder,
1194
0
                      &nals,
1195
0
                      &num_nals,
1196
0
                      pic,
1197
0
                      &out_pic);
1198
1199
0
  if (num_nals > 0) {
1200
0
    uintptr_t frameNr = reinterpret_cast<uintptr_t>(out_pic.userData);
1201
0
    encoder->append_nal_packets(nals, num_nals, frameNr);
1202
0
  }
1203
1204
0
#endif
1205
1206
0
  api->picture_free(pic);
1207
1208
0
  return heif_error_ok;
1209
0
}
1210
1211
1212
static heif_error x265_end_sequence_encoding(void* encoder_raw)
1213
0
{
1214
0
  encoder_struct_x265* encoder = (encoder_struct_x265*) encoder_raw;
1215
1216
0
  const x265_api* api = encoder->api;
1217
1218
0
  x265_nal* nals = nullptr;
1219
0
  uint32_t num_nals = 0;
1220
1221
#if X265_BUILD == 212
1222
  x265_picture* out_pic = NULL;
1223
  int result = api->encoder_encode(encoder->encoder,
1224
                                   &nals,
1225
                                   &num_nals,
1226
                                   NULL,
1227
                                   &out_pic);
1228
#else
1229
0
  x265_picture out_pic;
1230
0
  int result = api->encoder_encode(encoder->encoder,
1231
0
                                   &nals,
1232
0
                                   &num_nals,
1233
0
                                   NULL,
1234
0
                                   &out_pic);
1235
0
#endif
1236
0
  if (result > 0 && num_nals > 0) {
1237
#if X265_BUILD == 212
1238
    uintptr_t frameNr = out_pic ? reinterpret_cast<uintptr_t>(out_pic->userData) : 0;
1239
#else
1240
0
    uintptr_t frameNr = reinterpret_cast<uintptr_t>(out_pic.userData);
1241
0
#endif
1242
0
    encoder->append_nal_packets(nals, num_nals, frameNr);
1243
0
  }
1244
1245
0
  encoder->api->param_free(encoder->param);
1246
0
  encoder->param = nullptr;
1247
1248
0
  return heif_error_ok;
1249
0
}
1250
1251
1252
static heif_error x265_encode_image(void* encoder_raw, const heif_image* image,
1253
                                    heif_image_input_class input_class)
1254
0
{
1255
0
  heif_error err;
1256
0
  err = x265_start_sequence_encoding_intern(encoder_raw, image, input_class, 1,25, nullptr, false);
1257
0
  if (err.code) {
1258
0
    return err;
1259
0
  }
1260
1261
0
  err = x265_encode_sequence_frame(encoder_raw, image, 0);
1262
0
  if (err.code) {
1263
0
    return err;
1264
0
  }
1265
1266
0
  x265_end_sequence_encoding(encoder_raw);
1267
1268
0
  return heif_error_ok;
1269
0
}
1270
1271
1272
static heif_error x265_get_compressed_data_intern(void* encoder_raw, uint8_t** data, int* size,
1273
                                                  uintptr_t* out_frame_nr)
1274
0
{
1275
0
  encoder_struct_x265* encoder = ( encoder_struct_x265*) encoder_raw;
1276
1277
0
  if (encoder->output_data.empty()) {
1278
0
    *data = nullptr;
1279
0
    *size = 0;
1280
1281
0
    return heif_error_ok;
1282
0
  }
1283
1284
0
  encoder->active_output_nal = std::move(encoder->output_data.front().data);
1285
1286
0
  if (out_frame_nr) {
1287
0
    *out_frame_nr = encoder->output_data.front().frameNr;
1288
0
  }
1289
1290
0
  encoder->output_data.pop_front();
1291
1292
0
  *data = encoder->active_output_nal.data();
1293
0
  *size = static_cast<int>(encoder->active_output_nal.size());
1294
1295
0
  return heif_error_ok;
1296
0
}
1297
1298
1299
static heif_error x265_get_compressed_data(void* encoder_raw, uint8_t** data, int* size,
1300
                                           heif_encoded_data_type* type)
1301
0
{
1302
0
  return x265_get_compressed_data_intern(encoder_raw, data, size, nullptr);
1303
0
}
1304
1305
static heif_error x265_get_compressed_data2(void* encoder_raw, uint8_t** data, int* size,
1306
                                            uintptr_t* frame_nr, int* is_keyframe,
1307
                                            int* more_frame_packets)
1308
0
{
1309
0
  return x265_get_compressed_data_intern(encoder_raw, data, size, frame_nr);
1310
0
}
1311
1312
1313
static const heif_encoder_plugin encoder_plugin_x265
1314
    {
1315
        /* plugin_api_version */ 4,
1316
        /* compression_format */ heif_compression_HEVC,
1317
        /* id_name */ "x265",
1318
        /* priority */ X265_PLUGIN_PRIORITY,
1319
        /* supports_lossy_compression */ true,
1320
        /* supports_lossless_compression */ true,
1321
        /* get_plugin_name */ x265_plugin_name,
1322
        /* init_plugin */ x265_init_plugin,
1323
        /* cleanup_plugin */ x265_cleanup_plugin,
1324
        /* new_encoder */ x265_new_encoder,
1325
        /* free_encoder */ x265_free_encoder,
1326
        /* set_parameter_quality */ x265_set_parameter_quality,
1327
        /* get_parameter_quality */ x265_get_parameter_quality,
1328
        /* set_parameter_lossless */ x265_set_parameter_lossless,
1329
        /* get_parameter_lossless */ x265_get_parameter_lossless,
1330
        /* set_parameter_logging_level */ x265_set_parameter_logging_level,
1331
        /* get_parameter_logging_level */ x265_get_parameter_logging_level,
1332
        /* list_parameters */ x265_list_parameters,
1333
        /* set_parameter_integer */ x265_set_parameter_integer,
1334
        /* get_parameter_integer */ x265_get_parameter_integer,
1335
        /* set_parameter_boolean */ x265_set_parameter_integer, // boolean also maps to integer function
1336
        /* get_parameter_boolean */ x265_get_parameter_integer, // boolean also maps to integer function
1337
        /* set_parameter_string */ x265_set_parameter_string,
1338
        /* get_parameter_string */ x265_get_parameter_string,
1339
        /* query_input_colorspace */ x265_query_input_colorspace,
1340
        /* encode_image */ x265_encode_image,
1341
        /* get_compressed_data */ x265_get_compressed_data,
1342
        /* query_input_colorspace (v2) */ x265_query_input_colorspace2,
1343
        /* query_encoded_size (v3) */ nullptr,
1344
        /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,21,0),
1345
        /* start_sequence_encoding (v4) */ x265_start_sequence_encoding,
1346
        /* encode_sequence_frame (v4) */ x265_encode_sequence_frame,
1347
        /* end_sequence_encoding (v4) */ x265_end_sequence_encoding,
1348
        /* get_compressed_data2 (v4) */ x265_get_compressed_data2,
1349
        /* does_indicate_keyframes (v4) */ 0
1350
    };
1351
1352
const heif_encoder_plugin* get_encoder_plugin_x265()
1353
254
{
1354
254
  return &encoder_plugin_x265;
1355
254
}
1356
1357
1358
#if PLUGIN_X265
1359
heif_plugin_info plugin_info {
1360
  1,
1361
  heif_plugin_type_encoder,
1362
  &encoder_plugin_x265
1363
};
1364
#endif