Coverage Report

Created: 2026-07-25 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/plugins/decoder_libde265.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 "decoder_libde265.h"
24
#include <cassert>
25
#include <memory>
26
#include <cstring>
27
#include <limits>
28
#include <string>
29
30
#include <libde265/de265.h>
31
32
33
34
struct libde265_decoder
35
{
36
  de265_decoder_context* ctx;
37
  bool strict_decoding = false;
38
  std::string error_message;
39
};
40
41
static const char kEmptyString[] = "";
42
static const char kSuccess[] = "Success";
43
44
static const int LIBDE265_PLUGIN_PRIORITY = 100;
45
46
0
#define MAX_PLUGIN_NAME_LENGTH 80
47
48
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
49
static constexpr char version_prefix[] = ", version ";
50
51
static const char* libde265_plugin_name()
52
0
{
53
0
  strcpy(plugin_name, "libde265 HEVC decoder");
54
55
0
  const char* libde265_version = de265_get_version();
56
57
0
  if (strlen(plugin_name) + strlen(libde265_version) + (sizeof(version_prefix) - 1) < MAX_PLUGIN_NAME_LENGTH) {
58
0
    strcat(plugin_name, version_prefix);
59
0
    strcat(plugin_name, libde265_version);
60
0
  }
61
62
0
  return plugin_name;
63
0
}
64
65
66
static void libde265_init_plugin()
67
2.42k
{
68
2.42k
  de265_init();
69
2.42k
}
70
71
72
static void libde265_deinit_plugin()
73
2.42k
{
74
2.42k
  de265_free();
75
2.42k
}
76
77
78
static int libde265_does_support_format(heif_compression_format format)
79
1.21k
{
80
1.21k
  if (format == heif_compression_HEVC) {
81
702
    return LIBDE265_PLUGIN_PRIORITY;
82
702
  }
83
513
  else {
84
513
    return 0;
85
513
  }
86
1.21k
}
87
88
89
[[maybe_unused]]
90
static int libde265_does_support_format2(const heif_decoder_plugin_compressed_format_description* format)
91
0
{
92
0
  return libde265_does_support_format(format->format);
93
0
}
94
95
96
97
static heif_error convert_libde265_image_to_heif_image(libde265_decoder* decoder,
98
                                                       const de265_image* de265img,
99
                                                       heif_image** image,
100
                                                       const heif_security_limits* limits)
