/src/libheif/libheif/file_layout.cc
Line | Count | Source (jump to first uncovered line) |
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 | 38.1k | { |
28 | 38.1k | auto ftyp = std::make_shared<Box_ftyp>(); |
29 | 38.1k | ftyp->set_output_position(0); |
30 | 38.1k | m_boxes.push_back(ftyp); |
31 | | |
32 | | // TODO: these variables are not used yet |
33 | 38.1k | (void)m_writeMode; |
34 | 38.1k | (void)m_file_size; |
35 | 38.1k | } |
36 | | |
37 | | |
38 | | Error FileLayout::read(const std::shared_ptr<StreamReader>& stream, const heif_security_limits* limits) |
39 | 19.0k | { |
40 | 19.0k | m_boxes.clear(); |
41 | | |
42 | 19.0k | m_stream_reader = stream; |
43 | | |
44 | | // --- read initial range, large enough to cover 'ftyp' box |
45 | | |
46 | 19.0k | m_max_length = stream->request_range(0, INITIAL_FTYP_REQUEST); |
47 | | |
48 | 19.0k | if (m_max_length < MAXIMUM_BOX_HEADER_SIZE) { |
49 | 14 | return {heif_error_Invalid_input, |
50 | 14 | heif_suberror_Unspecified, |
51 | 14 | "File size too small."}; |
52 | 14 | } |
53 | | |
54 | | // --- read 'ftyp' box header |
55 | | |
56 | 19.0k | BitstreamRange ftyp_hdr_range(m_stream_reader, m_max_length); |
57 | 19.0k | BoxHeader ftyp_header; |
58 | 19.0k | Error err; |
59 | 19.0k | err = ftyp_header.parse_header(ftyp_hdr_range); |
60 | 19.0k | if (err) { |
61 | 19 | return err; |
62 | 19 | } |
63 | | |
64 | | // --- check whether it is a valid 'ftyp' box header |
65 | | |
66 | 19.0k | 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 | 19.0k | uint64_t ftyp_size = ftyp_header.get_box_size(); |
73 | | |
74 | 19.0k | 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 | 19.0k | if (ftyp_size > m_max_length) { |
81 | 50 | return {heif_error_Invalid_input, |
82 | 50 | heif_suberror_No_ftyp_box, |
83 | 50 | "ftyp box larger than initial read range"}; |
84 | 50 | } |
85 | | |
86 | | // --- read the 'ftyp' box |
87 | | |
88 | 18.9k | BitstreamRange ftyp_range(m_stream_reader, 0, ftyp_size); |
89 | 18.9k | std::shared_ptr<Box> ftyp_box; |
90 | 18.9k | err = Box::read(ftyp_range, &ftyp_box, limits); |
91 | | |
92 | 18.9k | m_boxes.push_back(ftyp_box); |
93 | 18.9k | m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(ftyp_box); |
94 | | |
95 | | |
96 | | // --- skip through box headers until we find the 'meta' box |
97 | | |
98 | 18.9k | uint64_t next_box_start = ftyp_size; |
99 | | |
100 | 18.9k | bool meta_found = false; |
101 | 18.9k | bool mini_found = false; |
102 | 18.9k | bool moov_found = false; |
103 | | |
104 | 51.8k | for (;;) { |
105 | | // TODO: overflow |
106 | 51.8k | uint64_t next_box_header_end = next_box_start + MAXIMUM_BOX_HEADER_SIZE; |
107 | 51.8k | if (next_box_header_end > m_max_length) { |
108 | 15.7k | m_max_length = stream->request_range(next_box_start, next_box_header_end); |
109 | 15.7k | } |
110 | | |
111 | 51.8k | if (next_box_header_end > m_max_length) { |
112 | 15.7k | if (meta_found || mini_found || moov_found) { |
113 | 15.4k | return Error::Ok; |
114 | 15.4k | } |
115 | 281 | else { |
116 | 281 | return {heif_error_Invalid_input, |
117 | 281 | heif_suberror_Unspecified, |
118 | 281 | "Insufficient input data"}; |
119 | 281 | } |
120 | 15.7k | } |
121 | | |
122 | 36.0k | BitstreamRange box_range(m_stream_reader, next_box_start, m_max_length); |
123 | 36.0k | BoxHeader box_header; |
124 | 36.0k | err = box_header.parse_header(box_range); |
125 | 36.0k | if (err) { |
126 | 12 | return err; |
127 | 12 | } |
128 | | |
129 | 36.0k | if (box_header.get_short_type() == fourcc("meta")) { |
130 | 18.9k | const uint64_t meta_box_start = next_box_start; |
131 | 18.9k | if (box_header.get_box_size() == 0) { |
132 | | // TODO: get file-size from stream and compute box size |
133 | 1 | return {heif_error_Invalid_input, |
134 | 1 | heif_suberror_No_meta_box, |
135 | 1 | "Cannot read meta box with unspecified size"}; |
136 | 1 | } |
137 | | |
138 | | // TODO: overflow |
139 | 18.9k | uint64_t end_of_meta_box = meta_box_start + box_header.get_box_size(); |
140 | 18.9k | if (m_max_length < end_of_meta_box) { |
141 | 66 | m_max_length = m_stream_reader->request_range(meta_box_start, end_of_meta_box); |
142 | 66 | } |
143 | | |
144 | 18.9k | if (m_max_length < end_of_meta_box) { |
145 | 66 | return {heif_error_Invalid_input, |
146 | 66 | heif_suberror_No_meta_box, |
147 | 66 | "Cannot read full meta box"}; |
148 | 66 | } |
149 | | |
150 | 18.8k | BitstreamRange meta_box_range(m_stream_reader, meta_box_start, end_of_meta_box); |
151 | 18.8k | std::shared_ptr<Box> meta_box; |
152 | 18.8k | err = Box::read(meta_box_range, &meta_box, limits); |
153 | 18.8k | if (err) { |
154 | 2.66k | return err; |
155 | 2.66k | } |
156 | | |
157 | 16.2k | m_boxes.push_back(meta_box); |
158 | 16.2k | m_meta_box = std::dynamic_pointer_cast<Box_meta>(meta_box); |
159 | 16.2k | meta_found = true; |
160 | 16.2k | } |
161 | | |
162 | | #if ENABLE_EXPERIMENTAL_MINI_FORMAT |
163 | | // TODO: this is basically the same as the meta box case above, with different error handling. |
164 | | if (box_header.get_short_type() == fourcc("mini")) { |
165 | | const uint64_t mini_box_start = next_box_start; |
166 | | if (box_header.get_box_size() == 0) { |
167 | | // TODO: get file-size from stream and compute box size |
168 | | return {heif_error_Invalid_input, |
169 | | heif_suberror_Invalid_mini_box, |
170 | | "Cannot read mini box with unspecified size"}; |
171 | | } |
172 | | uint64_t end_of_mini_box = mini_box_start + box_header.get_box_size(); |
173 | | if (m_max_length < end_of_mini_box) { |
174 | | m_max_length = m_stream_reader->request_range(mini_box_start, end_of_mini_box); |
175 | | } |
176 | | |
177 | | if (m_max_length < end_of_mini_box) { |
178 | | return {heif_error_Invalid_input, |
179 | | heif_suberror_Invalid_mini_box, |
180 | | "Cannot read full mini box"}; |
181 | | } |
182 | | BitstreamRange mini_box_range(m_stream_reader, mini_box_start, end_of_mini_box); |
183 | | std::shared_ptr<Box> mini_box; |
184 | | err = Box::read(mini_box_range, &mini_box, heif_get_global_security_limits()); |
185 | | if (err) { |
186 | | std::cout << "error reading mini box" << std::endl; |
187 | | return err; |
188 | | } |
189 | | |
190 | | m_boxes.push_back(mini_box); |
191 | | m_mini_box = std::dynamic_pointer_cast<Box_mini>(mini_box); |
192 | | if (m_mini_box == nullptr) { |
193 | | std::cout << "error casting mini box" << std::endl; |
194 | | } |
195 | | |
196 | | mini_found = true; |
197 | | } |
198 | | #endif |
199 | | |
200 | 33.3k | if (box_header.get_short_type() == fourcc("moov")) { |
201 | 178 | const uint64_t moov_box_start = next_box_start; |
202 | 178 | if (box_header.get_box_size() == 0) { |
203 | | // TODO: get file-size from stream and compute box size |
204 | 1 | return {heif_error_Invalid_input, |
205 | 1 | heif_suberror_No_moov_box, |
206 | 1 | "Cannot read moov box with unspecified size"}; |
207 | 1 | } |
208 | | |
209 | | // TODO: overflow |
210 | 177 | uint64_t end_of_moov_box = moov_box_start + box_header.get_box_size(); |
211 | 177 | if (m_max_length < end_of_moov_box) { |
212 | 74 | m_max_length = m_stream_reader->request_range(moov_box_start, end_of_moov_box); |
213 | 74 | } |
214 | | |
215 | 177 | if (m_max_length < end_of_moov_box) { |
216 | 74 | return {heif_error_Invalid_input, |
217 | 74 | heif_suberror_No_moov_box, |
218 | 74 | "Cannot read full moov box"}; |
219 | 74 | } |
220 | | |
221 | 103 | BitstreamRange moov_box_range(m_stream_reader, moov_box_start, end_of_moov_box); |
222 | 103 | std::shared_ptr<Box> moov_box; |
223 | 103 | err = Box::read(moov_box_range, &moov_box, limits); |
224 | 103 | if (err) { |
225 | 76 | return err; |
226 | 76 | } |
227 | | |
228 | 27 | m_boxes.push_back(moov_box); |
229 | 27 | m_moov_box = std::dynamic_pointer_cast<Box_moov>(moov_box); |
230 | | |
231 | 27 | moov_found = true; |
232 | 27 | } |
233 | | |
234 | 33.1k | uint64_t boxSize = box_header.get_box_size(); |
235 | 33.1k | if (boxSize == Box::size_until_end_of_file) { |
236 | 330 | if (meta_found || mini_found || moov_found) { |
237 | 325 | return Error::Ok; |
238 | 325 | } |
239 | 5 | else { |
240 | 5 | return {heif_error_Invalid_input, |
241 | 5 | heif_suberror_Unspecified, |
242 | 5 | "No meta box found"}; |
243 | 5 | } |
244 | 330 | } |
245 | | |
246 | 32.8k | if (std::numeric_limits<uint64_t>::max() - boxSize < next_box_start) { |
247 | 0 | return {heif_error_Invalid_input, |
248 | 0 | heif_suberror_Unspecified, |
249 | 0 | "Box size too large, integer overflow"}; |
250 | 0 | } |
251 | | |
252 | 32.8k | next_box_start = next_box_start + boxSize; |
253 | 32.8k | } |
254 | | |
255 | 0 | return Error::Ok; |
256 | 18.9k | } |
257 | | |
258 | | |
259 | | void FileLayout::set_write_mode(WriteMode writeMode, const std::shared_ptr<StreamWriter>& writer) |
260 | 0 | { |
261 | |
|
262 | 0 | } |
263 | | |
264 | | |
265 | | Error FileLayout::write(std::shared_ptr<StreamWriter>& stream) |
266 | 0 | { |
267 | 0 | return Error::Ok; |
268 | 0 | } |