/src/libheif/libheif/file_layout.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 "file_layout.h" |
22 | | #include "sequences/seq_boxes.h" |
23 | | #include <limits> |
24 | | |
25 | | |
26 | | FileLayout::FileLayout() |
27 | 26.4k | { |
28 | 26.4k | auto ftyp = std::make_shared<Box_ftyp>(); |
29 | 26.4k | ftyp->set_output_position(0); |
30 | 26.4k | m_boxes.push_back(ftyp); |
31 | | |
32 | | // TODO: these variables are not used yet |
33 | 26.4k | (void)m_writeMode; |
34 | 26.4k | (void)m_file_size; |
35 | 26.4k | } |
36 | | |
37 | | |
38 | | Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_security_limits* limits) |
39 | 13.2k | { |
40 | 13.2k | m_boxes.clear(); |
41 | | |
42 | 13.2k | m_stream_reader = stream; |
43 | | |
44 | | // --- read initial range, large enough to cover 'ftyp' box |
45 | | |
46 | 13.2k | m_max_length = stream->request_range(0, INITIAL_FTYP_REQUEST); |
47 | | |
48 | 13.2k | if (m_max_length < MAXIMUM_BOX_HEADER_SIZE) { |
49 | 9 | return {heif_error_Invalid_input, |
50 | 9 | heif_suberror_Unspecified, |
51 | 9 | "File size too small."}; |
52 | 9 | } |
53 | | |
54 | | // --- read 'ftyp' box header |
55 | | |
56 | 13.2k | BitstreamRange ftyp_hdr_range(m_stream_reader, m_max_length); |
57 | 13.2k | BoxHeader ftyp_header; |
58 | 13.2k | Error err; |
59 | 13.2k | err = ftyp_header.parse_header(ftyp_hdr_range); |
60 | 13.2k | if (err) { |
61 | 11 | return err; |
62 | 11 | } |
63 | | |
64 | | // --- check whether it is a valid 'ftyp' box header |
65 | | |
66 | 13.2k | if (ftyp_header.get_short_type() != fourcc("ftyp")) { |
67 | 94 | return {heif_error_Invalid_input, |
68 | 94 | heif_suberror_No_ftyp_box, |
69 | 94 | "File does not start with 'ftyp' box."}; |
70 | 94 | } |
71 | | |
72 | 13.1k | uint64_t ftyp_size = ftyp_header.get_box_size(); |
73 | | |
74 | 13.1k | if (ftyp_size == 0) { |
75 | 1 | return {heif_error_Invalid_input, |
76 | 1 | heif_suberror_No_ftyp_box, |
77 | 1 | "ftyp box shall not be the only box in the file"}; |
78 | 1 | } |
79 | | |
80 | 13.1k | if (ftyp_size > m_max_length) { |
81 | 128 | return {heif_error_Invalid_input, |
82 | 128 | heif_suberror_No_ftyp_box, |
83 | 128 | "ftyp box larger than initial read range"}; |
84 | 128 | } |
85 | | |
86 | | // --- read the 'ftyp' box |
87 | | |
88 | 12.9k | BitstreamRange ftyp_range(m_stream_reader, 0, ftyp_size); |
89 | 12.9k | std::shared_ptr<Box> ftyp_box; |
90 | 12.9k | err = Box::read(ftyp_range, &ftyp_box, limits); |
91 | 12.9k | if (err) { |
92 | 28 | return err; |
93 | 28 | } |
94 | | |
95 | 12.9k | m_boxes.push_back(ftyp_box); |
96 | 12.9k | m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(ftyp_box); |
97 | | |
98 | | |
99 | | // --- skip through box headers until we find the 'meta' box |
100 | | |
101 | 12.9k | uint64_t next_box_start = ftyp_size; |
102 | | |
103 | 12.9k | bool meta_found = false; |
104 | 12.9k | bool mini_found = false; |
105 | 12.9k | bool moov_found = false; |
106 | | |
107 | 39.0k | for (;;) { |
108 | | // TODO: overflow |
109 | 39.0k | uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE; |
110 | 39.0k | if (next_box_header_end > m_max_length) { |
111 | 3.37k | m_max_length = stream->request_range(next_box_start, next_box_header_end); |
112 | 3.37k | } |
113 | | |
114 | 39.0k | if (next_box_header_end > m_max_length) { |
115 | 3.37k | if (meta_found || mini_found || moov_found) { |
116 | 3.16k | return Error::Ok; |
117 | 3.16k | } |
118 | 215 | else { |
119 | 215 | return {heif_error_Invalid_input, |
120 | 215 | heif_suberror_Unspecified, |
121 | 215 | "Insufficient input data"}; |
122 | 215 | } |
123 | 3.37k | } |
124 | | |
125 | 35.6k | BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length); |
126 | 35.6k | BoxHeader box_header; |
127 | 35.6k | err = box_header.parse_header(box_range); |
128 | 35.6k | if (err) { |
129 | 1 | return err; |
130 | 1 | } |
131 | | |
132 | 35.6k | if (box_header.get_short_type() == fourcc("meta")) { |
133 | 2.32k | const uint64_t meta_box_start = next_box_start; |
134 | 2.32k | uint64_t end_of_meta_box; |
135 | 2.32k | if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) { |
136 | | // A box size of 0 means the box extends to the end of the file |
137 | | // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box. |
138 | | // Resolve it to the file size. |
139 | 339 | end_of_meta_box = m_stream_reader->request_range(meta_box_start, |
140 | 339 | std::numeric_limits<uint64_t>::max()); |
141 | 339 | m_max_length = end_of_meta_box; |
142 | 339 | if (end_of_meta_box <= meta_box_start) { |
143 | 0 | return {heif_error_Invalid_input, |
144 | 0 | heif_suberror_No_meta_box, |
145 | 0 | "Cannot read meta box with unspecified size"}; |
146 | 0 | } |
147 | 339 | } |
148 | 1.99k | else { |
149 | 1.99k | end_of_meta_box = box_header.get_box_size(); |
150 | 1.99k | if (end_of_meta_box > std::numeric_limits<uint64_t>::max() - meta_box_start) { |
151 | 0 | return {heif_error_Invalid_input, |
152 | 0 | heif_suberror_No_meta_box, |
153 | 0 | "Cannot read meta box with invalid size"}; |
154 | 0 | } |
155 | 1.99k | end_of_meta_box += meta_box_start; |
156 | 1.99k | } |
157 | 2.32k | if (m_max_length < end_of_meta_box) { |
158 | 122 | m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box); |
159 | 122 | } |
160 | | |
161 | 2.32k | if (m_max_length < end_of_meta_box) { |
162 | 122 | return {heif_error_Invalid_input, |
163 | 122 | heif_suberror_No_meta_box, |
164 | 122 | "Cannot read full meta box"}; |
165 | 122 | } |
166 | | |
167 | 2.20k | BitstreamRange meta_box_range(m_stream_reader, meta_box_start, end_of_meta_box); |
168 | 2.20k | std::shared_ptr<Box> meta_box; |
169 | 2.20k | err = Box::read(meta_box_range, &meta_box, limits); |
170 | 2.20k | if (err) { |
171 | 300 | return err; |
172 | 300 | } |
173 | | |
174 | 1.90k | m_boxes.push_back(meta_box); |
175 | 1.90k | m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box); |
176 | 1.90k | meta_found = true; |
177 | 1.90k | } |
178 | | |
179 | | // TODO: this is basically the same as the meta box case above, with different error handling. |
180 | 35.2k | if (box_header.get_short_type() == fourcc("mini")) { |
181 | 4.80k | const uint64_t mini_box_start = next_box_start; |
182 | 4.80k | uint64_t end_of_mini_box; |
183 | 4.80k | if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) { |
184 | | // A box size of 0 means the box extends to the end of the file |
185 | | // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box. |
186 | | // Resolve it to the file size. |
187 | 551 | end_of_mini_box = m_stream_reader->request_range(mini_box_start, |
188 | 551 | std::numeric_limits<uint64_t>::max()); |
189 | 551 | m_max_length = end_of_mini_box; |
190 | 551 | if (end_of_mini_box <= mini_box_start) { |
191 | 0 | return {heif_error_Invalid_input, |
192 | 0 | heif_suberror_Invalid_mini_box, |
193 | 0 | "Cannot read mini box with unspecified size"}; |
194 | 0 | } |
195 | 551 | } |
196 | 4.24k | else { |
197 | 4.24k | end_of_mini_box = box_header.get_box_size(); |
198 | 4.24k | if (end_of_mini_box > std::numeric_limits<uint64_t>::max() - mini_box_start) { |
199 | 0 | return {heif_error_Invalid_input, |
200 | 0 | heif_suberror_Invalid_mini_box, |
201 | 0 | "Cannot read mini box with invalid size"}; |
202 | 0 | } |
203 | 4.24k | end_of_mini_box += mini_box_start; |
204 | 4.24k | } |
205 | 4.80k | if (m_max_length < end_of_mini_box) { |
206 | 131 | m_max_length = m_stream_reader->request_range(mini_box_start, end_of_mini_box); |
207 | 131 | } |
208 | | |
209 | 4.80k | if (m_max_length < end_of_mini_box) { |
210 | 131 | return {heif_error_Invalid_input, |
211 | 131 | heif_suberror_Invalid_mini_box, |
212 | 131 | "Cannot read full mini box"}; |
213 | 131 | } |
214 | 4.66k | BitstreamRange mini_box_range(m_stream_reader, mini_box_start, end_of_mini_box); |
215 | 4.66k | std::shared_ptr<Box> mini_box; |
216 | 4.66k | err = Box::read(mini_box_range, &mini_box, heif_get_global_security_limits()); |
217 | 4.66k | if (err) { |
218 | 335 | return err; |
219 | 335 | } |
220 | | |
221 | 4.33k | m_boxes.push_back(mini_box); |
222 | 4.33k | m_mini_box = std::dynamic_pointer_cast<Box_mini>(mini_box); |
223 | | |
224 | 4.33k | mini_found = true; |
225 | 4.33k | } |
226 | | |
227 | 34.7k | if (box_header.get_short_type() == fourcc("moov")) { |
228 | 23.1k | const uint64_t moov_box_start = next_box_start; |
229 | 23.1k | uint64_t end_of_moov_box; |
230 | 23.1k | if (box_header.get_box_size() == BoxHeader::size_until_end_of_file) { |
231 | | // A box size of 0 means the box extends to the end of the file |
232 | | // (ISO/IEC 14496-12 clause 4.2), which is legal for the last box. |
233 | | // Resolve it to the file size. |
234 | 6.98k | end_of_moov_box = m_stream_reader->request_range(moov_box_start, |
235 | 6.98k | std::numeric_limits<uint64_t>::max()); |
236 | 6.98k | m_max_length = end_of_moov_box; |
237 | 6.98k | if (end_of_moov_box <= moov_box_start) { |
238 | 0 | return {heif_error_Invalid_input, |
239 | 0 | heif_suberror_No_moov_box, |
240 | 0 | "Cannot read moov box with unspecified size"}; |
241 | 0 | } |
242 | 6.98k | } |
243 | 16.1k | else { |
244 | 16.1k | end_of_moov_box = box_header.get_box_size(); |
245 | 16.1k | if (end_of_moov_box > std::numeric_limits<uint64_t>::max() - moov_box_start) { |
246 | 0 | return {heif_error_Invalid_input, |
247 | 0 | heif_suberror_No_moov_box, |
248 | 0 | "Cannot read moov box with invalid size"}; |
249 | 0 | } |
250 | 16.1k | end_of_moov_box += moov_box_start; |
251 | 16.1k | } |
252 | 23.1k | if (m_max_length < end_of_moov_box) { |
253 | 134 | m_max_length = m_stream_reader->request_range(moov_box_start, end_of_moov_box); |
254 | 134 | } |
255 | | |
256 | 23.1k | if (m_max_length < end_of_moov_box) { |
257 | 134 | return {heif_error_Invalid_input, |
258 | 134 | heif_suberror_No_moov_box, |
259 | 134 | "Cannot read full moov box"}; |
260 | 134 | } |
261 | | |
262 | 23.0k | BitstreamRange moov_box_range(m_stream_reader, moov_box_start, end_of_moov_box); |
263 | 23.0k | std::shared_ptr<Box> moov_box; |
264 | 23.0k | err = Box::read(moov_box_range, &moov_box, limits); |
265 | 23.0k | if (err) { |
266 | 6.25k | return err; |
267 | 6.25k | } |
268 | | |
269 | 16.7k | m_boxes.push_back(moov_box); |
270 | 16.7k | m_moov_box = std::dynamic_pointer_cast<Box_moov>(moov_box); |
271 | | |
272 | 16.7k | moov_found = true; |
273 | 16.7k | } |
274 | | |
275 | 28.3k | uint64_t boxSize = box_header.get_box_size(); |
276 | 28.3k | if (boxSize == Box::size_until_end_of_file) { |
277 | 2.29k | if (meta_found || mini_found || moov_found) { |
278 | 2.28k | return Error::Ok; |
279 | 2.28k | } |
280 | 12 | else { |
281 | 12 | return {heif_error_Invalid_input, |
282 | 12 | heif_suberror_Unspecified, |
283 | 12 | "No meta box found"}; |
284 | 12 | } |
285 | 2.29k | } |
286 | | |
287 | 26.0k | if (std::numeric_limits<uint64_t>::max() - boxSize < next_box_start) { |
288 | 0 | return {heif_error_Invalid_input, |
289 | 0 | heif_suberror_Unspecified, |
290 | 0 | "Box size too large, integer overflow"}; |
291 | 0 | } |
292 | | |
293 | 26.0k | next_box_start = next_box_start + boxSize; |
294 | 26.0k | } |
295 | | |
296 | 0 | return Error::Ok; |
297 | 12.9k | } |
298 | | |
299 | | |
300 | | void FileLayout::set_write_mode(WriteMode writeMode, const std::shared_ptr<StreamWriter>& writer) |
301 | 0 | { |
302 | |
|
303 | 0 | } |
304 | | |
305 | | |
306 | | Error FileLayout::write(std::shared_ptr<StreamWriter>& stream) |
307 | 0 | { |
308 | 0 | return Error::Ok; |
309 | 0 | } |