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