Coverage Report

Created: 2026-07-16 06:29

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
0
#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
0
{
58
0
  snprintf(plugin_name, MAX_PLUGIN_NAME_LENGTH, "dav1d v%d.%d.%d",
59
0
           DAV1D_API_VERSION_MAJOR,
60
0
           DAV1D_API_VERSION_MINOR,
61
0
           DAV1D_API_VERSION_PATCH);
62
63
  // make sure that the string is null-terminated
64
0
  plugin_name[MAX_PLUGIN_NAME_LENGTH - 1] = 0;
65
66
0
  return plugin_name;
67
0
}
68
69
70
static void dav1d_init_plugin()
71
2
{
72
2
}
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
26.6k
{
82
26.6k
  if (format == heif_compression_AV1) {
83
25.0k
    return DAV1D_PLUGIN_PRIORITY;
84
25.0k
  }
85
1.57k
  else {
86
1.57k
    return 0;
87
1.57k
  }
88
26.6k
}
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
25.7k
{
98
25.7k
  auto* decoder = new dav1d_decoder();
99
100
25.7k
  dav1d_default_settings(&decoder->settings);
101
102
25.7k
  const heif_security_limits* limits = options->limits ? options->limits : heif_get_global_security_limits();
103
25.7k
  if (limits->max_image_size_pixels > std::numeric_limits<unsigned int>::max()) {
104
0
    decoder->settings.frame_size_limit = 0;
105
0
  }
106
25.7k
  else {
107
25.7k
    decoder->settings.frame_size_limit = static_cast<unsigned int>(limits->max_image_size_pixels);
108
25.7k
  }
109
110
25.7k
  decoder->settings.all_layers = 0;
111
112
25.7k
  if (options->num_threads) {
113
0
    decoder->settings.n_threads = options->num_threads;
114
0
  }
115
116
25.7k
  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
25.7k
  *dec = decoder;
122
123
25.7k
  return heif_error_ok;
124
25.7k
}
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
25.7k
{
138
25.7k
  auto* decoder = (dav1d_decoder*) decoder_raw;
139
140
25.7k
  if (!decoder) {
141
0
    return;
142
0
  }
143
144
  // free queued data
145
146
25.7k
  for (auto& pkt : decoder->queued_data) {
147
19
    dav1d_data_unref(&pkt);
148
19
  }
149
25.7k
  decoder->queued_data.clear();
150
151
  // free decoder context
152
153
25.7k
  if (decoder->context) {
154
25.7k
    dav1d_close(&decoder->context);
155
25.7k
  }
156
157
25.7k
  delete decoder;
158
25.7k
}
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
149k
{
171
192k
  while (!decoder->queued_data.empty()) {
172
173
    // send data
174
175
47.1k
    int res = dav1d_send_data(decoder->context, &decoder->queued_data.front());
176
177
    // decoder does not accept more data at this moment
178
179
47.1k
    if (res == DAV1D_ERR(EAGAIN)) {
180
38
      break;
181
38
    }
182
183
47.1k
    if (res < 0) {
184
      // dav1d_send_data failed. Data was not consumed, unref before removing from queue.
185
4.30k
      dav1d_data_unref(&decoder->queued_data.front());
186
4.30k
      decoder->queued_data.pop_front();
187
4.30k
      return {
188
4.30k
        heif_error_Decoder_plugin_error,
189
4.30k
        heif_suberror_Unspecified,
190
4.30k
        kEmptyString
191
4.30k
      };
192
4.30k
    }
193
194
    // Decoder has accepted data (Dav1dData is consumed). Remove from queue.
195
42.8k
    decoder->queued_data.pop_front();
196
42.8k
  }
197
198
145k
  return heif_error_ok;
199
149k
}
200
201
202
heif_error dav1d_push_data2(void* decoder_raw, const void* frame_data, size_t frame_size, uintptr_t user_data)
203
25.7k
{
204
25.7k
  auto* decoder = (struct dav1d_decoder*) decoder_raw;
205
206
  // --- copy input data into Dav1dData packet
207
208
25.7k
  Dav1dData packet{};
209
210
25.7k
  uint8_t* d = dav1d_data_create(&packet, frame_size);
211
25.7k
  if (d == nullptr) {
212
0
    return {heif_error_Memory_allocation_error, heif_suberror_Unspecified, kSuccess};
213
0
  }
214
215
25.7k
  memcpy(d, frame_data, frame_size);
216
217
25.7k
  packet.m.user_data.data = (uint8_t*)user_data;
218
219
  // --- put data into queue
220
221
25.7k
  decoder->queued_data.push_back(packet);
222
223
  // --- push pending data to decoder
224
225
25.7k
  return push_pending_data_into_decoder(decoder);
226
25.7k
}
227
228
229
heif_error dav1d_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
230
0
{
231
0
  return dav1d_push_data2(decoder_raw, frame_data, frame_size, 0);
232
0
}
233
234
235
heif_error dav1d_decode_next_image2(void* decoder_raw, heif_image** out_img,
236
                                    uintptr_t* out_user_data,
237
                                    const heif_security_limits* limits)
