Coverage Report

Created: 2026-06-10 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libheif/libheif/plugins/decoder_aom.cc
Line
Count
Source
1
/*
2
 * AVIF codec.
3
 * Copyright (c) 2019 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_aom.h"
24
#include <memory>
25
#include <cstring>
26
#include <cassert>
27
#include <deque>
28
#include <string>
29
30
#include <aom/aom_decoder.h>
31
#include <aom/aomdx.h>
32
33
34
struct aom_decoder
35
{
36
  ~aom_decoder()
37
0
  {
38
    // --- free images that are still in the output queue
39
40
0
    for (auto& pkt : output_queue) {
41
0
      if (pkt.img) {
42
0
        heif_image_release(pkt.img);
43
0
      }
44
0
    }
45
0
  }
46
47
  aom_codec_ctx_t codec;
48
  bool codec_initialized = false;
49
50
  aom_codec_iface_t* iface;
51
52
  struct Packet
53
  {
54
    heif_image* img;
55
    uintptr_t user_data = 0;
56
  };
57
58
  std::deque<Packet> output_queue;
59
  bool strict_decoding = false;
60
  std::string error_message;
61
  const heif_security_limits* limits = nullptr;
62
};
63
64
static const char kSuccess[] = "Success";
65
static const char kEmptyString[] = "";
66
67
static const int AOM_PLUGIN_PRIORITY = 100;
68
69
0
#define MAX_PLUGIN_NAME_LENGTH 80
70
71
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
72
73
74
static const char* aom_plugin_name()
75
0
{
76
0
  if (strlen(aom_codec_iface_name(aom_codec_av1_dx())) < MAX_PLUGIN_NAME_LENGTH) {
77
0
    strcpy(plugin_name, aom_codec_iface_name(aom_codec_av1_dx()));
78
0
  }
79
0
  else {
80
0
    strcpy(plugin_name, "AOMedia AV1 decoder");
81
0
  }
82
83
0
  return plugin_name;
84
0
}
85
86
87
static void aom_init_plugin()
88
11
{
89
11
}
90
91
92
static void aom_deinit_plugin()
93
0
{
94
0
}
95
96
97
static int aom_does_support_format(heif_compression_format format)
98
47.2k
{
99
47.2k
  if (format == heif_compression_AV1) {
100
43.8k
    return AOM_PLUGIN_PRIORITY;
101
43.8k
  }
102
3.43k
  else {
103
3.43k
    return 0;
104
3.43k
  }
105
47.2k
}
106
107
static int aom_does_support_format2(const heif_decoder_plugin_compressed_format_description* format)
108
0
{
109
0
  return aom_does_support_format(format->format);
110
0
}
111
112
heif_error aom_new_decoder2(void** dec, const heif_decoder_plugin_options* options)
113
0
{
114
0
  aom_decoder* decoder = new aom_decoder();
115
116
0
  decoder->iface = aom_codec_av1_dx();
117
118
0
  aom_codec_dec_cfg_t cfg{};
119
120
0
  if (options->num_threads) {
121
0
    cfg.threads = options->num_threads;
122
0
  }
123
0
  cfg.w = 0;
124
0
  cfg.h = 0;
125
0
  cfg.allow_lowbitdepth = 1;
126
127
0
  aom_codec_err_t aomerr = aom_codec_dec_init(&decoder->codec, decoder->iface, &cfg, 0);
128
0
  if (aomerr) {
129
0
    *dec = NULL;
130
131
0
    delete decoder;
132
133
0
    heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, aom_codec_err_to_string(aomerr)};
134
0
    return err;
135
0
  }
136
137
0
  decoder->codec_initialized = true;
138
0
  decoder->limits = options->limits;
139
0
  *dec = decoder;
140
141
0
  heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
142
0
  return err;
143
0
}
144
145
146
heif_error aom_new_decoder(void** dec)
147
0
{
148
0
  heif_decoder_plugin_options options{};
149
0
  options.format = heif_compression_AV1;
150
0
  options.strict_decoding = false;
151
0
  options.num_threads = 0;
152
153
0
  return aom_new_decoder2(dec, &options);
154
0
}
155
156
void aom_free_decoder(void* decoder_raw)
157
0
{
158
0
  aom_decoder* decoder = (aom_decoder*) decoder_raw;
159
160
0
  if (!decoder) {
161
0
    return;
162
0
  }
163
164
0
  if (decoder->codec_initialized) {
165
0
    aom_codec_destroy(&decoder->codec);
166
0
    decoder->codec_initialized = false;
167
0
  }
168
169
0
  delete decoder;
170
0
}
171
172
173
void aom_set_strict_decoding(void* decoder_raw, int flag)
174
0
{
175
0
  aom_decoder* decoder = (aom_decoder*) decoder_raw;
176
177
0
  decoder->strict_decoding = flag;
178
0
}
179
180
181
static heif_error get_next_image_from_decoder(aom_decoder* decoder,
182
                                              aom_codec_iter_t* inout_iter,
183
                                              heif_image** out_img,
184
                                              uintptr_t* out_user_data,
185
                                              const heif_security_limits* limits)
186
0
{
187
0
  aom_image_t* img = aom_codec_get_frame(&decoder->codec, inout_iter);
188
189
0
  if (img == NULL) {
190
0
    *out_img = NULL;
191
0
    return {};
192
0
  }
193
194
0
  if (out_user_data) {
195
0
    *out_user_data = (uintptr_t)img->user_priv;
196
0
  }
197
198
  /*
199
  if (img == NULL) {
200
    return {
201
      heif_error_Decoder_plugin_error,
202
      heif_suberror_Unspecified,
203
      kEmptyString
204
    };
205
  }
206
*/
207
208
0
  if (img->fmt != AOM_IMG_FMT_I420 &&
209
0
      img->fmt != AOM_IMG_FMT_I42016 &&
210
0
      img->fmt != AOM_IMG_FMT_I422 &&
211
0
      img->fmt != AOM_IMG_FMT_I42216 &&
212
0
      img->fmt != AOM_IMG_FMT_I444 &&
213
0
      img->fmt != AOM_IMG_FMT_I44416) {
214
0
    return {
215
0
      heif_error_Decoder_plugin_error,
216
0
      heif_suberror_Unsupported_image_type,
217
0
      kEmptyString
218
0
    };
219
0
  }