101
319
{
102
319
  bool is_mono = (de265_get_chroma_format(de265img) == de265_chroma_mono);
103
104
319
  heif_error err;
105
319
  err = heif_image_create(de265_get_image_width(de265img, 0),
106
319
                          de265_get_image_height(de265img, 0),
107
319
                          is_mono ? heif_colorspace_monochrome : heif_colorspace_YCbCr,
108
319
                          (heif_chroma) de265_get_chroma_format(de265img),
109
319
                          image);
110
319
  if (err.code) {
111
0
    return err;
112
0
  }
113
114
  // --- transfer data from de265_image to HeifPixelImage
115
116
319
  heif_channel channel2plane[3] = {
117
319
      heif_channel_Y,
118
319
      heif_channel_Cb,
119
319
      heif_channel_Cr
120
319
  };
121
122
123
319
  int bpp = de265_get_bits_per_pixel(de265img, 0);
124
125
319
  int num_planes = (is_mono ? 1 : 3);
126
127
1.08k
  for (int c = 0; c < num_planes; c++) {
128
824
    if (de265_get_bits_per_pixel(de265img, c) != bpp) {
129
61
      heif_image_release(*image);
130
61
      err = {heif_error_Unsupported_feature,
131
61
             heif_suberror_Unsupported_color_conversion,
132
61
             "Channels with different number of bits per pixel are not supported"};
133
61
      return err;
134
61
    }
135
136
763
    int stride;
137
763
    const uint8_t* data = de265_get_image_plane(de265img, c, &stride);
138
139
763
    int w = de265_get_image_width(de265img, c);
140
763
    int h = de265_get_image_height(de265img, c);
141
763
    if (w <= 0 || h <= 0) {
142
0
      heif_image_release(*image);
143
0
      err = {heif_error_Decoder_plugin_error,
144
0
             heif_suberror_Invalid_image_size,
145
0
             kEmptyString};
146
0
      return err;
147
0
    }
148
149
763
    err = heif_image_add_plane_safe(*image, channel2plane[c], w,h, bpp, limits);
150
763
    if (err.code) {
151
      // copy error message to decoder object because heif_image will be released
152
0
      decoder->error_message = err.message;
153
0
      err.message = decoder->error_message.c_str();
154
155
0
      heif_image_release(*image);
156
0
      return err;
157
0
    }
158
159
763
    size_t dst_stride;
160
763
    uint8_t* dst_mem = heif_image_get_plane2(*image, channel2plane[c], &dst_stride);
161
162
763
    int bytes_per_pixel = (bpp + 7) / 8;
163
164
135k
    for (int y = 0; y < h; y++) {
165
134k
      memcpy(dst_mem + y * dst_stride, data + static_cast<size_t>(y) * stride, static_cast<size_t>(w) * bytes_per_pixel);
166
134k
    }
167
763
  }
168
169
170
258
  return {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
171
319
}
172
173
174
175
// Create a new decoder context for decoding an image
176
heif_error libde265_new_decoder2(void** dec, const heif_decoder_plugin_options* options)
177
605
{
178
605
  libde265_decoder* decoder = new libde265_decoder();
179
605
  heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
180
181
605
  decoder->ctx = de265_new_decoder();
182
183
605
#if LIBDE265_NUMERIC_VERSION >= 0x01010000
184
  // Pass libheif's max_image_size_pixels through to libde265 so the decoder
185
  // rejects bitstreams whose SPS declares an image far larger than what the
186
  // surrounding HEIF file claims. libheif tightens this limit per-decode to
187
  // ispe + one coding-unit margin (see tighten_image_size_limit_for_ispe),
188
  // so the padding margin is already baked into the value we see here.
189
605
  {
190
605
    const heif_security_limits* limits = options->limits ? options->limits : heif_get_global_security_limits();
191
605
    de265_security_limits* de265_limits = de265_get_security_limits(decoder->ctx);
192
605
    if (limits->max_image_size_pixels > std::numeric_limits<uint32_t>::max()) {
193
3
      de265_limits->max_image_size_pixels = 0;
194
3
    }
195
602
    else {
196
602
      de265_limits->max_image_size_pixels = static_cast<uint32_t>(limits->max_image_size_pixels);
197
602
    }
198
605
  }
199
605
#endif
200
201
#if defined(__EMSCRIPTEN__)
202
  // Speed up decoding from JavaScript.
203
  de265_set_parameter_bool(decoder->ctx, DE265_DECODER_PARAM_DISABLE_DEBLOCKING, 1);
204
  de265_set_parameter_bool(decoder->ctx, DE265_DECODER_PARAM_DISABLE_SAO, 1);
205
#else
206
605
  int nThreads = (options->num_threads ? options->num_threads : 1);
207
208
  // Worker threads are not supported when running on Emscripten.
209
605
  de265_start_worker_threads(decoder->ctx, nThreads);
210
605
#endif
211
212
605
  decoder->strict_decoding = options->strict_decoding;
213
214
605
  *dec = decoder;
215
605
  return err;
216
605
}
217
218
219
static heif_error libde265_new_decoder(void** dec)
220
0
{
221
0
  heif_decoder_plugin_options options{};
222
0
  options.format = heif_compression_HEVC;
223
0
  options.num_threads = 0;
224
0
  options.strict_decoding = false;
225
226
0
  return libde265_new_decoder2(dec, &options);
227
0
}
228
229
static void libde265_free_decoder(void* decoder_raw)
230
605
{
231
605
  libde265_decoder* decoder = (libde265_decoder*) decoder_raw;
232
233
605
  de265_error err = de265_free_decoder(decoder->ctx);
234
605
  (void) err;
235
236
605
  delete decoder;
237
605
}
238
239
240
void libde265_set_strict_decoding(void* decoder_raw, int flag)
241
0
{
242
0
  libde265_decoder* decoder = (libde265_decoder*) decoder_raw;
243
244
0
  decoder->strict_decoding = flag;
245
0
}
246
247
248
#if LIBDE265_NUMERIC_VERSION >= 0x02000000
249
250
static heif_error libde265_v2_push_data(void* decoder_raw, const void* data, size_t size)
251
{
252
  libde265_decoder* decoder = (libde265_decoder*)decoder_raw;
253
254
  const uint8_t* cdata = (const uint8_t*)data;
255
256
  size_t ptr=0;
257
  while (ptr < size) {
258
    if (4 > size - ptr) {
259
      return { heif_error_Decoder_plugin_error,
260
               heif_suberror_End_of_data,
261
               kEmptyString };
262
    }
263
264
    // TODO: the size of the NAL unit length variable is defined in the hvcC header.
265
    // We should not assume that it is always 4 bytes.
266
    uint32_t nal_size = (uint32_t)((cdata[ptr]<<24) | (cdata[ptr+1]<<16) | (cdata[ptr+2]<<8) | (cdata[ptr+3]));
267
    ptr+=4;
268
269
    if (nal_size > size - ptr) {
270
      //sstr << "NAL size (" << size32 << ") exceeds available data in file ("
271
      //<< data_bytes_left_to_read << ")";
272
273
      return { heif_error_Decoder_plugin_error,
274
               heif_suberror_End_of_data,
275
               kEmptyString };
276
    }
277
278
    de265_push_NAL(decoder->ctx, cdata+ptr, nal_size, 0, nullptr);
279
    ptr += nal_size;
280
  }
281
282
283
  return { heif_error_Ok, heif_suberror_Unspecified, kSuccess };
284
}
285
286
287
static heif_error libde265_v2_decode_next_image(void* decoder_raw,
288
                                                heif_image** out_img,
289
                                                const heif_security_limits* limits)
290
{
291
  libde265_decoder* decoder = (libde265_decoder*)decoder_raw;
292
293
  de265_push_end_of_stream(decoder->ctx);
294
295
  int action = de265_get_action(decoder->ctx, 1);
296
297
  // TODO: read NCLX from h265 bitstream
298
299
  // TODO(farindk): Set "err" if no image was decoded.
300
  if (action==de265_action_get_image) {
301
    const de265_image* img = de265_get_next_picture(decoder->ctx);
302
    if (img) {
303
      heif_error err = convert_libde265_image_to_heif_image(decoder, img,
304
                                                                   out_img, limits);
305
      de265_release_picture(img);
306
307
      return err;
308
    }
309
  }
310
311
  return { heif_error_Decoder_plugin_error, heif_suberror_Unspecified, kEmptyString };
312
}
313
314
static heif_error libde265_v2_decode_image(void* decoder_raw,
315
                                           heif_image** out_img)
316
{
317
  auto* limits = heif_get_global_security_limits();
318
  return libde265_v2_decode_next_image(decoder_raw, out_img, limits);
319
}
320
#else
321
322
static heif_error libde265_v1_push_data2(void* decoder_raw, const void* data, size_t size, uintptr_t user_data)
323
531
{
324
531
  libde265_decoder* decoder = (libde265_decoder*) decoder_raw;
325
326
531
  const uint8_t* cdata = (const uint8_t*) data;
327
328
531
  size_t ptr = 0;
329
3.18k
  while (ptr < size) {
330
2.65k
    if (4 > size - ptr) {
331
0
      return {
332
0
        heif_error_Decoder_plugin_error,
333
0
        heif_suberror_End_of_data,
334
0
        kEmptyString
335
0
      };
336
0
    }
337
338
2.65k
    uint32_t nal_size = static_cast<uint32_t>((cdata[ptr] << 24) | (cdata[ptr + 1] << 16) | (cdata[ptr + 2] << 8) | (cdata[ptr + 3]));
339
2.65k
    ptr += 4;
340
341
2.65k
    if (nal_size > size - ptr) {
342
0
      return {
343
0
        heif_error_Decoder_plugin_error,
344
0
        heif_suberror_End_of_data,
345
0
        kEmptyString
346
0
      };
347
0
    }
348
349
#if 0
350
    FILE* fh = fopen("data.h265", "a");
351
    fputc(0, fh);
352
    fputc(0, fh);
353
    fputc(1, fh);
354
    fwrite(cdata + ptr, nal_size, 1, fh);
355
    fclose(fh);
356
357
    printf("put nal with size %d %x\n", nal_size, *(cdata+ptr));
358
#endif
359
360
2.65k
    de265_push_NAL(decoder->ctx, cdata + ptr, nal_size, 0, (void*)user_data);
361
2.65k
    ptr += nal_size;
362
2.65k
  }
363
364
  // TODO(farindk): Set "err" if data could not be pushed
365
  //de265_push_data(decoder->ctx, data, size, 0, nullptr);
366
367
531
  return {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
368
531
}
369
370
static heif_error libde265_v1_push_data(void* decoder_raw, const void* data, size_t size)
371
0
{
372
0
  return libde265_v1_push_data2(decoder_raw, data, size, 0);
373
0
}
374
375
static heif_error libde265_flush_data(void* decoder_raw)
376
531
{
377
531
  libde265_decoder* decoder = (libde265_decoder*) decoder_raw;
378
379
531
  de265_flush_data(decoder->ctx);
380
381
531
  return heif_error_ok;
382
531
}
383
384
385
386
static heif_error libde265_v1_decode_next_image2(void* decoder_raw,
387
                                                 heif_image** out_img,
388
                                                 uintptr_t* out_user_data,
389
                                                 const heif_security_limits* limits)
390
11.2k
{
391
11.2k
  libde265_decoder* decoder = (libde265_decoder*) decoder_raw;
392
11.2k
  heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
393
394
  // TODO(251119) : de265_flush_data(decoder->ctx);
395
396
  // TODO(farindk): Set "err" if no image was decoded.
397
11.2k
  int more;
398
11.2k
  de265_error decode_err;
399
11.2k
  *out_img = nullptr;
400
12.7k
  do {
401
12.7k
    more = 0;
402
12.7k
    decode_err = de265_decode(decoder->ctx, &more);
403
12.7k
    if (decode_err != DE265_OK) {
404
      // printf("Error decoding: %s (%d)\n", de265_get_error_text(decode_err), decode_err);
405
828
      break;
406
828
    }
407
408
    // TODO: read NCLX from h265 bitstream
409
410
11.9k
    const de265_image* image = de265_get_next_picture(decoder->ctx);
411
11.9k
    if (image) {
412
      // TODO(farindk): Should we return the first image instead?
413
319
      if (*out_img) {
414
0
        heif_image_release(*out_img);
415
0
      }
416
417
319
      if (out_user_data) {
418
0
        *out_user_data = (uintptr_t)de265_get_image_user_data(image);
419
0
      }
420
421
319
      err = convert_libde265_image_to_heif_image(decoder, image, out_img, limits);
422
319
      if (err.code != heif_error_Ok) {
423
61
        return err;
424
61
      }
425
426
258
      heif_color_profile_nclx* nclx = heif_nclx_color_profile_alloc();
427
258
#if LIBDE265_NUMERIC_VERSION >= 0x01000700
428
258
      HEIF_WARN_OR_FAIL(decoder->strict_decoding, *out_img, heif_nclx_color_profile_set_color_primaries(nclx, static_cast<uint16_t>(de265_get_image_colour_primaries(image))),
429
258
                        {
430
258
                          heif_nclx_color_profile_free(nclx);
431
258
                          heif_image_release(*out_img);
432
258
                          *out_img = nullptr;
433
258
                        });
434
258
      HEIF_WARN_OR_FAIL(decoder->strict_decoding, *out_img, heif_nclx_color_profile_set_transfer_characteristics(nclx, static_cast<uint16_t>(de265_get_image_transfer_characteristics(image))),
435
258
                        {
436
258
                          heif_nclx_color_profile_free(nclx);
437
258
                          heif_image_release(*out_img);
438
258
                          *out_img = nullptr;
439
258
                        });
440
258
      HEIF_WARN_OR_FAIL(decoder->strict_decoding, *out_img, heif_nclx_color_profile_set_matrix_coefficients(nclx, static_cast<uint16_t>(de265_get_image_matrix_coefficients(image))),
441
258
                        {
442
258
                          heif_nclx_color_profile_free(nclx);
443
258
                          heif_image_release(*out_img);
444
258
                          *out_img = nullptr;
445
258
                        });
446
251
      nclx->full_range_flag = (bool) de265_get_image_full_range_flag(image);
447
251
#endif
448
251
      heif_image_set_nclx_color_profile(*out_img, nclx);
449
251
      heif_nclx_color_profile_free(nclx);
450
451
251
      de265_release_next_picture(decoder->ctx);
452
251
      return heif_error_ok;
453
258
    }
454
11.9k
  } while (more);
455
456
10.8k
  return err;
457
11.2k
}
458
459
460
static heif_error libde265_v1_decode_next_image(void* decoder_raw,
461
                                                heif_image** out_img,
462
                                                const heif_security_limits* limits)
463
0
{
464
0
  return libde265_v1_decode_next_image2(decoder_raw, out_img, nullptr, limits);
465
0
}
466
467
static heif_error libde265_v1_decode_image(void* decoder_raw,
468
                                           heif_image** out_img)
469
0
{
470
0
  auto* limits = heif_get_global_security_limits();
471
0
  return libde265_v1_decode_next_image(decoder_raw, out_img, limits);
472
0
}
473
474
#endif
475
476
477
#if LIBDE265_NUMERIC_VERSION >= 0x02000000
478
479
static const heif_decoder_plugin decoder_libde265
480
{
481
  1,
482
  libde265_plugin_name,
483
  libde265_init_plugin,
484
  libde265_deinit_plugin,
485
  libde265_does_support_format,
486
  libde265_new_decoder,
487
  libde265_free_decoder,
488
  libde265_v2_push_data,
489
  libde265_v2_decode_image,
490
  libde265_set_strict_decoding,
491
  "libde265",
492
  libde265_v2_decode_next_image
493
};
494
495
#else
496
497
static const heif_decoder_plugin decoder_libde265
498
    {
499
        5,
500
        libde265_plugin_name,
501
        libde265_init_plugin,
502
        libde265_deinit_plugin,
503
        libde265_does_support_format,
504
        libde265_new_decoder,
505
        libde265_free_decoder,
506
        libde265_v1_push_data,
507
        libde265_v1_decode_image,
508
        libde265_set_strict_decoding,
509
        "libde265",
510
        libde265_v1_decode_next_image,
511
        /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,21,0),
512
        libde265_does_support_format2,
513
        libde265_new_decoder2,
514
        libde265_v1_push_data2,
515
        libde265_flush_data,
516
        libde265_v1_decode_next_image2
517
    };
518
519
#endif
520
521
const heif_decoder_plugin* get_decoder_plugin_libde265()
522
2.42k
{
523
2.42k
  return &decoder_libde265;
524
2.42k
}
525
526
527
528
#if PLUGIN_LIBDE265
529
heif_plugin_info plugin_info {
530
  1,
531
  heif_plugin_type_decoder,
532
  &decoder_libde265
533
};
534
#endif