238
102k
{
239
102k
  auto* decoder = (struct dav1d_decoder*) decoder_raw;
240
241
102k
  heif_error err;
242
243
102k
  Dav1dPicture frame{};
244
245
102k
  for (;;) {
246
247
    // --- send more pending data to decoder
248
249
102k
    err = push_pending_data_into_decoder(decoder);
250
102k
    if (err.code) {
251
0
      return err;
252
0
    }
253
254
    // --- try to get decoded image
255
256
102k
    int res = dav1d_get_picture(decoder->context, &frame);
257
258
    // We got a picture from the decoder. Continue with processing it.
259
102k
    if (res == 0) {
260
13.8k
      break;
261
13.8k
    }
262
263
    // decoder wants more data, but queue is empty
264
88.7k
    if (res == DAV1D_ERR(EAGAIN) && decoder->queued_data.empty()) {
265
82.3k
      *out_img = nullptr;
266
82.3k
      return heif_error_ok;
267
82.3k
    }
268
269
    // continue feeding more data from queue
270
6.34k
    if (res == DAV1D_ERR(EAGAIN)) {
271
0
      continue;
272
0
    }
273
274
    // decoder error
275
6.34k
    if (res < 0) {
276
6.34k
      return {
277
6.34k
        heif_error_Decoder_plugin_error,
278
6.34k
        heif_suberror_Unspecified,
279
6.34k
        kEmptyString
280
6.34k
      };
281
6.34k
    }
282
6.34k
  }
283
284
  // --- convert image to heif_image
285
286
13.8k
  heif_chroma chroma;
287
13.8k
  heif_colorspace colorspace;
288
13.8k
  switch (frame.p.layout) {
289
3.60k
    case DAV1D_PIXEL_LAYOUT_I420:
290
3.60k
      chroma = heif_chroma_420;
291
3.60k
      colorspace = heif_colorspace_YCbCr;
292
3.60k
      break;
293
604
    case DAV1D_PIXEL_LAYOUT_I422:
294
604
      chroma = heif_chroma_422;
295
604
      colorspace = heif_colorspace_YCbCr;
296
604
      break;
297
7.66k
    case DAV1D_PIXEL_LAYOUT_I444:
298
7.66k
      chroma = heif_chroma_444;
299
7.66k
      colorspace = heif_colorspace_YCbCr;
300
7.66k
      break;
301
1.96k
    case DAV1D_PIXEL_LAYOUT_I400:
302
1.96k
      chroma = heif_chroma_monochrome;
303
1.96k
      colorspace = heif_colorspace_monochrome;
304
1.96k
      break;
305
0
    default: {
306
0
      dav1d_picture_unref(&frame);
307
0
      return {
308
0
        heif_error_Decoder_plugin_error,
309
0
        heif_suberror_Unspecified,
310
0
        kEmptyString
311
0
      };
312
0
    }
313
13.8k
  }
314
315
13.8k
  if (out_user_data) {
316
0
    *out_user_data = (uintptr_t)frame.m.user_data.data;
317
0
  }
318
319
13.8k
  heif_image* heif_img = nullptr;
320
13.8k
  err = heif_image_create(frame.p.w, frame.p.h,
321
13.8k
                          colorspace,
322
13.8k
                          chroma,
323
13.8k
                          &heif_img);
324
13.8k
  if (err.code != heif_error_Ok) {
325
0
    assert(heif_img == nullptr);
326
0
    dav1d_picture_unref(&frame);
327
0
    return err;
328
0
  }
329
330
331
  // --- read nclx parameters from decoded AV1 bitstream
332
333
13.8k
  heif_color_profile_nclx nclx;
334
13.8k
  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)), {});
