Coverage Report

Created: 2025-11-16 07:22

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 <string>
28
29
#include <aom/aom_decoder.h>
30
#include <aom/aomdx.h>
31
32
33
struct aom_decoder
34
{
35
  aom_codec_ctx_t codec;
36
  bool codec_initialized = false;
37
38
  aom_codec_iface_t* iface;
39
40
  bool strict_decoding = false;
41
  std::string error_message;
42
};
43
44
static const char kSuccess[] = "Success";
45
static const char kEmptyString[] = "";
46
47
static const int AOM_PLUGIN_PRIORITY = 100;
48
49
0
#define MAX_PLUGIN_NAME_LENGTH 80
50
51
static char plugin_name[MAX_PLUGIN_NAME_LENGTH];
52
53
54
static const char* aom_plugin_name()
55
0
{
56
0
  if (strlen(aom_codec_iface_name(aom_codec_av1_dx())) < MAX_PLUGIN_NAME_LENGTH) {
57
0
    strcpy(plugin_name, aom_codec_iface_name(aom_codec_av1_dx()));
58
0
  }
59
0
  else {
60
0
    strcpy(plugin_name, "AOMedia AV1 decoder");
61
0
  }
62
63
0
  return plugin_name;
64
0
}
65
66
67
static void aom_init_plugin()
68
252
{
69
252
}
70
71
72
static void aom_deinit_plugin()
73
0
{
74
0
}
75
76
77
static int aom_does_support_format(heif_compression_format format)
78
35.1k
{
79
35.1k
  if (format == heif_compression_AV1) {
80
34.8k
    return AOM_PLUGIN_PRIORITY;
81
34.8k
  }
82
347
  else {
83
347
    return 0;
84
347
  }
85
35.1k
}
86
87
88
heif_error aom_new_decoder(void** dec)
89
34.8k
{
90
34.8k
  aom_decoder* decoder = new aom_decoder();
91
92
34.8k
  decoder->iface = aom_codec_av1_dx();
93
94
34.8k
  aom_codec_err_t aomerr = aom_codec_dec_init(&decoder->codec, decoder->iface, NULL, 0);
95
34.8k
  if (aomerr) {
96
0
    *dec = NULL;
97
98
0
    delete decoder;
99
100
0
    heif_error err = {heif_error_Decoder_plugin_error, heif_suberror_Unspecified, aom_codec_err_to_string(aomerr)};
101
0
    return err;
102
0
  }
103
104
34.8k
  decoder->codec_initialized = true;
105
34.8k
  *dec = decoder;
106
107
34.8k
  heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
108
34.8k
  return err;
109
34.8k
}
110
111
112
void aom_free_decoder(void* decoder_raw)
113
34.8k
{
114
34.8k
  aom_decoder* decoder = (aom_decoder*) decoder_raw;
115
116
34.8k
  if (!decoder) {
117
0
    return;
118
0
  }
119
120
34.8k
  if (decoder->codec_initialized) {
121
34.8k
    aom_codec_destroy(&decoder->codec);
122
34.8k
    decoder->codec_initialized = false;
123
34.8k
  }
124
125
34.8k
  delete decoder;
126
34.8k
}
127
128
129
void aom_set_strict_decoding(void* decoder_raw, int flag)
130
34.8k
{
131
34.8k
  aom_decoder* decoder = (aom_decoder*) decoder_raw;
132
133
34.8k
  decoder->strict_decoding = flag;
134
34.8k
}
135
136
137
heif_error aom_push_data(void* decoder_raw, const void* frame_data, size_t frame_size)
138
34.3k
{
139
34.3k
  aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
140
141
34.3k
  const char* ver = aom_codec_version_str();
142
34.3k
  (void)ver;
143
144
34.3k
  aom_codec_err_t aomerr;
145
34.3k
  aomerr = aom_codec_decode(&decoder->codec, (const uint8_t*) frame_data, frame_size, NULL);
146
34.3k
  if (aomerr) {
147
31.7k
    heif_error err = {heif_error_Invalid_input, heif_suberror_Unspecified, aom_codec_err_to_string(aomerr)};
148
31.7k
    return err;
149
31.7k
  }
150
151
152
2.63k
  heif_error err = {heif_error_Ok, heif_suberror_Unspecified, kSuccess};
153
2.63k
  return err;
154
34.3k
}
155
156
157
heif_error aom_decode_next_image(void* decoder_raw, heif_image** out_img,
158
                                        const heif_security_limits* limits)