220
221
0
  heif_chroma chroma;
222
0
  heif_colorspace colorspace;
223
224
0
  if (img->monochrome) {
225
0
      chroma = heif_chroma_monochrome;
226
0
      colorspace = heif_colorspace_monochrome;
227
0
  }
228
0
  else {
229
0
    if (img->fmt == AOM_IMG_FMT_I444 ||
230
0
        img->fmt == AOM_IMG_FMT_I44416) {
231
0
      chroma = heif_chroma_444;
232
0
    }
233
0
    else if (img->fmt == AOM_IMG_FMT_I422 ||
234
0
             img->fmt == AOM_IMG_FMT_I42216) {
235
0
      chroma = heif_chroma_422;
236
0
    }
237
0
    else {
238
0
      chroma = heif_chroma_420;
239
0
    }
240
0
    colorspace = heif_colorspace_YCbCr;
241
0
  }
242
243
0
  heif_image* heif_img = nullptr;
244
0
  heif_error err = heif_image_create(img->d_w, img->d_h,
245
0
                                     colorspace,
246
0
                                     chroma,
247
0
                                     &heif_img);
248
0
  if (err.code != heif_error_Ok) {
249
0
    assert(heif_img==nullptr);
250
0
    return err;
251
0
  }
252
253
254
  // --- read nclx parameters from decoded AV1 bitstream
255
256
0
  heif_color_profile_nclx nclx;
257
0
  nclx.version = 1;
258
0
  HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_color_primaries(&nclx, static_cast<uint16_t>(img->cp)), { heif_image_release(heif_img); });
259
0
  HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_transfer_characteristics(&nclx, static_cast<uint16_t>(img->tc)), { heif_image_release(heif_img); });
260
0
  HEIF_WARN_OR_FAIL(decoder->strict_decoding, heif_img, heif_nclx_color_profile_set_matrix_coefficients(&nclx, static_cast<uint16_t>(img->mc)), { heif_image_release(heif_img); });
261
0
  nclx.full_range_flag = (img->range == AOM_CR_FULL_RANGE);
262
0
  heif_image_set_nclx_color_profile(heif_img, &nclx);
263
264
265
  // --- transfer data from aom_image_t to HeifPixelImage
266
267
0
  heif_channel channel2plane[3] = {
268
0
      heif_channel_Y,
269
0
      heif_channel_Cb,
270
0
      heif_channel_Cr
271
0
  };
272
273
274
0
  int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3);
275
276
0
  for (int c = 0; c < num_planes; c++) {
277
0
    int bpp = img->bit_depth;
278
279
0
    const uint8_t* data = img->planes[c];
280
0
    int stride = img->stride[c];
281
282
0
    int w = img->d_w;
283
0
    int h = img->d_h;
284
285
0
    if (c > 0 && chroma == heif_chroma_420) {
286
0
      w = (w + 1) / 2;
287
0
      h = (h + 1) / 2;
288
0
    }
289
0
    else if (c > 0 && chroma == heif_chroma_422) {
290
0
      w = (w + 1) / 2;
291
0
    }
292
293
0
    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
294
0
    if (err.code != heif_error_Ok) {
295
      // copy error message to decoder object because heif_image will be released
296
0
      decoder->error_message = err.message;
297
0
      err.message = decoder->error_message.c_str();
298
299
0
      heif_image_release(heif_img);
300
0
      return err;
301
0
    }
302
303
0
    size_t dst_stride;
304
0
    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
305
306
0
    int bytes_per_pixel = (bpp + 7) / 8;
307
308
0
    for (int y = 0; y < h; y++) {
309
0
      memcpy(dst_mem + y * dst_stride, data + static_cast<size_t>(y) * stride, static_cast<size_t>(w) * bytes_per_pixel);
310
0
    }
311
0
  }