335
13.8k
  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)), {});
336
13.8k
  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)), {});
337
13.8k
  nclx.full_range_flag = (frame.seq_hdr->color_range != 0);
338
13.8k
  heif_image_set_nclx_color_profile(heif_img, &nclx);
339
340
341
13.8k
  heif_channel channel2plane[3] = {
342
13.8k
      heif_channel_Y,
343
13.8k
      heif_channel_Cb,
344
13.8k
      heif_channel_Cr
345
13.8k
  };
346
347
348
  // --- copy image data
349
350
13.8k
  int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3);
351
352
51.4k
  for (int c = 0; c < num_planes; c++) {
353
37.5k
    int bpp = frame.p.bpc;
354
355
37.5k
    const uint8_t* data = (uint8_t*) frame.data[c];
356
37.5k
    int stride = (int) frame.stride[c > 0 ? 1 : 0];
357
358
37.5k
    uint32_t w, h;
359
37.5k
    get_subsampled_size(frame.p.w, frame.p.h,
360
37.5k
                        channel2plane[c], chroma, &w, &h);
361
362
37.5k
    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
363
37.5k
    if (err.code != heif_error_Ok) {
364
      // copy error message to decoder object because heif_image will be released
365
0
      decoder->error_message = err.message;
366
0
      err.message = decoder->error_message.c_str();
367
368
0
      heif_image_release(heif_img);
369
0
      dav1d_picture_unref(&frame);
370
0
      return err;
371
0
    }
372
373
37.5k
    size_t dst_stride;
374
37.5k
    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
375
376
37.5k
    const int bytes_per_pixel = (bpp + 7) / 8;
377
378
12.0M
    for (uint32_t y = 0; y < h; y++) {
379
11.9M
      memcpy(dst_mem + y * dst_stride, data + static_cast<size_t>(y) * stride, static_cast<size_t>(w) * bytes_per_pixel);
380
11.9M
    }
381
37.5k
  }
382
383
13.8k
  dav1d_picture_unref(&frame);
384
385
13.8k
  *out_img = heif_img;
386
387
388
13.8k
  return {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
389
13.8k
}
390
391
392
heif_error dav1d_decode_next_image(void* decoder_raw, heif_image** out_img,
393
                                   const heif_security_limits* limits)
394
0
{
395
0
  return dav1d_decode_next_image2(decoder_raw, out_img, nullptr, limits);
396
0
}
397
398
399
heif_error dav1d_decode_image(void* decoder_raw, struct heif_image** out_img)
400
0
{
401
0
  auto* limits = heif_get_global_security_limits();
402
0
  return dav1d_decode_next_image(decoder_raw, out_img, limits);
403
0
}
404
405
406
heif_error dav1d_flush_data(void* decoder_raw)
407
21.4k
{
408
21.4k
  auto* decoder = (struct dav1d_decoder*) decoder_raw;
409
410
21.4k
  constexpr Dav1dData packet{}; // empty packet
411
412
  // --- put data into queue
413
414
21.4k
  decoder->queued_data.push_back(packet);
415
416
  // --- push pending data to decoder
417
418
21.4k
  return push_pending_data_into_decoder(decoder);
419
21.4k
}
420
421
422
static const heif_decoder_plugin decoder_dav1d
423
    {
424
        6,
425
        dav1d_plugin_name,
426
        dav1d_init_plugin,
427
        dav1d_deinit_plugin,
428
        dav1d_does_support_format,
429
        dav1d_new_decoder,
430
        dav1d_free_decoder,
431
        dav1d_push_data,
432
        dav1d_decode_image,
433
        dav1d_set_strict_decoding,
434
        "dav1d",
435
        dav1d_decode_next_image,
436
        /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,22,0),
437
        dav1d_does_support_format2,
438
        dav1d_new_decoder2,
439
        dav1d_push_data2,
440
        dav1d_flush_data,
441
        dav1d_decode_next_image2
442
    };
443
444
445
const heif_decoder_plugin* get_decoder_plugin_dav1d()
446
2
{
447
2
  return &decoder_dav1d;
448
2
}
449
450
451
#if PLUGIN_DAV1D
452
heif_plugin_info plugin_info {
453
  1,
454
  heif_plugin_type_decoder,
455
  &decoder_dav1d
456
};
457
#endif