/src/libheif/libheif/codecs/decoder.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2024 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 "codecs/decoder.h" |
22 | | |
23 | | #include <utility> |
24 | | #include "error.h" |
25 | | #include "context.h" |
26 | | #include "plugin_registry.h" |
27 | | #include "api_structs.h" |
28 | | #include "security_limits.h" |
29 | | |
30 | | #include "codecs/hevc_dec.h" |
31 | | #include "codecs/avif_dec.h" |
32 | | #include "codecs/avc_dec.h" |
33 | | #include "codecs/vvc_dec.h" |
34 | | #include "codecs/jpeg_dec.h" |
35 | | #include "codecs/jpeg2000_dec.h" |
36 | | #include "avc_boxes.h" |
37 | | #include "avif_boxes.h" |
38 | | #include "hevc_boxes.h" |
39 | | #include "vvc_boxes.h" |
40 | | #include "jpeg_boxes.h" |
41 | | #include "jpeg2000_boxes.h" |
42 | | |
43 | | #if WITH_UNCOMPRESSED_CODEC |
44 | | #include "codecs/uncompressed/unc_dec.h" |
45 | | #include "codecs/uncompressed/unc_boxes.h" |
46 | | #endif |
47 | | |
48 | | void DataExtent::set_from_image_item(std::shared_ptr<HeifFile> file, heif_item_id item) |
49 | 29.1k | { |
50 | 29.1k | m_file = std::move(file); |
51 | 29.1k | m_item_id = item; |
52 | 29.1k | m_source = Source::Image; |
53 | 29.1k | } |
54 | | |
55 | | |
56 | | void DataExtent::set_file_range(std::shared_ptr<HeifFile> file, uint64_t offset, uint32_t size) |
57 | 0 | { |
58 | 0 | m_file = std::move(file); |
59 | 0 | m_source = Source::FileRange; |
60 | 0 | m_offset = offset; |
61 | 0 | m_size = size; |
62 | 0 | } |
63 | | |
64 | | |
65 | | Result<std::vector<uint8_t>*> DataExtent::read_data() const |
66 | 12.7k | { |
67 | 12.7k | if (!m_raw.empty()) { |
68 | 32 | return &m_raw; |
69 | 32 | } |
70 | 12.7k | else if (m_source == Source::Image) { |
71 | 12.7k | assert(m_file); |
72 | | |
73 | | // image |
74 | 12.7k | Error err = m_file->append_data_from_iloc(m_item_id, m_raw); |
75 | 12.7k | if (err) { |
76 | 594 | return err; |
77 | 594 | } |
78 | | |
79 | | // Account the (now-known) buffer size against the file's total-memory budget. |
80 | | // append_data_from_iloc has already enforced max_memory_block_size per extent. |
81 | 12.1k | if (auto memErr = m_raw_memory_handle.alloc(m_raw.size(), m_file->get_security_limits(), |
82 | 12.1k | "decoder input buffer (iloc)")) { |
83 | 0 | m_raw.clear(); |
84 | 0 | m_raw.shrink_to_fit(); |
85 | 0 | return memErr; |
86 | 0 | } |
87 | 12.1k | } |
88 | 1 | else { |
89 | 1 | assert(m_file); |
90 | | |
91 | | // Reserve the buffer in the total-memory tracker before allocating it. |
92 | | // This also enforces max_memory_block_size and rejects sizes that would |
93 | | // exceed max_total_memory across all concurrently-live DataExtents. |
94 | 0 | if (auto memErr = m_raw_memory_handle.alloc(m_size, m_file->get_security_limits(), |
95 | 0 | "decoder input buffer (sample)")) { |
96 | 0 | return memErr; |
97 | 0 | } |
98 | | |
99 | | // file range |
100 | 0 | Error err = m_file->append_data_from_file_range(m_raw, m_offset, m_size); |
101 | 0 | if (err) { |
102 | 0 | m_raw_memory_handle.free(); |
103 | 0 | return err; |
104 | 0 | } |
105 | 0 | } |
106 | | |
107 | 12.1k | return &m_raw; |
108 | 12.7k | } |
109 | | |
110 | | |
111 | | Result<std::vector<uint8_t>> DataExtent::read_data(uint64_t offset, uint64_t size) const |
112 | 0 | { |
113 | 0 | std::vector<uint8_t> data; |
114 | |
|
115 | 0 | if (!m_raw.empty()) { |
116 | | // No caller currently reaches this cached path with an out-of-range request, so |
117 | | // hitting it indicates an internal logic error rather than malformed input. Guard |
118 | | // it defensively anyway. The subtraction form avoids a uint64_t wrap in |
119 | | // 'offset + size' that would otherwise allow an out-of-bounds read below. |
120 | | // TODO: this would be better reported as an internal error; change it once we have |
121 | | // a dedicated error code for that. |
122 | 0 | if (offset > m_raw.size() || size > m_raw.size() - offset) { |
123 | 0 | return Error{heif_error_Invalid_input, |
124 | 0 | heif_suberror_End_of_data, |
125 | 0 | "Requested data range exceeds the cached extent buffer"}; |
126 | 0 | } |
127 | 0 | data.insert(data.begin(), m_raw.begin() + offset, m_raw.begin() + offset + size); |
128 | 0 | return data; |
129 | 0 | } |
130 | 0 | else if (m_source == Source::Image) { |
131 | | // TODO: cache data |
132 | | |
133 | | // image |
134 | 0 | Error err = m_file->append_data_from_iloc(m_item_id, data, offset, size); |
135 | 0 | if (err) { |
136 | 0 | return err; |
137 | 0 | } |
138 | 0 | return data; |
139 | 0 | } |
140 | 0 | else { |
141 | | // file range |
142 | 0 | Error err = m_file->append_data_from_file_range(data, m_offset, m_size); |
143 | 0 | if (err) { |
144 | 0 | return err; |
145 | 0 | } |
146 | | |
147 | 0 | return data; |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | | |
152 | | std::shared_ptr<Decoder> Decoder::alloc_for_infe_type(const ImageItem* item) |
153 | 0 | { |
154 | 0 | uint32_t format_4cc = item->get_infe_type(); |
155 | |
|
156 | 0 | switch (format_4cc) { |
157 | 0 | case fourcc("hvc1"): { |
158 | 0 | auto hvcC = item->get_property<Box_hvcC>(); |
159 | 0 | if (!hvcC) return nullptr; |
160 | 0 | return std::make_shared<Decoder_HEVC>(hvcC); |
161 | 0 | } |
162 | 0 | case fourcc("av01"): { |
163 | 0 | auto av1C = item->get_property<Box_av1C>(); |
164 | 0 | if (!av1C) return nullptr; |
165 | 0 | return std::make_shared<Decoder_AVIF>(av1C); |
166 | 0 | } |
167 | 0 | case fourcc("avc1"): { |
168 | 0 | auto avcC = item->get_property<Box_avcC>(); |
169 | 0 | if (!avcC) return nullptr; |
170 | 0 | return std::make_shared<Decoder_AVC>(avcC); |
171 | 0 | } |
172 | 0 | case fourcc("j2k1"): { |
173 | 0 | auto j2kH = item->get_property<Box_j2kH>(); |
174 | 0 | return std::make_shared<Decoder_JPEG2000>(j2kH); |
175 | 0 | } |
176 | 0 | case fourcc("vvc1"): { |
177 | 0 | auto vvcC = item->get_property<Box_vvcC>(); |
178 | 0 | if (!vvcC) return nullptr; |
179 | 0 | return std::make_shared<Decoder_VVC>(vvcC); |
180 | 0 | } |
181 | 0 | case fourcc("jpeg"): { |
182 | 0 | auto jpgC = item->get_property<Box_jpgC>(); |
183 | 0 | return std::make_shared<Decoder_JPEG>(jpgC); |
184 | 0 | } |
185 | | #if WITH_UNCOMPRESSED_CODEC |
186 | | case fourcc("unci"): { |
187 | | auto uncC = item->get_property<Box_uncC>(); |
188 | | auto cmpd = item->get_property<Box_cmpd>(); |
189 | | auto ispe = item->get_property<Box_ispe>(); |
190 | | auto decoder = std::make_shared<Decoder_uncompressed>(uncC, cmpd, ispe); |
191 | | decoder->set_cpat(item->get_property<Box_cpat>()); |
192 | | decoder->set_cmpC(item->get_property<Box_cmpC>()); |
193 | | decoder->set_icef(item->get_property<Box_icef>()); |
194 | | decoder->set_cloc(item->get_property<Box_cloc>()); |
195 | | decoder->set_splz(item->get_all_properties<Box_splz>()); |
196 | | decoder->set_sbpm(item->get_all_properties<Box_sbpm>()); |
197 | | decoder->set_snuc(item->get_all_properties<Box_snuc>()); |
198 | | return decoder; |
199 | | } |
200 | | #endif |
201 | 0 | case fourcc("mski"): { |
202 | 0 | return nullptr; // do we need a decoder for this? |
203 | 0 | } |
204 | 0 | default: |
205 | 0 | return nullptr; |
206 | 0 | } |
207 | 0 | } |
208 | | |
209 | | |
210 | | std::shared_ptr<Decoder> Decoder::alloc_for_sequence_sample_description_box(std::shared_ptr<const Box_VisualSampleEntry> sample_description_box) |
211 | 0 | { |
212 | 0 | std::string compressor = sample_description_box->get_VisualSampleEntry_const().compressorname; |
213 | 0 | uint32_t sampleType = sample_description_box->get_short_type(); |
214 | |
|
215 | 0 | switch (sampleType) { |
216 | 0 | case fourcc("hvc1"): { |
217 | 0 | auto hvcC = sample_description_box->get_child_box<Box_hvcC>(); |
218 | 0 | if (!hvcC) return nullptr; |
219 | 0 | return std::make_shared<Decoder_HEVC>(hvcC); |
220 | 0 | } |
221 | | |
222 | 0 | case fourcc("av01"): { |
223 | 0 | auto av1C = sample_description_box->get_child_box<Box_av1C>(); |
224 | 0 | if (!av1C) return nullptr; |
225 | 0 | return std::make_shared<Decoder_AVIF>(av1C); |
226 | 0 | } |
227 | | |
228 | 0 | case fourcc("vvc1"): { |
229 | 0 | auto vvcC = sample_description_box->get_child_box<Box_vvcC>(); |
230 | 0 | if (!vvcC) return nullptr; |
231 | 0 | return std::make_shared<Decoder_VVC>(vvcC); |
232 | 0 | } |
233 | | |
234 | 0 | case fourcc("avc1"): { |
235 | 0 | auto avcC = sample_description_box->get_child_box<Box_avcC>(); |
236 | 0 | if (!avcC) return nullptr; |
237 | 0 | return std::make_shared<Decoder_AVC>(avcC); |
238 | 0 | } |
239 | | |
240 | | #if WITH_UNCOMPRESSED_CODEC |
241 | | case fourcc("uncv"): { |
242 | | auto uncC = sample_description_box->get_child_box<Box_uncC>(); |
243 | | auto cmpd = sample_description_box->get_child_box<Box_cmpd>(); |
244 | | auto ispe = std::make_shared<Box_ispe>(); |
245 | | ispe->set_size(sample_description_box->get_VisualSampleEntry_const().width, |
246 | | sample_description_box->get_VisualSampleEntry_const().height); |
247 | | auto decoder = std::make_shared<Decoder_uncompressed>(uncC, cmpd, ispe); |
248 | | decoder->set_cpat(sample_description_box->get_child_box<Box_cpat>()); |
249 | | decoder->set_cmpC(sample_description_box->get_child_box<Box_cmpC>()); |
250 | | decoder->set_icef(sample_description_box->get_child_box<Box_icef>()); |
251 | | decoder->set_cloc(sample_description_box->get_child_box<Box_cloc>()); |
252 | | decoder->set_splz(sample_description_box->get_child_boxes<Box_splz>()); |
253 | | decoder->set_sbpm(sample_description_box->get_child_boxes<Box_sbpm>()); |
254 | | decoder->set_snuc(sample_description_box->get_child_boxes<Box_snuc>()); |
255 | | return decoder; |
256 | | } |
257 | | #endif |
258 | | |
259 | 0 | case fourcc("j2ki"): { |
260 | 0 | auto j2kH = sample_description_box->get_child_box<Box_j2kH>(); |
261 | 0 | return std::make_shared<Decoder_JPEG2000>(j2kH); |
262 | 0 | } |
263 | | |
264 | 0 | case fourcc("mjpg"): { |
265 | 0 | auto jpgC = sample_description_box->get_child_box<Box_jpgC>(); |
266 | 0 | return std::make_shared<Decoder_JPEG>(jpgC); |
267 | 0 | } |
268 | | |
269 | 0 | default: |
270 | 0 | return nullptr; |
271 | 0 | } |
272 | 0 | } |
273 | | |
274 | | |
275 | | Result<std::vector<uint8_t>> Decoder::get_compressed_data(bool with_configuration_NALs) const |
276 | 12.7k | { |
277 | | // --- get the compressed image data |
278 | | |
279 | 12.7k | if (with_configuration_NALs) { |
280 | | // data from configuration blocks |
281 | | |
282 | 12.7k | Result<std::vector<uint8_t>> confData = read_bitstream_configuration_data(); |
283 | 12.7k | if (!confData) { |
284 | 0 | return confData.error(); |
285 | 0 | } |
286 | | |
287 | 12.7k | std::vector<uint8_t> data = *confData; |
288 | | |
289 | | // append image data |
290 | | |
291 | 12.7k | auto dataResult = m_data_extent.read_data(); |
292 | 12.7k | if (!dataResult) { |
293 | 594 | return dataResult.error(); |
294 | 594 | } |
295 | | |
296 | 12.1k | data.insert(data.end(), (*dataResult)->begin(), (*dataResult)->end()); |
297 | | |
298 | 12.1k | return data; |
299 | 12.7k | } |
300 | 0 | else { |
301 | 0 | auto dataResult = m_data_extent.read_data(); |
302 | 0 | if (!dataResult) { |
303 | 0 | return dataResult.error(); |
304 | 0 | } |
305 | | |
306 | 0 | return {*(*dataResult)}; |
307 | 0 | } |
308 | 12.7k | } |
309 | | |
310 | | |
311 | | Decoder::~Decoder() |
312 | 16.3k | { |
313 | 16.3k | release_decoder(); |
314 | 16.3k | } |
315 | | |
316 | | |
317 | | void Decoder::release_decoder() |
318 | 29.1k | { |
319 | 29.1k | if (m_decoder) { |
320 | 12.6k | assert(m_decoder_plugin); |
321 | 12.6k | m_decoder_plugin->free_decoder(m_decoder); |
322 | 12.6k | m_decoder = nullptr; |
323 | 12.6k | } |
324 | 29.1k | } |
325 | | |
326 | | |
327 | | Error Decoder::require_decoder_plugin(const heif_decoding_options& options) |
328 | 116k | { |
329 | 116k | if (!m_decoder_plugin) { |
330 | 12.7k | if (options.decoder_id && !has_decoder(get_compression_format(), options.decoder_id)) { |
331 | 0 | return { |
332 | 0 | heif_error_Plugin_loading_error, |
333 | 0 | heif_suberror_Unspecified, |
334 | 0 | "No decoder with that ID found." |
335 | 0 | }; |
336 | 0 | } |
337 | | |
338 | 12.7k | m_decoder_plugin = get_decoder(get_compression_format(), options.decoder_id); |
339 | 12.7k | if (!m_decoder_plugin) { |
340 | 1 | return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed); |
341 | 1 | } |
342 | | |
343 | 12.7k | if (m_decoder_plugin->plugin_api_version < 5) { |
344 | 0 | return Error{ |
345 | 0 | heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed, |
346 | 0 | "Decoder plugin needs to be at least version 5." |
347 | 0 | }; |
348 | 0 | } |
349 | 12.7k | } |
350 | | |
351 | 116k | return {}; |
352 | 116k | } |
353 | | |
354 | | |
355 | | Error Decoder::decode_sequence_frame_from_compressed_data(bool upload_configuration_NALs, |
356 | | const heif_decoding_options& options, |
357 | | uintptr_t user_data, |
358 | | const heif_security_limits* limits) |
359 | 12.8k | { |
360 | 12.8k | auto pluginErr = require_decoder_plugin(options); |
361 | 12.8k | if (pluginErr) { |
362 | 1 | return pluginErr; |
363 | 1 | } |
364 | | |
365 | | // Reject memory-bomb inputs whose codec configuration record (SPS) declares |
366 | | // a coded picture size beyond libheif's security limits, before handing any |
367 | | // bytes to the decoder plugin. Codecs whose configuration record does not |
368 | | // carry dimensions (e.g. AV1's av1C) return nullopt and skip the check. |
369 | | // |
370 | | // TODO: check this also in the decoder plugin since SPS packets may be |
371 | | // found within the actual image bitstream. |
372 | 12.8k | auto codedSize = get_coded_image_size_from_config(); |
373 | 12.8k | if (codedSize.is_error()) { |
374 | 176 | return codedSize.error(); |
375 | 176 | } |
376 | | |
377 | 12.6k | if (codedSize->has_value()) { |
378 | 4.32k | Error sizeErr = check_for_valid_image_size(limits, (*codedSize)->width, (*codedSize)->height); |
379 | 4.32k | if (sizeErr) { |
380 | 28 | return sizeErr; |
381 | 28 | } |
382 | 4.32k | } |
383 | | |
384 | | // --- decode image with the plugin |
385 | | |
386 | 12.6k | heif_error err; |
387 | | |
388 | 12.6k | if (!m_decoder) { |
389 | 12.6k | if (m_decoder_plugin->new_decoder == nullptr) { |
390 | 0 | return Error(heif_error_Plugin_loading_error, heif_suberror_No_matching_decoder_installed, |
391 | 0 | "Cannot decode with a dummy decoder plugin."); |
392 | 0 | } |
393 | | |
394 | 12.6k | if (m_decoder_plugin->plugin_api_version >= 5) { |
395 | 12.6k | heif_decoder_plugin_options plugin_options; |
396 | 12.6k | plugin_options.format = get_compression_format(); |
397 | 12.6k | plugin_options.num_threads = options.num_codec_threads; |
398 | 12.6k | plugin_options.strict_decoding = options.strict_decoding; |
399 | 12.6k | plugin_options.limits = limits; |
400 | | |
401 | 12.6k | err = m_decoder_plugin->new_decoder2(&m_decoder, &plugin_options); |
402 | 12.6k | if (err.code != heif_error_Ok) { |
403 | 0 | return Error(err.code, err.subcode, err.message); |
404 | 0 | } |
405 | 12.6k | } |
406 | 1 | else { |
407 | 1 | err = m_decoder_plugin->new_decoder(&m_decoder); |
408 | 1 | if (err.code != heif_error_Ok) { |
409 | 0 | return Error(err.code, err.subcode, err.message); |
410 | 0 | } |
411 | | |
412 | | // automatically delete decoder plugin when we leave the scope |
413 | | //std::unique_ptr<void, void (*)(void*)> decoderSmartPtr(m_decoder, m_decoder_plugin->free_decoder); |
414 | | |
415 | 1 | if (m_decoder_plugin->plugin_api_version >= 2) { |
416 | 0 | if (m_decoder_plugin->set_strict_decoding) { |
417 | 0 | m_decoder_plugin->set_strict_decoding(m_decoder, options.strict_decoding); |
418 | 0 | } |
419 | 0 | } |
420 | 1 | } |
421 | 12.6k | } |
422 | | |
423 | 12.6k | auto dataResult = get_compressed_data(upload_configuration_NALs); |
424 | 12.6k | if (!dataResult) { |
425 | 567 | return dataResult.error(); |
426 | 567 | } |
427 | | |
428 | | // Check that we are pushing at least some data into the decoder. |
429 | | // Some decoders (e.g. aom) do not complain when the input data is empty and we might |
430 | | // get stuck in an endless decoding loop, waiting for the decompressed image. |
431 | | |
432 | 12.0k | if (dataResult->size() == 0) { |
433 | 9 | return Error{ |
434 | 9 | heif_error_Invalid_input, |
435 | 9 | heif_suberror_Unspecified, |
436 | 9 | "Input with empty data extent." |
437 | 9 | }; |
438 | 9 | } |
439 | | |
440 | | //std::cout << "Decoder::decode_sequence_frame_from_compressed_data push " << dataResult->size() << "\n"; |
441 | 12.0k | if (m_decoder_plugin->plugin_api_version >= 5 && m_decoder_plugin->push_data2) { |
442 | 12.0k | err = m_decoder_plugin->push_data2(m_decoder, dataResult->data(), dataResult->size(), user_data); |
443 | 12.0k | } |
444 | 18.4E | else { |
445 | 18.4E | err = m_decoder_plugin->push_data(m_decoder, dataResult->data(), dataResult->size()); |
446 | 18.4E | } |
447 | 12.0k | if (err.code != heif_error_Ok) { |
448 | 6.06k | return Error(err.code, err.subcode, err.message); |
449 | 6.06k | } |
450 | | |
451 | 5.98k | return {}; |
452 | 12.0k | } |
453 | | |
454 | | Error Decoder::flush_decoder() |
455 | 5.98k | { |
456 | 5.98k | assert(m_decoder_plugin); |
457 | | |
458 | 5.98k | if (m_decoder_plugin->plugin_api_version >= 5) { |
459 | 5.98k | heif_error err = m_decoder_plugin->flush_data(m_decoder); |
460 | 5.98k | return Error::from_heif_error(err); |
461 | 5.98k | } |
462 | | |
463 | 0 | return {}; |
464 | 5.98k | } |
465 | | |
466 | | Result<std::shared_ptr<HeifPixelImage> > Decoder::get_decoded_frame(const heif_decoding_options& options, |
467 | | uintptr_t* out_user_data, |
468 | | const heif_security_limits* limits) |
469 | 103k | { |
470 | 103k | auto pluginErr = require_decoder_plugin(options); |
471 | 103k | if (pluginErr) { |
472 | 0 | return pluginErr; |
473 | 0 | } |
474 | | |
475 | | // The plugin's per-decoder context is created lazily on the first push of |
476 | | // compressed data. If a caller polls for a frame before any data was pushed |
477 | | // (e.g. when a sequence advances into a new chunk that uses a freshly- |
478 | | // allocated decoder), there is nothing buffered yet — return nullptr. |
479 | 103k | if (!m_decoder) { |
480 | 0 | return {nullptr}; |
481 | 0 | } |
482 | | |
483 | 103k | heif_image* decoded_img = nullptr; |
484 | | |
485 | 103k | heif_error err; |
486 | | |
487 | 103k | if (m_decoder_plugin->plugin_api_version >= 5 && |
488 | 103k | m_decoder_plugin->decode_next_image2 != nullptr) { |
489 | | |
490 | 103k | err = m_decoder_plugin->decode_next_image2(m_decoder, &decoded_img, out_user_data, limits); |
491 | 103k | if (err.code != heif_error_Ok) { |
492 | 971 | return Error::from_heif_error(err); |
493 | 971 | } |
494 | 103k | } |
495 | 18.4E | else if (m_decoder_plugin->plugin_api_version >= 4 && |
496 | 0 | m_decoder_plugin->decode_next_image != nullptr) { |
497 | |
|
498 | 0 | err = m_decoder_plugin->decode_next_image(m_decoder, &decoded_img, limits); |
499 | 0 | if (err.code != heif_error_Ok) { |
500 | 0 | return Error::from_heif_error(err); |
501 | 0 | } |
502 | 0 | } |
503 | 18.4E | else { |
504 | 18.4E | err = m_decoder_plugin->decode_image(m_decoder, &decoded_img); |
505 | 18.4E | if (err.code != heif_error_Ok) { |
506 | 0 | return Error::from_heif_error(err); |
507 | 0 | } |
508 | 18.4E | } |
509 | | |
510 | 102k | if (!decoded_img) { |
511 | 99.1k | return {nullptr}; |
512 | 99.1k | } |
513 | | |
514 | | // -- cleanup |
515 | | |
516 | 3.09k | std::shared_ptr<HeifPixelImage> img = std::move(decoded_img->image); |
517 | 3.09k | heif_image_release(decoded_img); |
518 | | |
519 | 3.09k | return img; |
520 | 102k | } |
521 | | |
522 | | |
523 | | Result<std::shared_ptr<HeifPixelImage>> |
524 | | Decoder::decode_single_frame_from_compressed_data(const heif_decoding_options& options, |
525 | | const heif_security_limits* limits) |
526 | 12.8k | { |
527 | 12.8k | Error decodeError = decode_sequence_frame_from_compressed_data(true, options, 0, limits); |
528 | 12.8k | if (decodeError) { |
529 | 6.84k | release_decoder(); |
530 | 6.84k | return decodeError; |
531 | 6.84k | } |
532 | | |
533 | 5.98k | flush_decoder(); |
534 | | |
535 | | // We might have to try several times to get an image out of the decoder. |
536 | | // However, we stop after a maximum number of tries because the decoder might not |
537 | | // give any image when the input data is incomplete. |
538 | 5.98k | const int max_decoding_tries = 50; // hardcoded value, should be large enough |
539 | | |
540 | 105k | for (int i = 0; i < max_decoding_tries; i++) { |
541 | 103k | Result<std::shared_ptr<HeifPixelImage>> imgResult; |
542 | 103k | imgResult = get_decoded_frame(options, nullptr, limits); |
543 | 103k | if (imgResult.error()) { |
544 | 971 | release_decoder(); |
545 | 971 | return imgResult.error(); |
546 | 971 | } |
547 | | |
548 | 102k | if (*imgResult != nullptr) { |
549 | 3.09k | release_decoder(); |
550 | 3.09k | return imgResult; |
551 | 3.09k | } |
552 | 102k | } |
553 | | |
554 | | // We did not receive an image from the decoder. We give up. |
555 | | |
556 | 1.91k | release_decoder(); |
557 | | |
558 | 1.91k | return Error{ |
559 | 1.91k | heif_error_Decoder_plugin_error, |
560 | 1.91k | heif_suberror_Unspecified, |
561 | 1.91k | "Decoding the input data did not give a decompressed image." |
562 | 1.91k | }; |
563 | 5.98k | } |