/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 | 32.8k | { |
28 | 32.8k | auto ftyp = std::make_shared<Box_ftyp>(); |
29 | 32.8k | ftyp->set_output_position(0); |
30 | 32.8k | m_boxes.push_back(ftyp); |
31 | | |
32 | | // TODO: these variables are not used yet |
33 | 32.8k | (void)m_writeMode; |
34 | 32.8k | (void)m_file_size; |
35 | 32.8k | } |
36 | | |
37 | | |
38 | | Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_security_limits* limits) |
39 | 16.4k | { |
40 | 16.4k | m_boxes.clear(); |
41 | | |
42 | 16.4k | m_stream_reader = stream; |
43 | | |
44 | | // --- read initial range, large enough to cover 'ftyp' box |
45 | | |
46 | 16.4k | m_max_length = stream->request_range(0, INITIAL_FTYP_REQUEST); |
47 | | |
48 | 16.4k | if (m_max_length < MAXIMUM_BOX_HEADER_SIZE) { |
49 | 0 | return {heif_error_Invalid_input, |
50 | 0 | heif_suberror_Unspecified, |
51 | 0 | "File size too small."}; |
52 | 0 | } |
53 | | |
54 | | // --- read 'ftyp' box header |
55 | | |
56 | 16.4k | BitstreamRange ftyp_hdr_range(m_stream_reader, m_max_length); |
57 | 16.4k | BoxHeader ftyp_header; |
58 | 16.4k | Error err; |
59 | 16.4k | err = ftyp_header.parse_header(ftyp_hdr_range); |
60 | 16.4k | if (err) { |
61 | 0 | return err; |
62 | 0 | } |
63 | | |
64 | | // --- check whether it is a valid 'ftyp' box header |
65 | | |
66 | 16.4k | if (ftyp_header.get_short_type() != fourcc("ftyp")) { |
67 | 7 | return {heif_error_Invalid_input, |
68 | 7 | heif_suberror_No_ftyp_box, |
69 | 7 | "File does not start with 'ftyp' box."}; |
70 | 7 | } |
71 | | |
72 | 16.4k | uint64_t ftyp_size = ftyp_header.get_box_size(); |
73 | | |
74 | 16.4k | if (ftyp_size == 0) { |
75 | 0 | return {heif_error_Invalid_input, |
76 | 0 | heif_suberror_No_ftyp_box, |
77 | 0 | "ftyp box shall not be the only box in the file"}; |
78 | 0 | } |
79 | | |
80 | 16.4k | if (ftyp_size > m_max_length) { |
81 | 1 | return {heif_error_Invalid_input, |
82 | 1 | heif_suberror_No_ftyp_box, |
83 | 1 | "ftyp box larger than initial read range"}; |
84 | 1 | } |
85 | | |
86 | | // --- read the 'ftyp' box |
87 | | |
88 | 16.4k | BitstreamRange ftyp_range(m_stream_reader, 0, ftyp_size); |
89 | 16.4k | std::shared_ptr<Box> ftyp_box; |
90 | 16.4k | err = Box::read(ftyp_range, &ftyp_box, limits); |
91 | 16.4k | if (err) { |
92 | 0 | return err; |
93 | 0 | } |
94 | | |
95 | 16.4k | m_boxes.push_back(ftyp_box); |
96 | 16.4k | 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 | 16.4k | uint64_t next_box_start = ftyp_size; |
102 | | |
103 | 16.4k | bool meta_found = false; |
104 | 16.4k | bool mini_found = false; |
105 | 16.4k | bool moov_found = false; |
106 | | |
107 | 46.4k | for (;;) { |
108 | | // TODO: overflow |
109 | 46.4k | uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE; |
110 | 46.4k | if (next_box_header_end > m_max_length) { |
111 | 15.0k | m_max_length = stream->request_range(next_box_start, next_box_header_end); |
112 | 15.0k | } |
113 | | |
114 | 46.4k | if (next_box_header_end > m_max_length) { |
115 | 10.8k | if (meta_found || mini_found || moov_found) { |
116 | 10.5k | return Error::Ok; |
117 | 10.5k | } |
118 | 260 | else { |
119 | 260 | return {heif_error_Invalid_input, |
120 | 260 | heif_suberror_Unspecified, |
121 | 260 | "Insufficient input data"}; |
122 | 260 | } |
123 | 10.8k | } |
124 | | |
125 | 35.5k | BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length); |
126 | 35.5k | BoxHeader box_header; |
127 | 35.5k | err = box_header.parse_header(box_range); |
128 | 35.5k | if (err) { |
129 | 11 | return err; |
130 | 11 | } |
131 | | |
132 | 35.5k | if (box_header.get_short_type() == fourcc("meta")) { |
133 | 16.2k | const uint64_t meta_box_start = next_box_start; |
134 | 16.2k | uint64_t end_of_meta_box; |
135 | 16.2k | 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 | 807 | end_of_meta_box = m_stream_reader->request_range(meta_box_start, |
140 | 807 | std::numeric_limits<uint64_t>::max()); |
141 | 807 | m_max_length = end_of_meta_box; |
142 | 807 | 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 | 807 | } |
148 | 15.4k | else { |
149 | 15.4k | end_of_meta_box = box_header.get_box_size(); |
150 | 15.4k | 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 | 15.4k | end_of_meta_box += meta_box_start; |
156 | 15.4k | } |
157 | 16.2k | if (m_max_length < end_of_meta_box) { |
158 | 1.49k | m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box); |
159 | 1.49k | } |
160 | | |
161 | 16.2k | if (m_max_length < end_of_meta_box) { |
162 | 111 | return {heif_error_Invalid_input, |
163 | 111 | heif_suberror_No_meta_box, |
164 | 111 | "Cannot read full meta box"}; |
165 | 111 | } |
166 | | |
167 | 16.1k | BitstreamRange meta_box_range(m_stream_reader, meta_box_start, end_of_meta_box); |
168 | 16.1k | std::shared_ptr<Box> meta_box; |
169 | 16.1k | err = Box::read(meta_box_range, &meta_box, limits); |
170 | 16.1k | if (err) { |
171 | 3.83k | return err; |
172 | 3.83k | } |
173 | | |
174 | 12.3k | m_boxes.push_back(meta_box); |
175 | 12.3k | m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box); |
176 | 12.3k | meta_found = true; |
177 | 12.3k | } |
178 | | |
179 | | // TODO: this is basically the same as the meta box case above, with different error handling. |
180 | 31.6k | if (box_header.get_short_type() == fourcc("mini")) { |
181 | 3.29k | const uint64_t mini_box_start = next_box_start; |
182 | 3.29k | uint64_t end_of_mini_box; |
183 | 3.29k | 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 | 99 | end_of_mini_box = m_stream_reader->request_range(mini_box_start, |
188 | 99 | std::numeric_limits<uint64_t>::max()); |
189 | 99 | m_max_length = end_of_mini_box; |
190 | 99 | 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 | 99 | } |
196 | 3.19k | else { |
197 | 3.19k | end_of_mini_box = box_header.get_box_size(); |
198 | 3.19k | 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 | 3.19k | end_of_mini_box += mini_box_start; |
204 | 3.19k | } |
205 | 3.29k | if (m_max_length < end_of_mini_box) { |
206 | 898 | m_max_length = m_stream_reader->request_range(mini_box_start, end_of_mini_box); |
207 | 898 | } |
208 | | |
209 | 3.29k | if (m_max_length < end_of_mini_box) { |
210 | 93 | return {heif_error_Invalid_input, |
211 | 93 | heif_suberror_Invalid_mini_box, |
212 | 93 | "Cannot read full mini box"}; |
213 | 93 | } |
214 | 3.19k | BitstreamRange mini_box_range(m_stream_reader, mini_box_start, end_of_mini_box); |
215 | 3.19k | std::shared_ptr<Box> mini_box; |
216 | 3.19k | err = Box::read(mini_box_range, &mini_box, limits); |
217 | 3.19k | if (err) { |
218 | 360 | return err; |
219 | 360 | } |
220 | | |
221 | 2.83k | m_boxes.push_back(mini_box); |
222 | 2.83k | m_mini_box = std::dynamic_pointer_cast<Box_mini>(mini_box); |
223 | | |
224 | 2.83k | mini_found = true; |
225 | 2.83k | } |
226 | | |
227 | 31.1k | if (box_header.get_short_type() == fourcc("moov")) { |
228 | 2.99k | const uint64_t moov_box_start = next_box_start; |
229 | 2.99k | uint64_t end_of_moov_box; |
230 | 2.99k | 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 | 124 | end_of_moov_box = m_stream_reader->request_range(moov_box_start, |
235 | 124 | std::numeric_limits<uint64_t>::max()); |
236 | 124 | m_max_length = end_of_moov_box; |
237 | 124 | 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 | 124 | } |
243 | 2.87k | else { |
244 | 2.87k | end_of_moov_box = box_header.get_box_size(); |
245 | 2.87k | 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 | 2.87k | end_of_moov_box += moov_box_start; |
251 | 2.87k | } |
252 | 2.99k | if (m_max_length < end_of_moov_box) { |
253 | 1.06k | m_max_length = m_stream_reader->request_range(moov_box_start, end_of_moov_box); |
254 | 1.06k | } |
255 | | |
256 | 2.99k | if (m_max_length < end_of_moov_box) { |
257 | 128 | return {heif_error_Invalid_input, |
258 | 128 | heif_suberror_No_moov_box, |
259 | 128 | "Cannot read full moov box"}; |
260 | 128 | } |
261 | | |
262 | 2.87k | BitstreamRange moov_box_range(m_stream_reader, moov_box_start, end_of_moov_box); |
263 | 2.87k | std::shared_ptr<Box> moov_box; |
264 | 2.87k | err = Box::read(moov_box_range, &moov_box, limits); |
265 | 2.87k | if (err) { |
266 | 551 | return err; |
267 | 551 | } |
268 | | |
269 | 2.32k | m_boxes.push_back(moov_box); |
270 | 2.32k | m_moov_box = std::dynamic_pointer_cast<Box_moov>(moov_box); |
271 | | |
272 | 2.32k | moov_found = true; |
273 | 2.32k | } |
274 | | |
275 | 30.4k | uint64_t boxSize = box_header.get_box_size(); |
276 | 30.4k | if (boxSize == Box::size_until_end_of_file) { |
277 | 515 | if (meta_found || mini_found || moov_found) { |
278 | 503 | return Error::Ok; |
279 | 503 | } |
280 | 12 | else { |
281 | 12 | return {heif_error_Invalid_input, |
282 | 12 | heif_suberror_Unspecified, |
283 | 12 | "No meta box found"}; |
284 | 12 | } |
285 | 515 | } |
286 | | |
287 | 29.9k | 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 | 29.9k | next_box_start = next_box_start + boxSize; |
294 | 29.9k | } |
295 | | |
296 | 0 | return Error::Ok; |
297 | 16.4k | } |
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 | } |