/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 |