Coverage Report

Created: 2026-09-06 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/plugins/decoder_dav1d.cc
Line
Count
Source
1
/*
2
 * AVIF codec.
3
 * Copyright (c) 2020 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 "security_limits.h"
24
#include "common_utils.h"
25
#include "decoder_dav1d.h"
26
#include <cstring>
27
#include <cassert>
28
#include <cstdio>
29
#include <deque>
30
#include <limits>
31
#include <string>
32
33
#include <dav1d/version.h>
34
#include <dav1d/dav1d.h>
35
36
37
struct dav1d_decoder
38
{
39
  Dav1dSettings settings{};
40
  Dav1dContext* context{};
41
  std::deque<Dav1dData> queued_data;
42
  bool strict_decoding = false;
43
  std::string error_message;
44
};
45
46
static constexpr char kEmptyString[] = "";
47
static constexpr char kSuccess[] = "Success";
48
49
static constexpr int DAV1D_PLUGIN_PRIORITY = 150;
50
51
54
#define MAX_PLUGIN_NAME_LENGTH 80
52
53
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
54
55
56
static const char* dav1d_plugin_name()
57
27
{
58
27
  snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "dav1d v%d.%d.%d",
59
27
           DAV1D_API_VERSION_MAJOR,
60
27
           DAV1D_API_VERSION_MINOR,
61
27
           DAV1D_API_VERSION_PATCH);
62
63
  // make sure that the string is null-terminated
64
27
  plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
65
66
27
  return plugin_name;
67
27
}
68
69
70
static void dav1d_init_plugin()
71
252
{
72
252
}
73
74
75
static void dav1d_deinit_plugin()
76
0
{
77
0
}
78
79
80
static int dav1d_does_support_format(heif_compression_format format)
81
60.4k
{
82
60.4k
  if (format == heif_compression_AV1) {
83
37.0k
    return DAV1D_PLUGIN_PRIORITY;
84
37.0k
  }
85
23.4k
  else {
86
23.4k
    return 0;
87
23.4k
  }
88
60.4k
}
89
90
91
static int dav1d_does_support_format2(const heif_decoder_plugin_compressed_format_description* format)
92
0
{
93
0
  return dav1d_does_support_format(format->format);
94
0
}
95
96
heif_error dav1d_new_decoder2(void** dec, const heif_decoder_plugin_options* options)
97
37.0k
{
98
37.0k
  auto* decoder = new dav1d_decoder();
99
100
37.0k
  dav1d_default_settings(&decoder->settings);
101
102
18.4E
  const heif_security_limits* limits = options->limits ? options->limits : heif_get_global_security_limits();
103
37.0k
  if (limits->max_image_size_pixels > std::numeric_limits<unsigned int>::max()) {
104
0
    decoder->settings.frame_size_limit = 0;
105
0
  }
106
37.0k
  else {
107
37.0k
    decoder->settings.frame_size_limit = static_cast<unsigned int>(limits->max_image_size_pixels);
108
37.0k
  }
109
110
37.0k
  decoder->settings.all_layers = 0;
111
112
37.0k
  if (options->num_threads) {
113
0
    decoder->settings.n_threads = options->num_threads;
114
0
  }
115
116
37.0k
  if (dav1d_open(&decoder->context, &decoder->settings) != 0) {
117
0
    delete decoder;
118
0
    return {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, kSuccess};
119
0
  }
120
121
37.0k
  *dec = decoder;
122
123
37.0k
  return heif_error_ok;
124
37.0k
}
125
126
heif_error dav1d_new_decoder(void** dec)
127
0
{
128
0
  struct heif_decoder_plugin_options options{};
129
0
  options.format = heif_compression_AV1;
130
0
  options.strict_decoding = false;
131
0
  options.num_threads = 0;
132
133
0
  return dav1d_new_decoder2(dec, &options);
134
0
}
135
136
void dav1d_free_decoder(void* decoder_raw)
137
37.0k
{
138
37.0k
  auto* decoder = (dav1d_decoder*) decoder_raw;
139
140
37.0k
  if (!decoder) {
141
0
    return;
142
0
  }
143
144
  // free queued data
145
146
37.0k
  for (auto& pkt : decoder->queued_data) {
147
27
    dav1d_data_unref(&pkt);
148
27
  }
149
37.0k
  decoder->queued_data.clear();
150
151
  // free decoder context
152
153
37.0k
  if (decoder->context) {
154
37.0k
    dav1d_close(&decoder->context);
155
37.0k
  }
156
157
37.0k
  delete decoder;
158
37.0k
}
159
160
161
void dav1d_set_strict_decoding(void* decoder_raw, int flag)
162
0
{
163
0
  dav1d_decoder* decoder = (dav1d_decoder*) decoder_raw;
164
165
0
  decoder->strict_decoding = flag;
166
0
}
167
168
169
static heif_error push_pending_data_into_decoder(dav1d_decoder* decoder)
170
246k
{
171
314k
  while (!decoder->queued_data.empty()) {
172
173
    // send data
174
175
70.6k
    int res = dav1d_send_data(decoder->context, &decoder->queued_data.front());
176
177
    // decoder does not accept more data at this moment
178
179
70.6k
    if (res == DAV1D_ERR(EAGAIN)) {
180
54
      break;
181
54
    }
182
183
70.5k
    if (res < 0) {
184
      // dav1d_send_data failed. Data was not consumed, unref before removing from queue.
185
2.09k
      dav1d_data_unref(&decoder->queued_data.front());
186
2.09k
      decoder->queued_data.pop_front();
187
2.09k
      return {
188
2.09k
        heif_error_Decoder_plugin_error,
189
2.09k
        heif_suberror_Unspecified,
190
2.09k
        kEmptyString
191
2.09k
      };
192
2.09k
    }
193
194
    // Decoder has accepted data (Dav1dData is consumed). Remove from queue.
195
68.4k
    decoder->queued_data.pop_front();
196
68.4k
  }
197
198
243k
  return heif_error_ok;
199
246k
}
200
201
202
heif_error dav1d_push_data2(void* decoder_raw, const void* frame_data, size_t frame_size, uintptr_t user_data)
203
36.3k
{
204
36.3k
  auto* decoder = (struct dav1d_decoder*) decoder_raw;
205
206
  // --- copy input data into Dav1dData packet
207
208
36.3k
  Dav1dData packet{};
209
210
36.3k
  uint8_t* d = dav1d_data_create(&packet, frame_size);
211
36.3k
  if (d == nullptr) {
212
0
    return {heif_error_Memory_allocation_error, heif_suberror_Unspecified, kSuccess};
213
0
  }
214
215
36.3k
  memcpy(d, frame_data, frame_size);
216
217
  // The codec hands this opaque pointer back unchanged; it carries an integer, not an address.
218
36.3k
  packet.m.user_data.data = (uint8_t*)user_data; // NOLINT(performance-no-int-to-ptr)
219
220
  // --- put data into queue
221
222
36.3k
  decoder->queued_data.push_back(packet);
223
224
  // --- push pending data to decoder
225
226
36.3k
  return push_pending_data_into_decoder(decoder);
227
36.3k
}
228
229
230
heif_error dav1d_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
231
0
{
232
0
  return dav1d_push_data2(decoder_raw, frame_data, frame_size, 0);
233
0
}
234
235
236
heif_error dav1d_decode_next_image2(void* decoder_raw, heif_image** out_img,
237
                                    uintptr_t* out_user_data,
238
                                    const heif_security_limits* limits)
239
175k
{
240
175k
  auto* decoder = (struct dav1d_decoder*) decoder_raw;
241
242
175k
  heif_error err;
243
244
175k
  Dav1dPicture frame{};
245
246
175k
  for (;;) {
247
248
    // --- send more pending data to decoder
249
250
175k
    err = push_pending_data_into_decoder(decoder);
251
175k
    if (err.code) {
252
0
      return err;
253
0
    }
254
255
    // --- try to get decoded image
256
257
175k
    int res = dav1d_get_picture(decoder->context, &frame);
258
259
    // We got a picture from the decoder. Continue with processing it.
260
175k
    if (res == 0) {
261
23.1k
      break;
262
23.1k
    }
263
264
    // decoder wants more data, but queue is empty
265
152k
    if (res == DAV1D_ERR(EAGAIN) && decoder->queued_data.empty()) {
266
143k
      *out_img = nullptr;
267
143k
      return heif_error_ok;
268
143k
    }
269
270
    // continue feeding more data from queue
271
8.91k
    if (res == DAV1D_ERR(EAGAIN)) {
272
0
      continue;
273
0
    }
274
275
    // decoder error
276
8.91k
    if (res < 0) {
277
8.91k
      return {
278
8.91k
        heif_error_Decoder_plugin_error,
279
8.91k
        heif_suberror_Unspecified,
280
8.91k
        kEmptyString
281
8.91k
      };
282
8.91k
    }
283
8.91k
  }
284
285
  // --- convert image to heif_image
286
287
23.1k
  heif_chroma chroma;
288
23.1k
  heif_colorspace colorspace;
289
23.1k
  switch (frame.p.layout) {
290
12.1k
    case DAV1D_PIXEL_LAYOUT_I420:
291
12.1k
      chroma = heif_chroma_420;
292
12.1k
      colorspace = heif_colorspace_YCbCr;
293
12.1k
      break;
294
503
    case DAV1D_PIXEL_LAYOUT_I422:
295
503
      chroma = heif_chroma_422;
296
503
      colorspace = heif_colorspace_YCbCr;
297
503
      break;
298
5.39k
    case DAV1D_PIXEL_LAYOUT_I444:
299
5.39k
      chroma = heif_chroma_444;
300
5.39k
      colorspace = heif_colorspace_YCbCr;
301
5.39k
      break;
302
5.03k
    case DAV1D_PIXEL_LAYOUT_I400:
303
5.03k
      chroma = heif_chroma_monochrome;
304
5.03k
      colorspace = heif_colorspace_monochrome;
305
5.03k
      break;
306
0
    default: {
307
0
      dav1d_picture_unref(&frame);
308
0
      return {
309
0
        heif_error_Decoder_plugin_error,
310
0
        heif_suberror_Unspecified,
311
0
        kEmptyString
312
0
      };
313
0
    }
314
23.1k
  }
315
316
23.1k
  if (out_user_data) {
317
0
    *out_user_data = (uintptr_t)frame.m.user_data.data;
318
0
  }
319
320
23.1k
  heif_image* heif_img = nullptr;
321
23.1k
  err = heif_image_create(frame.p.w, frame.p.h,
322
23.1k
                          colorspace,
323
23.1k
                          chroma,
324
23.1k
                          &heif_img);
325
23.1k
  if (err.code != heif_error_Ok) {
326
0
    assert(heif_img == nullptr);
327
0
    dav1d_picture_unref(&frame);
328
0
    return err;
329
0
  }
330
331
332
  // --- read nclx parameters from decoded AV1 bitstream
333
334
23.1k
  heif_color_profile_nclx nclx;
335
23.1k
  HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_color_primaries(&nclx, static_cast<uint16_t>(frame.seq_hdr->pri)), {});
336
23.1k
  HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_transfer_characteristics(&nclx, static_cast<uint16_t>(frame.seq_hdr->trc)), {});
337
23.1k
  HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_matrix_coefficients(&nclx, static_cast<uint16_t>(frame.seq_hdr->mtrx)), {});
338
23.1k
  nclx.full_range_flag = (frame.seq_hdr->color_range != 0);
339
23.1k
  heif_image_set_nclx_color_profile(heif_img, &nclx);
340
341
342
23.1k
  heif_channel channel2plane[3] = {
343
23.1k
      heif_channel_Y,
344
23.1k
      heif_channel_Cb,
345
23.1k
      heif_channel_Cr
346
23.1k
  };
347
348
349
  // --- copy image data
350
351
23.1k
  int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3);
352
353
82.3k
  for (int c = 0; c < num_planes; c++) {
354
59.2k
    int bpp = frame.p.bpc;
355
356
59.2k
    const uint8_t* data = (uint8_t*) frame.data[c];
357
59.2k
    int stride = (int) frame.stride[c > 0 ? 1 : 0];
358
359
59.2k
    uint32_t w, h;
360
59.2k
    get_subsampled_size(frame.p.w, frame.p.h,
361
59.2k
                        channel2plane[c], chroma, &w, &h);
362
363
59.2k
    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
364
59.2k
    if (err.code != heif_error_Ok) {
365
      // copy error message to decoder object because heif_image will be released
366
0
      decoder->error_message = err.message;
367
0
      err.message = decoder->error_message.c_str();
368
369
0
      heif_image_release(heif_img);
370
0
      dav1d_picture_unref(&frame);
371
0
      return err;
372
0
    }
373
374
59.2k
    size_t dst_stride;
375
59.2k
    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
376
377
59.2k
    const int bytes_per_pixel = (bpp + 7) / 8;
378
379
28.9M
    for (uint32_t y = 0; y < h; y++) {
380
28.8M
      memcpy(dst_mem + y * dst_stride, data + static_cast<size_t>(y) * stride, static_cast<size_t>(w) * bytes_per_pixel);
381
28.8M
    }
382
59.2k
  }
383
384
23.1k
  dav1d_picture_unref(&frame);
385
386
23.1k
  *out_img = heif_img;
387
388
389
23.1k
  return {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
390
23.1k
}
391
392
393
heif_error dav1d_decode_next_image(void* decoder_raw, heif_image** out_img,
394
                                   const heif_security_limits* limits)
395
0
{
396
0
  return dav1d_decode_next_image2(decoder_raw, out_img, nullptr, limits);
397
0
}
398
399
400
heif_error dav1d_decode_image(void* decoder_raw, struct heif_image** out_img)
401
0
{
402
0
  auto* limits = heif_get_global_security_limits();
403
0
  return dav1d_decode_next_image(decoder_raw, out_img, limits);
404
0
}
405
406
407
heif_error dav1d_flush_data(void* decoder_raw)
408
34.2k
{
409
34.2k
  auto* decoder = (struct dav1d_decoder*) decoder_raw;
410
411
34.2k
  constexpr Dav1dData packet{}; // empty packet
412
413
  // --- put data into queue
414
415
34.2k
  decoder->queued_data.push_back(packet);
416
417
  // --- push pending data to decoder
418
419
34.2k
  return push_pending_data_into_decoder(decoder);
420
34.2k
}
421
422
423
static const heif_decoder_plugin decoder_dav1d
424
    {
425
        6,
426
        dav1d_plugin_name,
427
        dav1d_init_plugin,
428
        dav1d_deinit_plugin,
429
        dav1d_does_support_format,
430
        dav1d_new_decoder,
431
        dav1d_free_decoder,
432
        dav1d_push_data,
433
        dav1d_decode_image,
434
        dav1d_set_strict_decoding,
435
        "dav1d",
436
        dav1d_decode_next_image,
437
        /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,22,0),
438
        dav1d_does_support_format2,
439
        dav1d_new_decoder2,
440
        dav1d_push_data2,
441
        dav1d_flush_data,
442
        dav1d_decode_next_image2
443
    };
444
445
446
const heif_decoder_plugin* get_decoder_plugin_dav1d()
447
252
{
448
252
  return &decoder_dav1d;
449
252
}
450
451
452
#if PLUGIN_DAV1D
453
heif_plugin_info plugin_info {
454
  1,
455
  heif_plugin_type_decoder,
456
  &decoder_dav1d
457
};
458
#endif