159
2.63k
{
160
2.63k
  aom_decoder* decoder = (struct aom_decoder*) decoder_raw;
161
162
2.63k
  aom_codec_iter_t iter = NULL;
163
2.63k
  aom_image_t* img = NULL;
164
165
2.63k
  img = aom_codec_get_frame(&decoder->codec, &iter);
166
167
2.63k
  if (img == NULL) {
168
68
    return {
169
68
      heif_error_Decoder_plugin_error,
170
68
      heif_suberror_Unspecified,
171
68
      kEmptyString
172
68
    };
173
68
  }
174
175
176
2.56k
  if (img->fmt != AOM_IMG_FMT_I420 &&
177
2.22k
      img->fmt != AOM_IMG_FMT_I42016 &&
178
1.83k
      img->fmt != AOM_IMG_FMT_I422 &&
179
1.82k
      img->fmt != AOM_IMG_FMT_I42216 &&
180
1.77k
      img->fmt != AOM_IMG_FMT_I444 &&
181
981
      img->fmt != AOM_IMG_FMT_I44416) {
182
0
    return {
183
0
      heif_error_Decoder_plugin_error,
184
0
      heif_suberror_Unsupported_image_type,
185
0
      kEmptyString
186
0
    };
187
0
  }
188
189
2.56k
  heif_chroma chroma;
190
2.56k
  heif_colorspace colorspace;
191
192
2.56k
  if (img->monochrome) {
193
172
      chroma = heif_chroma_monochrome;
194
172
      colorspace = heif_colorspace_monochrome;
195
172
  }
196
2.39k
  else {
197
2.39k
    if (img->fmt == AOM_IMG_FMT_I444 ||
198
1.77k
        img->fmt == AOM_IMG_FMT_I44416) {
199
1.77k
      chroma = heif_chroma_444;
200
1.77k
    }
201
621
    else if (img->fmt == AOM_IMG_FMT_I422 ||
202
609
             img->fmt == AOM_IMG_FMT_I42216) {
203
58
      chroma = heif_chroma_422;
204
58
    }
205
563
    else {
206
563
      chroma = heif_chroma_420;
207
563
    }
208
2.39k
    colorspace = heif_colorspace_YCbCr;
209
2.39k
  }
210
211
2.56k
  heif_image* heif_img = nullptr;
212
2.56k
  heif_error err = heif_image_create(img->d_w, img->d_h,
213
2.56k
                                     colorspace,
214
2.56k
                                     chroma,
215
2.56k
                                     &heif_img);
216
2.56k
  if (err.code != heif_error_Ok) {
217
0
    assert(heif_img==nullptr);
218
0
    return err;
219
0
  }
220
221
222
  // --- read nclx parameters from decoded AV1 bitstream
223
224
2.56k
  heif_color_profile_nclx nclx;
225
2.56k
  nclx.version = 1;
226
2.56k
  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); });
227
2.56k
  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); });
228
2.56k
  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); });
229
2.56k
  nclx.full_range_flag = (img->range == AOM_CR_FULL_RANGE);
230
2.56k
  heif_image_set_nclx_color_profile(heif_img, &nclx);
231
232
233
  // --- transfer data from aom_image_t to HeifPixelImage
234
235
2.56k
  heif_channel channel2plane[3] = {
236
2.56k
      heif_channel_Y,
237
2.56k
      heif_channel_Cb,
238
2.56k
      heif_channel_Cr
239
2.56k
  };
240
241
242
2.56k
  int num_planes = (chroma == heif_chroma_monochrome ? 1 : 3);
243
244
9.86k
  for (int c = 0; c < num_planes; c++) {
245
7.32k
    int bpp = img->bit_depth;
246
247
7.32k
    const uint8_t* data = img->planes[c];
248
7.32k
    int stride = img->stride[c];
249
250
7.32k
    int w = img->d_w;
251
7.32k
    int h = img->d_h;
252
253
7.32k
    if (c > 0 && chroma == heif_chroma_420) {
254
1.12k
      w = (w + 1) / 2;
255
1.12k
      h = (h + 1) / 2;
256
1.12k
    }
257
6.19k
    else if (c > 0 && chroma == heif_chroma_422) {
258
116
      w = (w + 1) / 2;
259
116
    }
260
261
7.32k
    err = heif_image_add_plane_safe(heif_img, channel2plane[c], w, h, bpp, limits);
262
7.32k
    if (err.code != heif_error_Ok) {
263
      // copy error message to decoder object because heif_image will be released
264
20
      decoder->error_message = err.message;
265
20
      err.message = decoder->error_message.c_str();
266
267
20
      heif_image_release(heif_img);
268
20
      return err;
269
20
    }
270
271
7.30k
    size_t dst_stride;
272
7.30k
    uint8_t* dst_mem = heif_image_get_plane2(heif_img, channel2plane[c], &dst_stride);
273
274
7.30k
    int bytes_per_pixel = (bpp + 7) / 8;
275
276
7.77M
    for (int y = 0; y < h; y++) {
277
7.77M
      memcpy(dst_mem + y * dst_stride, data + y * stride, w * bytes_per_pixel);
278
7.77M
    }
279
7.30k
  }
280
281
2.54k
  *out_img = heif_img;
282
2.54k
  return err;
283
2.56k
}
284
285
heif_error aom_decode_image(void* decoder_raw, heif_image** out_img)
286
0
{
287
0
  auto* limits = heif_get_global_security_limits();
288
0
  return aom_decode_next_image(decoder_raw, out_img, limits);
289
0
}
290
291
static const heif_decoder_plugin decoder_aom
292
    {
293
        4,
294
        aom_plugin_name,
295
        aom_init_plugin,
296
        aom_deinit_plugin,
297
        aom_does_support_format,
298
        aom_new_decoder,
299
        aom_free_decoder,
300
        aom_push_data,
301
        aom_decode_image,
302
        aom_set_strict_decoding,
303
        "aom",
304
        aom_decode_next_image
305
    };
306
307
308
const heif_decoder_plugin* get_decoder_plugin_aom()
309
252
{
310
252
  return &decoder_aom;
311
252
}
312
313
314
#if PLUGIN_AOM_DECODER
315
heif_plugin_info plugin_info {
316
  1,
317
  heif_plugin_type_decoder,
318
  &decoder_aom
319
};
320
#endif