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