107 {
109
110 if (data.
size() < 8) {
111 if (error) *error = "Data too small for MP4";
112 return info;
113 }
114
115
116 fl::size moovPos = findBox(data, 0, data.
size(),
"moov");
117 if (moovPos == fl::size(-1)) {
118 if (error) *error = "No moov box found";
119 return info;
120 }
121
122 bool ok = true;
123 fl::u32 moovSize =
readU32BE(data, moovPos, ok);
124 if (!ok) {
125 if (error) *error = "Invalid moov box size";
126 return info;
127 }
128 fl::size moovEnd = moovPos + moovSize;
129 if (moovEnd > data.
size()) moovEnd = data.
size();
130 fl::size moovBody = moovPos + 8;
131
132
133 fl::size trakPos =
findBox(data, moovBody, moovEnd,
"trak");
134 if (trakPos == fl::size(-1)) {
135 if (error) *error = "No trak box found";
136 return info;
137 }
138
139 fl::u32 trakSize =
readU32BE(data, trakPos, ok);
140 if (!ok) {
141 if (error) *error = "Invalid trak box size";
142 return info;
143 }
144 fl::size trakEnd = trakPos + trakSize;
145 if (trakEnd > moovEnd) trakEnd = moovEnd;
146 fl::size trakBody = trakPos + 8;
147
148
149 fl::size mdiaPos =
findBox(data, trakBody, trakEnd,
"mdia");
150 if (mdiaPos == fl::size(-1)) {
151 if (error) *error = "No mdia box found";
152 return info;
153 }
154
155 fl::u32 mdiaSize =
readU32BE(data, mdiaPos, ok);
156 if (!ok) {
157 if (error) *error = "Invalid mdia box size";
158 return info;
159 }
160 fl::size mdiaEnd = mdiaPos + mdiaSize;
161 if (mdiaEnd > trakEnd) mdiaEnd = trakEnd;
162 fl::size mdiaBody = mdiaPos + 8;
163
164
165 fl::size minfPos =
findBox(data, mdiaBody, mdiaEnd,
"minf");
166 if (minfPos == fl::size(-1)) {
167 if (error) *error = "No minf box found";
168 return info;
169 }
170
171 fl::u32 minfSize =
readU32BE(data, minfPos, ok);
172 if (!ok) {
173 if (error) *error = "Invalid minf box size";
174 return info;
175 }
176 fl::size minfEnd = minfPos + minfSize;
177 if (minfEnd > mdiaEnd) minfEnd = mdiaEnd;
178 fl::size minfBody = minfPos + 8;
179
180
181 fl::size stblPos =
findBox(data, minfBody, minfEnd,
"stbl");
182 if (stblPos == fl::size(-1)) {
183 if (error) *error = "No stbl box found";
184 return info;
185 }
186
187 fl::u32 stblSize =
readU32BE(data, stblPos, ok);
188 if (!ok) {
189 if (error) *error = "Invalid stbl box size";
190 return info;
191 }
192 fl::size stblEnd = stblPos + stblSize;
193 if (stblEnd > minfEnd) stblEnd = minfEnd;
194 fl::size stblBody = stblPos + 8;
195
196
197 fl::size stsdPos =
findBox(data, stblBody, stblEnd,
"stsd");
198 if (stsdPos == fl::size(-1)) {
199 if (error) *error = "No stsd box found";
200 return info;
201 }
202
203 fl::u32 stsdSize =
readU32BE(data, stsdPos, ok);
204 if (!ok) {
205 if (error) *error = "Invalid stsd box size";
206 return info;
207 }
208 fl::size stsdEnd = stsdPos + stsdSize;
209 if (stsdEnd > stblEnd) stsdEnd = stblEnd;
210
211
212 fl::size stsdBody = stsdPos + 8 + 4 + 4;
213 if (stsdBody > stsdEnd) {
214 if (error) *error = "stsd box too small";
215 return info;
216 }
217
218
219 fl::size avc1Pos =
findBox(data, stsdBody, stsdEnd,
"avc1");
220 if (avc1Pos == fl::size(-1)) {
221 if (error) *error = "No avc1 sample entry found (not H.264)";
222 return info;
223 }
224
225 fl::u32 avc1Size =
readU32BE(data, avc1Pos, ok);
226 if (!ok) {
227 if (error) *error = "Invalid avc1 box size";
228 return info;
229 }
230 fl::size avc1End = avc1Pos + avc1Size;
231 if (avc1End > stsdEnd) avc1End = stsdEnd;
232
233
234
235
236
237
238
239
240
241
242
243 fl::size avc1Body = avc1Pos + 8 + 6 + 2 + 2 + 2 + 12;
244 if (avc1Body + 4 > avc1End) {
245 if (error) *error = "avc1 box too small for dimensions";
246 return info;
247 }
248
251 if (!ok) {
252 if (error) *error = "Failed to read dimensions from avc1";
253 return info;
254 }
255
256
257
258 fl::size subBoxStart = avc1Body + 4 + 4 + 4 + 4 + 2 + 32 + 2 + 2;
259
260
261 fl::size avcCPos =
findBox(data, subBoxStart, avc1End,
"avcC");
262 if (avcCPos == fl::size(-1)) {
263 if (error) *error = "No avcC box found inside avc1";
264 return info;
265 }
266
267 fl::u32 avcCSize =
readU32BE(data, avcCPos, ok);
268 if (!ok) {
269 if (error) *error = "Invalid avcC box size";
270 return info;
271 }
272 fl::size avcCEnd = avcCPos + avcCSize;
273 if (avcCEnd > avc1End) avcCEnd = avc1End;
274
275 if (!
parseAvcC(data, avcCPos, avcCEnd, info)) {
276 if (error) *error = "Failed to parse avcC configuration record";
277 return info;
278 }
279
281 return info;
282}
constexpr fl::size size() const FL_NO_EXCEPT
fl::u32 readU32BE(fl::span< const fl::u8 > data, fl::size offset, bool &ok) FL_NO_EXCEPT
fl::size findBox(fl::span< const fl::u8 > data, fl::size start, fl::size end, const char *type) FL_NO_EXCEPT
fl::u16 readU16BE(fl::span< const fl::u8 > data, fl::size offset, bool &ok) FL_NO_EXCEPT
bool parseAvcC(fl::span< const fl::u8 > data, fl::size offset, fl::size boxEnd, Mp4TrackInfo &info) FL_NO_EXCEPT