312
313
0
  *out_img = heif_img;
314
0
  return err;
315
0
}
316
317
318
319
heif_error aom_push_data2(void* decoder_raw, const void* frame_data, size_t frame_size, uintptr_t user_data)
320
0
{
321
0
  aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
322
323
0
  const char* ver = aom_codec_version_str();
324
0
  (void)ver;
325
326
0
  aom_codec_err_t aomerr;
327
0
  aomerr = aom_codec_decode(&decoder->codec, (const uint8_t*) frame_data, frame_size, (void*)user_data);
328
0
  if (aomerr) {
329
0
    heif_error err = {heif_error_Invalid_input, heif_suberror_Unspecified, aom_codec_err_to_string(aomerr)};
330
0
    return err;
331
0
  }
332
333
334
  // --- decode images and fill into output queue
335
336
0
  aom_codec_iter_t iter = NULL;
337
0
  for (;;) {
338
0
    heif_image* img;
339
0
    uintptr_t out_user_data;
340
0
    heif_error err = get_next_image_from_decoder(decoder, &iter, &img, &out_user_data, decoder->limits);
341
0
    if (err.code) {
342
0
      return err;
343
0
    }
344
345
0
    if (img) {
346
0
      decoder->output_queue.push_back({img, out_user_data});
347
0
    }
348
0
    else {
349
0
      break;
350
0
    }
351
0
  }
352
353
0
  heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
354
0
  return err;
355
0
}
356
357
358
heif_error aom_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
359
0
{
360
0
  return aom_push_data2(decoder_raw, frame_data, frame_size, 0);
361
0
}
362
363
364
static heif_error aom_decode_next_image2(void* decoder_raw, heif_image** out_img,
365
                                         uintptr_t* out_user_data,
366
                                         const heif_security_limits* limits)
367
0
{
368
0
  aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
369
370
  // --- if there are images in the output queue, pass them back
371
372
0
  if (!decoder->output_queue.empty()) {
373
0
    if (out_user_data) {
374
0
      *out_user_data = decoder->output_queue.front().user_data;
375
0
    }
376
377
0
    heif_image* next_image = decoder->output_queue.front().img;
378
0
    decoder->output_queue.pop_front();
379
380
0
    *out_img = next_image;
381
0
    return {};
382
0
  }
383
0
  else {
384
0
    *out_img = nullptr;
385
0
    return {};
386
0
  }
387
0
}
388
389
390
static heif_error aom_decode_next_image(void* decoder_raw, heif_image** out_img,
391
                                        const heif_security_limits* limits)
392
0
{
393
0
  return aom_decode_next_image2(decoder_raw, out_img, nullptr, limits);
394
0
}
395
396
heif_error aom_decode_image(void* decoder_raw, heif_image** out_img)
397
0
{
398
0
  auto* limits = heif_get_global_security_limits();
399
0
  return aom_decode_next_image(decoder_raw, out_img, limits);
400
0
}
401
402
403
static heif_error aom_flush_data(void* decoder_raw)
404
0
{
405
  // aom_decoder* decoder = (aom_decoder*) decoder_raw;
406
407
0
  return heif_error_ok;
408
0
}
409
410
411
static const heif_decoder_plugin decoder_aom
412
    {
413
        6,
414
        aom_plugin_name,
415
        aom_init_plugin,
416
        aom_deinit_plugin,
417
        aom_does_support_format,
418
        aom_new_decoder,
419
        aom_free_decoder,
420
        aom_push_data,
421
        aom_decode_image,
422
        aom_set_strict_decoding,
423
        "aom",
424
        aom_decode_next_image,
425
        /* minimum_required_libheif_version */ LIBHEIF_MAKE_VERSION(1,22,0),
426
        aom_does_support_format2,
427
        aom_new_decoder2,
428
        aom_push_data2,
429
        aom_flush_data,
430
        aom_decode_next_image2
431
    };
432
433
434
const heif_decoder_plugin* get_decoder_plugin_aom()
435
11
{
436
11
  return &decoder_aom;
437
11
}
438
439
440
#if PLUGIN_AOM_DECODER
441
heif_plugin_info plugin_info {
442
  1,
443
  heif_plugin_type_decoder,
444
  &decoder_aom
445
};
446
#endif