/src/libheif/libheif/codecs/avif_dec.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 "avif_dec.h" |
22 | | #include "avif_boxes.h" |
23 | | #include "error.h" |
24 | | #include "context.h" |
25 | | |
26 | | #include <algorithm> |
27 | | #include <string> |
28 | | |
29 | | |
30 | | Result<std::vector<uint8_t>> Decoder_AVIF::read_bitstream_configuration_data() const |
31 | 52.6k | { |
32 | 52.6k | std::vector<uint8_t> data; |
33 | 52.6k | if (!m_av1C->get_headers(&data)) { |
34 | 0 | return Error{heif_error_Invalid_input, |
35 | 0 | heif_suberror_No_item_data}; |
36 | 0 | } |
37 | | |
38 | 52.6k | return data; |
39 | 52.6k | } |
40 | | |
41 | | |
42 | | Error Decoder_AVIF::prepare_compressed_data_for_decoding(std::vector<uint8_t>& compressed_data, |
43 | | size_t configuration_data_size) const |
44 | 51.8k | { |
45 | 51.8k | return normalize_av1_sample_data(compressed_data, configuration_data_size); |
46 | 51.8k | } |
47 | | |
48 | | |
49 | | Result<std::optional<ImageSize>> Decoder_AVIF::get_max_coded_image_size(const std::vector<uint8_t>& compressed_data, |
50 | | size_t configuration_data_size) const |
51 | 51.5k | { |
52 | | // The AV1 sequence header carries the coded frame size |
53 | | // (max_frame_width_minus_1 / max_frame_height_minus_1). Per the AVIF |
54 | | // specification it is present in the image item data, and it may additionally |
55 | | // be duplicated in the av1C configOBUs. A decoder allocates buffers for that |
56 | | // coded size, which can be far larger than the (possibly deliberately small) |
57 | | // 'ispe' dimensions on which the container-level size checks are based. |
58 | | // |
59 | | // `compressed_data` is the combined configOBUs + item/sample data buffer that |
60 | | // is about to be pushed to the decoder plugin. Scan both regions for the |
61 | | // largest sequence-header frame size so the shared decode path can reject |
62 | | // over-limit inputs before ANY AV1 plugin allocates a frame buffer. They are |
63 | | // scanned separately because the sample boundary must not depend on the |
64 | | // contents of configOBUs. prepare_compressed_data_for_decoding() has already |
65 | | // made any terminal unsized sample OBU explicit, so every OBU is now sized. |
66 | 51.5k | if (configuration_data_size > compressed_data.size()) { |
67 | 0 | return Error{heif_error_Invalid_input, |
68 | 0 | heif_suberror_Unspecified, |
69 | 0 | "AV1 stream was rejected because its configuration-data size is invalid."}; |
70 | 0 | } |
71 | | |
72 | 51.5k | bool found = false; |
73 | 51.5k | uint32_t max_width = 0; |
74 | 51.5k | uint32_t max_height = 0; |
75 | | |
76 | 102k | auto scan_region = [&](size_t offset, size_t size) -> Error { |
77 | 102k | if (size == 0) { |
78 | 48.7k | return Error::Ok; |
79 | 48.7k | } |
80 | | |
81 | 54.2k | uint32_t width = 0; |
82 | 54.2k | uint32_t height = 0; |
83 | 54.2k | auto result = find_max_av1_frame_size_in_stream(compressed_data.data() + offset, size, |
84 | 54.2k | &width, &height); |
85 | 54.2k | if (!result) { |
86 | 323 | return result.error(); |
87 | 323 | } |
88 | 53.8k | if (*result) { |
89 | 52.4k | found = true; |
90 | 52.4k | max_width = std::max(max_width, width); |
91 | 52.4k | max_height = std::max(max_height, height); |
92 | 52.4k | } |
93 | 53.8k | return Error::Ok; |
94 | 54.2k | }; |
95 | | |
96 | 51.5k | Error err = scan_region(0, configuration_data_size); |
97 | 51.5k | if (err) { |
98 | 133 | return err; |
99 | 133 | } |
100 | | |
101 | 51.3k | err = scan_region(configuration_data_size, |
102 | 51.3k | compressed_data.size() - configuration_data_size); |
103 | 51.3k | if (err) { |
104 | 190 | return err; |
105 | 190 | } |
106 | | |
107 | 51.2k | if (!found) { |
108 | | // No parseable sequence header (e.g. a non-sync frame in a sequence). |
109 | 298 | return std::optional<ImageSize>{}; |
110 | 298 | } |
111 | | |
112 | 50.9k | return std::optional<ImageSize>{ImageSize{max_width, max_height}}; |
113 | 51.2k | } |
114 | | |
115 | | |
116 | | Result<uint32_t> Decoder_AVIF::get_number_of_coded_pictures(const std::vector<uint8_t>& compressed_data, |
117 | | size_t configuration_data_size) const |
118 | 51.1k | { |
119 | 51.1k | if (configuration_data_size > compressed_data.size()) { |
120 | 0 | return Error{heif_error_Invalid_input, heif_suberror_Unspecified, |
121 | 0 | "AV1 stream was rejected because its configuration-data size is invalid."}; |
122 | 0 | } |
123 | 51.1k | uint32_t total = 0; |
124 | 102k | auto scan_region = [&](size_t offset, size_t size) -> Error { |
125 | 102k | uint32_t width = 0, height = 0, count = 0; |
126 | 102k | auto result = find_max_av1_frame_size_in_stream(compressed_data.data() + offset, size, |
127 | 102k | &width, &height, &count); |
128 | 102k | if (!result) { return result.error(); } |
129 | 102k | total += count; |
130 | 102k | return Error::Ok; |
131 | 102k | }; |
132 | 51.1k | Error err = scan_region(0, configuration_data_size); |
133 | 51.1k | if (err) { return err; } |
134 | 51.1k | err = scan_region(configuration_data_size, |
135 | 51.1k | compressed_data.size() - configuration_data_size); |
136 | 51.1k | if (err) { return err; } |
137 | 51.1k | return total; |
138 | 51.1k | } |
139 | | |
140 | | |
141 | | int Decoder_AVIF::get_luma_bits_per_pixel() const |
142 | 208k | { |
143 | 208k | Box_av1C::configuration config = m_av1C->get_configuration(); |
144 | 208k | if (!config.high_bitdepth) { |
145 | 152k | return 8; |
146 | 152k | } |
147 | 56.4k | else if (config.twelve_bit) { |
148 | 12.5k | return 12; |
149 | 12.5k | } |
150 | 43.9k | else { |
151 | 43.9k | return 10; |
152 | 43.9k | } |
153 | 208k | } |
154 | | |
155 | | |
156 | | int Decoder_AVIF::get_chroma_bits_per_pixel() const |
157 | 77.7k | { |
158 | 77.7k | return get_luma_bits_per_pixel(); |
159 | 77.7k | } |
160 | | |
161 | | |
162 | | Error Decoder_AVIF::get_coded_image_colorspace(heif_colorspace* out_colorspace, heif_chroma* out_chroma) const |
163 | 164k | { |
164 | 164k | *out_chroma = m_av1C->get_configuration().get_heif_chroma(); |
165 | | |
166 | 164k | if (*out_chroma == heif_chroma_monochrome) { |
167 | 15.0k | *out_colorspace = heif_colorspace_monochrome; |
168 | 15.0k | } |
169 | 149k | else { |
170 | 149k | *out_colorspace = heif_colorspace_YCbCr; |
171 | 149k | } |
172 | | |
173 | 164k | return Error::Ok; |
174 | 164k | } |