61
|
1 /*
|
|
2 * The contents of this file are subject to the Mozilla Public
|
|
3 * License Version 1.1 (the "License"); you may not use this file
|
|
4 * except in compliance with the License. You may obtain a copy of
|
|
5 * the License at http://www.mozilla.org/MPL/
|
|
6 *
|
|
7 * Software distributed under the License is distributed on an "AS
|
|
8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
9 * implied. See the License for the specific language governing
|
|
10 * rights and limitations under the License.
|
|
11 *
|
|
12 * The Original Code is MPEG4IP.
|
|
13 *
|
|
14 * The Initial Developer of the Original Code is Cisco Systems Inc.
|
|
15 * Portions created by Cisco Systems Inc. are
|
|
16 * Copyright (C) Cisco Systems Inc. 2001 - 2004. All Rights Reserved.
|
|
17 *
|
|
18 * 3GPP features implementation is based on 3GPP's TS26.234-v5.60,
|
|
19 * and was contributed by Ximpo Group Ltd.
|
|
20 *
|
|
21 * Portions created by Ximpo Group Ltd. are
|
|
22 * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved.
|
|
23 *
|
|
24 * Contributor(s):
|
|
25 * Dave Mackie dmackie@cisco.com
|
|
26 * Alix Marchandise-Franquet alix@cisco.com
|
|
27 * Ximpo Group Ltd. mp4v2@ximpo.com
|
|
28 */
|
|
29
|
|
30 #include "mp4common.h"
|
|
31 #include "atoms.h"
|
|
32
|
|
33 MP4AtomInfo::MP4AtomInfo(const char* name, bool mandatory, bool onlyOne)
|
|
34 {
|
|
35 m_name = name;
|
|
36 m_mandatory = mandatory;
|
|
37 m_onlyOne = onlyOne;
|
|
38 m_count = 0;
|
|
39 }
|
|
40
|
|
41 MP4Atom::MP4Atom(const char* type)
|
|
42 {
|
|
43 SetType(type);
|
|
44 m_unknownType = FALSE;
|
|
45 m_pFile = NULL;
|
|
46 m_start = 0;
|
|
47 m_end = 0;
|
|
48 m_size = 0;
|
|
49 m_pParentAtom = NULL;
|
|
50 m_depth = 0xFF;
|
|
51 }
|
|
52
|
|
53 MP4Atom::~MP4Atom()
|
|
54 {
|
|
55 u_int32_t i;
|
|
56
|
|
57 for (i = 0; i < m_pProperties.Size(); i++) {
|
|
58 delete m_pProperties[i];
|
|
59 }
|
|
60 for (i = 0; i < m_pChildAtomInfos.Size(); i++) {
|
|
61 delete m_pChildAtomInfos[i];
|
|
62 }
|
|
63 for (i = 0; i < m_pChildAtoms.Size(); i++) {
|
|
64 delete m_pChildAtoms[i];
|
|
65 }
|
|
66 }
|
|
67
|
|
68 MP4Atom* MP4Atom::CreateAtom(const char* type)
|
|
69 {
|
|
70 MP4Atom* pAtom = NULL;
|
|
71
|
|
72 if (type == NULL) {
|
|
73 pAtom = new MP4RootAtom();
|
|
74 } else {
|
|
75 switch((uint8_t)type[0]) {
|
|
76 case 'a':
|
|
77 if (ATOMID(type) == ATOMID("avc1")) {
|
|
78 pAtom = new MP4Avc1Atom();
|
|
79 } else if (ATOMID(type) == ATOMID("avcC")) {
|
|
80 pAtom = new MP4AvcCAtom();
|
|
81 } else if (ATOMID(type) == ATOMID("alis")) {
|
|
82 pAtom = new MP4UrlAtom("alis");
|
|
83 } else if (ATOMID(type) == ATOMID("alaw")) {
|
|
84 pAtom = new MP4SoundAtom("alaw");
|
|
85 }
|
|
86 break;
|
|
87 case 'd':
|
|
88 if (ATOMID(type) == ATOMID("d263")) {
|
|
89 pAtom = new MP4D263Atom();
|
|
90 } else if (ATOMID(type) == ATOMID("damr")) {
|
|
91 pAtom = new MP4DamrAtom();
|
|
92 } else if (ATOMID(type) == ATOMID("dref")) {
|
|
93 pAtom = new MP4DrefAtom();
|
|
94 } else if (ATOMID(type) == ATOMID("dpnd")) {
|
|
95 pAtom = new MP4TrefTypeAtom(type);
|
|
96 } else if (ATOMID(type) == ATOMID("data")) { /* Apple iTunes */
|
|
97 pAtom = new MP4DataAtom();
|
|
98 }
|
|
99 break;
|
|
100 case 'e':
|
|
101 if (ATOMID(type) == ATOMID("elst")) {
|
|
102 pAtom = new MP4ElstAtom();
|
|
103 } else if (ATOMID(type) == ATOMID("enca")) {
|
|
104 pAtom = new MP4EncaAtom();
|
|
105 } else if (ATOMID(type) == ATOMID("encv")) {
|
|
106 pAtom = new MP4EncvAtom();
|
|
107 }
|
|
108 break;
|
|
109 case 'f':
|
|
110 if (ATOMID(type) == ATOMID("free")) {
|
|
111 pAtom = new MP4FreeAtom();
|
|
112 } else if (ATOMID(type) == ATOMID("ftyp")) {
|
|
113 pAtom = new MP4FtypAtom();
|
|
114 }
|
|
115 break;
|
|
116 case 'h':
|
|
117 if (ATOMID(type) == ATOMID("hdlr")) {
|
|
118 pAtom = new MP4HdlrAtom();
|
|
119 } else if (ATOMID(type) == ATOMID("hint")) {
|
|
120 pAtom = new MP4TrefTypeAtom(type);
|
|
121 } else if (ATOMID(type) == ATOMID("hnti")) {
|
|
122 pAtom = new MP4HntiAtom();
|
|
123 } else if (ATOMID(type) == ATOMID("hinf")) {
|
|
124 pAtom = new MP4HinfAtom();
|
|
125 } else if (ATOMID(type) == ATOMID("h263")) {
|
|
126 pAtom = new MP4VideoAtom("h263");
|
|
127 } else if (ATOMID(type) == ATOMID("href")) {
|
|
128 pAtom = new MP4HrefAtom();
|
|
129 }
|
|
130 break;
|
|
131 case 'i':
|
|
132 if (ATOMID(type) == ATOMID("ipir")) {
|
|
133 pAtom = new MP4TrefTypeAtom(type);
|
|
134 } else if (ATOMID(type) == ATOMID("ima4")) {
|
|
135 pAtom = new MP4SoundAtom("ima4");
|
|
136 }
|
|
137 break;
|
|
138 case 'j':
|
|
139 if (ATOMID(type) == ATOMID("jpeg")) {
|
|
140 pAtom = new MP4VideoAtom("jpeg");
|
|
141 }
|
|
142 break;
|
|
143 case 'm':
|
|
144 if (ATOMID(type) == ATOMID("mdhd")) {
|
|
145 pAtom = new MP4MdhdAtom();
|
|
146 } else if (ATOMID(type) == ATOMID("mvhd")) {
|
|
147 pAtom = new MP4MvhdAtom();
|
|
148 } else if (ATOMID(type) == ATOMID("mdat")) {
|
|
149 pAtom = new MP4MdatAtom();
|
|
150 } else if (ATOMID(type) == ATOMID("mpod")) {
|
|
151 pAtom = new MP4TrefTypeAtom(type);
|
|
152 } else if (ATOMID(type) == ATOMID("mp4a")) {
|
|
153 pAtom = new MP4Mp4aAtom();
|
|
154 } else if (ATOMID(type) == ATOMID("mp4s")) {
|
|
155 pAtom = new MP4Mp4sAtom();
|
|
156 } else if (ATOMID(type) == ATOMID("mp4v")) {
|
|
157 pAtom = new MP4Mp4vAtom();
|
|
158 } else if (ATOMID(type) == ATOMID("mean")) { // iTunes
|
|
159 pAtom = new MP4MeanAtom();
|
|
160 }
|
|
161 break;
|
|
162 case 'n':
|
|
163 if (ATOMID(type) == ATOMID("name")) { // iTunes
|
|
164 pAtom = new MP4NameAtom();
|
|
165 }
|
|
166 break;
|
|
167 case 'r':
|
|
168 if (ATOMID(type) == ATOMID("rtp ")) {
|
|
169 pAtom = new MP4RtpAtom();
|
|
170 } else if (ATOMID(type) == ATOMID("raw ")) {
|
|
171 pAtom = new MP4VideoAtom("raw ");
|
|
172 }
|
|
173 break;
|
|
174 case 's':
|
|
175 if (ATOMID(type) == ATOMID("s263")) {
|
|
176 pAtom = new MP4S263Atom();
|
|
177 } else if (ATOMID(type) == ATOMID("samr")) {
|
|
178 pAtom = new MP4AmrAtom("samr");
|
|
179 } else if (ATOMID(type) == ATOMID("sawb")) {
|
|
180 pAtom = new MP4AmrAtom("sawb");
|
|
181 } else if (ATOMID(type) == ATOMID("stbl")) {
|
|
182 pAtom = new MP4StblAtom();
|
|
183 } else if (ATOMID(type) == ATOMID("stsd")) {
|
|
184 pAtom = new MP4StsdAtom();
|
|
185 } else if (ATOMID(type) == ATOMID("stsz")) {
|
|
186 pAtom = new MP4StszAtom();
|
|
187 } else if (ATOMID(type) == ATOMID("stsc")) {
|
|
188 pAtom = new MP4StscAtom();
|
|
189 } else if (ATOMID(type) == ATOMID("stdp")) {
|
|
190 pAtom = new MP4StdpAtom();
|
|
191 } else if (ATOMID(type) == ATOMID("sdp ")) {
|
|
192 pAtom = new MP4SdpAtom();
|
|
193 } else if (ATOMID(type) == ATOMID("sync")) {
|
|
194 pAtom = new MP4TrefTypeAtom(type);
|
|
195 } else if (ATOMID(type) == ATOMID("skip")) {
|
|
196 pAtom = new MP4FreeAtom();
|
|
197 pAtom->SetType("skip");
|
|
198 } else if (ATOMID(type) == ATOMID("sowt")) {
|
|
199 pAtom = new MP4SoundAtom("sowt");
|
|
200 }
|
|
201 break;
|
|
202 case 't':
|
|
203 if (ATOMID(type) == ATOMID("tkhd")) {
|
|
204 pAtom = new MP4TkhdAtom();
|
|
205 } else if (ATOMID(type) == ATOMID("tfhd")) {
|
|
206 pAtom = new MP4TfhdAtom();
|
|
207 } else if (ATOMID(type) == ATOMID("trun")) {
|
|
208 pAtom = new MP4TrunAtom();
|
|
209 } else if (ATOMID(type) == ATOMID("twos")) {
|
|
210 pAtom = new MP4SoundAtom("twos");
|
|
211 }
|
|
212 break;
|
|
213 case 'u':
|
|
214 if (ATOMID(type) == ATOMID("udta")) {
|
|
215 pAtom = new MP4UdtaAtom();
|
|
216 } else if (ATOMID(type) == ATOMID("url ")) {
|
|
217 pAtom = new MP4UrlAtom();
|
|
218 } else if (ATOMID(type) == ATOMID("urn ")) {
|
|
219 pAtom = new MP4UrnAtom();
|
|
220 } else if (ATOMID(type) == ATOMID("ulaw")) {
|
|
221 pAtom = new MP4SoundAtom("ulaw");
|
|
222 }
|
|
223 break;
|
|
224 case 'v':
|
|
225 if (ATOMID(type) == ATOMID("vmhd")) {
|
|
226 pAtom = new MP4VmhdAtom();
|
|
227 }
|
|
228 break;
|
|
229 case 'y':
|
|
230 if (ATOMID(type) == ATOMID("yuv2")) {
|
|
231 pAtom = new MP4VideoAtom("yuv2");
|
|
232 }
|
|
233 break;
|
|
234 case 'S':
|
|
235 if (ATOMID(type) == ATOMID("SVQ3")) {
|
|
236 pAtom = new MP4VideoAtom("SVQ3");
|
|
237 } else if (ATOMID(type) == ATOMID("SMI ")) {
|
|
238 pAtom = new MP4SmiAtom();
|
|
239 }
|
|
240 break;
|
|
241 }
|
|
242 }
|
|
243
|
|
244 if (pAtom == NULL) {
|
|
245 pAtom = new MP4StandardAtom(type);
|
|
246 // unknown type is set by StandardAtom type
|
|
247 }
|
|
248
|
|
249 ASSERT(pAtom);
|
|
250 return pAtom;
|
|
251 }
|
|
252
|
|
253 // generate a skeletal self
|
|
254
|
|
255 void MP4Atom::Generate()
|
|
256 {
|
|
257 u_int32_t i;
|
|
258
|
|
259 // for all properties
|
|
260 for (i = 0; i < m_pProperties.Size(); i++) {
|
|
261 // ask it to self generate
|
|
262 m_pProperties[i]->Generate();
|
|
263 }
|
|
264
|
|
265 // for all mandatory, single child atom types
|
|
266 for (i = 0; i < m_pChildAtomInfos.Size(); i++) {
|
|
267 if (m_pChildAtomInfos[i]->m_mandatory
|
|
268 && m_pChildAtomInfos[i]->m_onlyOne) {
|
|
269
|
|
270 // create the mandatory, single child atom
|
|
271 MP4Atom* pChildAtom =
|
|
272 CreateAtom(m_pChildAtomInfos[i]->m_name);
|
|
273
|
|
274 AddChildAtom(pChildAtom);
|
|
275
|
|
276 // and ask it to self generate
|
|
277 pChildAtom->Generate();
|
|
278 }
|
|
279 }
|
|
280 }
|
|
281
|
|
282 MP4Atom* MP4Atom::ReadAtom(MP4File* pFile, MP4Atom* pParentAtom)
|
|
283 {
|
|
284 u_int8_t hdrSize = 8;
|
|
285 u_int8_t extendedType[16];
|
|
286
|
|
287 u_int64_t pos = pFile->GetPosition();
|
|
288
|
|
289 VERBOSE_READ(pFile->GetVerbosity(),
|
|
290 printf("ReadAtom: pos = 0x%llx\n", pos));
|
|
291
|
|
292 u_int64_t dataSize = pFile->ReadUInt32();
|
|
293
|
|
294 char type[5];
|
|
295 pFile->ReadBytes((u_int8_t*)&type[0], 4);
|
|
296 type[4] = '\0';
|
|
297
|
|
298 // extended size
|
|
299 if (dataSize == 1) {
|
|
300 dataSize = pFile->ReadUInt64();
|
|
301 hdrSize += 8;
|
|
302 }
|
|
303
|
|
304 // extended type
|
|
305 if (ATOMID(type) == ATOMID("uuid")) {
|
|
306 pFile->ReadBytes(extendedType, sizeof(extendedType));
|
|
307 hdrSize += sizeof(extendedType);
|
|
308 }
|
|
309
|
|
310 if (dataSize == 0) {
|
|
311 // extends to EOF
|
|
312 dataSize = pFile->GetSize() - pos;
|
|
313 }
|
|
314
|
|
315 dataSize -= hdrSize;
|
|
316
|
|
317 VERBOSE_READ(pFile->GetVerbosity(),
|
|
318 printf("ReadAtom: type = \"%s\" data-size = %llu (0x%llx) hdr %u\n",
|
|
319 type, dataSize, dataSize, hdrSize));
|
|
320
|
|
321 if (pos + hdrSize + dataSize > pParentAtom->GetEnd()) {
|
|
322 VERBOSE_ERROR(pFile->GetVerbosity(),
|
|
323 printf("ReadAtom: invalid atom size, extends outside parent atom - skipping to end of \"%s\" \"%s\" %llu vs %llu\n",
|
|
324 pParentAtom->GetType(), type,
|
|
325 pos + hdrSize + dataSize,
|
|
326 pParentAtom->GetEnd()));
|
|
327 VERBOSE_READ(pFile->GetVerbosity(),
|
|
328 printf("parent %s (%llu) pos %llu hdr %d data %llu sum %llu\n",
|
|
329 pParentAtom->GetType(),
|
|
330 pParentAtom->GetEnd(),
|
|
331 pos,
|
|
332 hdrSize,
|
|
333 dataSize,
|
|
334 pos + hdrSize + dataSize));
|
|
335 #if 0
|
|
336 throw new MP4Error("invalid atom size", "ReadAtom");
|
|
337 #else
|
|
338 // skip to end of atom
|
|
339 dataSize = pParentAtom->GetEnd() - pos - hdrSize;
|
|
340 #endif
|
|
341 }
|
|
342
|
|
343
|
|
344 MP4Atom* pAtom = CreateAtom(type);
|
|
345 pAtom->SetFile(pFile);
|
|
346 pAtom->SetStart(pos);
|
|
347 pAtom->SetEnd(pos + hdrSize + dataSize);
|
|
348 pAtom->SetSize(dataSize);
|
|
349 if (ATOMID(type) == ATOMID("uuid")) {
|
|
350 pAtom->SetExtendedType(extendedType);
|
|
351 }
|
|
352 if (pAtom->IsUnknownType()) {
|
|
353 if (!IsReasonableType(pAtom->GetType())) {
|
|
354 VERBOSE_READ(pFile->GetVerbosity(),
|
|
355 printf("Warning: atom type %s is suspect\n", pAtom->GetType()));
|
|
356 } else {
|
|
357 VERBOSE_READ(pFile->GetVerbosity(),
|
|
358 printf("Info: atom type %s is unknown\n", pAtom->GetType()));
|
|
359 }
|
|
360
|
|
361 if (dataSize > 0) {
|
|
362 pAtom->AddProperty(
|
|
363 new MP4BytesProperty("data", dataSize));
|
|
364 }
|
|
365 }
|
|
366
|
|
367 pAtom->SetParentAtom(pParentAtom);
|
|
368
|
|
369 pAtom->Read();
|
|
370
|
|
371 return pAtom;
|
|
372 }
|
|
373
|
|
374 bool MP4Atom::IsReasonableType(const char* type)
|
|
375 {
|
|
376 for (u_int8_t i = 0; i < 4; i++) {
|
|
377 if (isalnum(type[i])) {
|
|
378 continue;
|
|
379 }
|
|
380 if (i == 3 && type[i] == ' ') {
|
|
381 continue;
|
|
382 }
|
|
383 return false;
|
|
384 }
|
|
385 return true;
|
|
386 }
|
|
387
|
|
388 // generic read
|
|
389 void MP4Atom::Read()
|
|
390 {
|
|
391 ASSERT(m_pFile);
|
|
392
|
|
393 if (ATOMID(m_type) != 0 && m_size > 1000000) {
|
|
394 VERBOSE_READ(GetVerbosity(),
|
|
395 printf("Warning: %s atom size %llu is suspect\n",
|
|
396 m_type, m_size));
|
|
397 }
|
|
398
|
|
399 ReadProperties();
|
|
400
|
|
401 // read child atoms, if we expect there to be some
|
|
402 if (m_pChildAtomInfos.Size() > 0) {
|
|
403 ReadChildAtoms();
|
|
404 }
|
|
405
|
|
406 Skip(); // to end of atom
|
|
407 }
|
|
408
|
|
409 void MP4Atom::Skip()
|
|
410 {
|
|
411 if (m_pFile->GetPosition() != m_end) {
|
|
412 VERBOSE_READ(m_pFile->GetVerbosity(),
|
|
413 printf("Skip: %llu bytes\n", m_end - m_pFile->GetPosition()));
|
|
414 }
|
|
415 m_pFile->SetPosition(m_end);
|
|
416 }
|
|
417
|
|
418 MP4Atom* MP4Atom::FindAtom(const char* name)
|
|
419 {
|
|
420 if (!IsMe(name)) {
|
|
421 return NULL;
|
|
422 }
|
|
423
|
|
424 if (!IsRootAtom()) {
|
|
425 VERBOSE_FIND(m_pFile->GetVerbosity(),
|
|
426 printf("FindAtom: matched %s\n", name));
|
|
427
|
|
428 name = MP4NameAfterFirst(name);
|
|
429
|
|
430 // I'm the sought after atom
|
|
431 if (name == NULL) {
|
|
432 return this;
|
|
433 }
|
|
434 }
|
|
435
|
|
436 // else it's one of my children
|
|
437 return FindChildAtom(name);
|
|
438 }
|
|
439
|
|
440 bool MP4Atom::FindProperty(const char *name,
|
|
441 MP4Property** ppProperty, u_int32_t* pIndex)
|
|
442 {
|
|
443 if (!IsMe(name)) {
|
|
444 return false;
|
|
445 }
|
|
446
|
|
447 if (!IsRootAtom()) {
|
|
448 VERBOSE_FIND(m_pFile->GetVerbosity(),
|
|
449 printf("FindProperty: matched %s\n", name));
|
|
450
|
|
451 name = MP4NameAfterFirst(name);
|
|
452
|
|
453 // no property name given
|
|
454 if (name == NULL) {
|
|
455 return false;
|
|
456 }
|
|
457 }
|
|
458
|
|
459 return FindContainedProperty(name, ppProperty, pIndex);
|
|
460 }
|
|
461
|
|
462 bool MP4Atom::IsMe(const char* name)
|
|
463 {
|
|
464 if (name == NULL) {
|
|
465 return false;
|
|
466 }
|
|
467
|
|
468 // root atom always matches
|
|
469 if (!strcmp(m_type, "")) {
|
|
470 return true;
|
|
471 }
|
|
472
|
|
473 // check if our atom name is specified as the first component
|
|
474 if (!MP4NameFirstMatches(m_type, name)) {
|
|
475 return false;
|
|
476 }
|
|
477
|
|
478 return true;
|
|
479 }
|
|
480
|
|
481 MP4Atom* MP4Atom::FindChildAtom(const char* name)
|
|
482 {
|
|
483 u_int32_t atomIndex = 0;
|
|
484
|
|
485 // get the index if we have one, e.g. moov.trak[2].mdia...
|
|
486 MP4NameFirstIndex(name, &atomIndex);
|
|
487
|
|
488 // need to get to the index'th child atom of the right type
|
|
489 for (u_int32_t i = 0; i < m_pChildAtoms.Size(); i++) {
|
|
490 if (MP4NameFirstMatches(m_pChildAtoms[i]->GetType(), name)) {
|
|
491 if (atomIndex == 0) {
|
|
492 // this is the one, ask it to match
|
|
493 return m_pChildAtoms[i]->FindAtom(name);
|
|
494 }
|
|
495 atomIndex--;
|
|
496 }
|
|
497 }
|
|
498
|
|
499 return NULL;
|
|
500 }
|
|
501
|
|
502 bool MP4Atom::FindContainedProperty(const char *name,
|
|
503 MP4Property** ppProperty, u_int32_t* pIndex)
|
|
504 {
|
|
505 u_int32_t numProperties = m_pProperties.Size();
|
|
506 u_int32_t i;
|
|
507 // check all of our properties
|
|
508 for (i = 0; i < numProperties; i++) {
|
|
509 if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) {
|
|
510 return true;
|
|
511 }
|
|
512 }
|
|
513
|
|
514 // not one of our properties,
|
|
515 // presumably one of our children's properties
|
|
516 // check child atoms...
|
|
517
|
|
518 // check if we have an index, e.g. trak[2].mdia...
|
|
519 u_int32_t atomIndex = 0;
|
|
520 MP4NameFirstIndex(name, &atomIndex);
|
|
521
|
|
522 // need to get to the index'th child atom of the right type
|
|
523 for (i = 0; i < m_pChildAtoms.Size(); i++) {
|
|
524 if (MP4NameFirstMatches(m_pChildAtoms[i]->GetType(), name)) {
|
|
525 if (atomIndex == 0) {
|
|
526 // this is the one, ask it to match
|
|
527 return m_pChildAtoms[i]->FindProperty(name, ppProperty, pIndex);
|
|
528 }
|
|
529 atomIndex--;
|
|
530 }
|
|
531 }
|
|
532
|
|
533 VERBOSE_FIND(m_pFile->GetVerbosity(),
|
|
534 printf("FindProperty: no match for %s\n", name));
|
|
535 return false;
|
|
536 }
|
|
537
|
|
538 void MP4Atom::ReadProperties(u_int32_t startIndex, u_int32_t count)
|
|
539 {
|
|
540 u_int32_t numProperties = MIN(count, m_pProperties.Size() - startIndex);
|
|
541
|
|
542 // read any properties of the atom
|
|
543 for (u_int32_t i = startIndex; i < startIndex + numProperties; i++) {
|
|
544
|
|
545 m_pProperties[i]->Read(m_pFile);
|
|
546
|
|
547 if (m_pFile->GetPosition() > m_end) {
|
|
548 VERBOSE_READ(GetVerbosity(),
|
|
549 printf("ReadProperties: insufficient data for property: %s pos 0x%llx atom end 0x%llx\n",
|
|
550 m_pProperties[i]->GetName(),
|
|
551 m_pFile->GetPosition(), m_end));
|
|
552
|
|
553 throw new MP4Error("atom is too small", "Atom ReadProperties");
|
|
554 }
|
|
555
|
|
556 if (m_pProperties[i]->GetType() == TableProperty) {
|
|
557 VERBOSE_READ_TABLE(GetVerbosity(),
|
|
558 printf("Read: "); m_pProperties[i]->Dump(stdout, 0, true));
|
|
559 } else if (m_pProperties[i]->GetType() != DescriptorProperty) {
|
|
560 VERBOSE_READ(GetVerbosity(),
|
|
561 printf("Read: "); m_pProperties[i]->Dump(stdout, 0, true));
|
|
562 }
|
|
563 }
|
|
564 }
|
|
565
|
|
566 void MP4Atom::ReadChildAtoms()
|
|
567 {
|
|
568 bool this_is_udta = ATOMID(m_type) == ATOMID("udta");
|
|
569
|
|
570 VERBOSE_READ(GetVerbosity(),
|
|
571 printf("ReadChildAtoms: of %s\n", m_type[0] ? m_type : "root"));
|
|
572 for (u_int64_t position = m_pFile->GetPosition();
|
|
573 position < m_end;
|
|
574 position = m_pFile->GetPosition()) {
|
|
575 // make sure that we have enough to read at least 8 bytes
|
|
576 // size and type.
|
|
577 if (m_end - position < 2 * sizeof(uint32_t)) {
|
|
578 // if we're reading udta, it's okay to have 4 bytes of 0
|
|
579 if (this_is_udta &&
|
|
580 m_end - position == sizeof(uint32_t)) {
|
|
581 u_int32_t mbz = m_pFile->ReadUInt32();
|
|
582 if (mbz != 0) {
|
|
583 VERBOSE_WARNING(GetVerbosity(),
|
|
584 printf("Error: In udta atom, end value is not zero %x\n",
|
|
585 mbz));
|
|
586 }
|
|
587 continue;
|
|
588 }
|
|
589 // otherwise, output a warning, but don't care
|
|
590 VERBOSE_WARNING(GetVerbosity(),
|
|
591 printf("Error: In %s atom, extra %lld bytes at end of atom\n",
|
|
592 m_type, (m_end - position)));
|
|
593 for (uint64_t ix = 0; ix < m_end - position; ix++) {
|
|
594 m_pFile->ReadUInt8();
|
|
595 }
|
|
596 continue;
|
|
597 }
|
|
598 MP4Atom* pChildAtom = MP4Atom::ReadAtom(m_pFile, this);
|
|
599
|
|
600 AddChildAtom(pChildAtom);
|
|
601
|
|
602 MP4AtomInfo* pChildAtomInfo = FindAtomInfo(pChildAtom->GetType());
|
|
603
|
|
604 // if child atom is of known type
|
|
605 // but not expected here print warning
|
|
606 if (pChildAtomInfo == NULL && !pChildAtom->IsUnknownType()) {
|
|
607 VERBOSE_READ(GetVerbosity(),
|
|
608 printf("Warning: In atom %s unexpected child atom %s\n",
|
|
609 GetType(), pChildAtom->GetType()));
|
|
610 }
|
|
611
|
|
612 // if child atoms should have just one instance
|
|
613 // and this is more than one, print warning
|
|
614 if (pChildAtomInfo) {
|
|
615 pChildAtomInfo->m_count++;
|
|
616
|
|
617 if (pChildAtomInfo->m_onlyOne && pChildAtomInfo->m_count > 1) {
|
|
618 VERBOSE_READ(GetVerbosity(),
|
|
619 printf("Warning: In atom %s multiple child atoms %s\n",
|
|
620 GetType(), pChildAtom->GetType()));
|
|
621 }
|
|
622 }
|
|
623
|
|
624 }
|
|
625
|
|
626 // if mandatory child atom doesn't exist, print warning
|
|
627 u_int32_t numAtomInfo = m_pChildAtomInfos.Size();
|
|
628 for (u_int32_t i = 0; i < numAtomInfo; i++) {
|
|
629 if (m_pChildAtomInfos[i]->m_mandatory
|
|
630 && m_pChildAtomInfos[i]->m_count == 0) {
|
|
631 VERBOSE_READ(GetVerbosity(),
|
|
632 printf("Warning: In atom %s missing child atom %s\n",
|
|
633 GetType(), m_pChildAtomInfos[i]->m_name));
|
|
634 }
|
|
635 }
|
|
636
|
|
637 VERBOSE_READ(GetVerbosity(),
|
|
638 printf("ReadChildAtoms: finished %s\n", m_type));
|
|
639 }
|
|
640
|
|
641 MP4AtomInfo* MP4Atom::FindAtomInfo(const char* name)
|
|
642 {
|
|
643 u_int32_t numAtomInfo = m_pChildAtomInfos.Size();
|
|
644 for (u_int32_t i = 0; i < numAtomInfo; i++) {
|
|
645 if (ATOMID(m_pChildAtomInfos[i]->m_name) == ATOMID(name)) {
|
|
646 return m_pChildAtomInfos[i];
|
|
647 }
|
|
648 }
|
|
649 return NULL;
|
|
650 }
|
|
651
|
|
652 // generic write
|
|
653 void MP4Atom::Write()
|
|
654 {
|
|
655 ASSERT(m_pFile);
|
|
656
|
|
657 BeginWrite();
|
|
658
|
|
659 WriteProperties();
|
|
660
|
|
661 WriteChildAtoms();
|
|
662
|
|
663 FinishWrite();
|
|
664 }
|
|
665
|
|
666 void MP4Atom::Rewrite()
|
|
667 {
|
|
668 ASSERT(m_pFile);
|
|
669
|
|
670 if (!m_end) {
|
|
671 // This atom hasn't been written yet...
|
|
672 return;
|
|
673 }
|
|
674
|
|
675 u_int64_t fPos = m_pFile->GetPosition();
|
|
676 m_pFile->SetPosition(GetStart());
|
|
677 Write();
|
|
678 m_pFile->SetPosition(fPos);
|
|
679 }
|
|
680
|
|
681 void MP4Atom::BeginWrite(bool use64)
|
|
682 {
|
|
683 m_start = m_pFile->GetPosition();
|
|
684 //use64 = m_pFile->Use64Bits();
|
|
685 if (use64) {
|
|
686 m_pFile->WriteUInt32(1);
|
|
687 } else {
|
|
688 m_pFile->WriteUInt32(0);
|
|
689 }
|
|
690 m_pFile->WriteBytes((u_int8_t*)&m_type[0], 4);
|
|
691 if (use64) {
|
|
692 m_pFile->WriteUInt64(0);
|
|
693 }
|
|
694 if (ATOMID(m_type) == ATOMID("uuid")) {
|
|
695 m_pFile->WriteBytes(m_extendedType, sizeof(m_extendedType));
|
|
696 }
|
|
697 }
|
|
698
|
|
699 void MP4Atom::FinishWrite(bool use64)
|
|
700 {
|
|
701 m_end = m_pFile->GetPosition();
|
|
702 m_size = (m_end - m_start);
|
|
703 VERBOSE_WRITE(GetVerbosity(),
|
|
704 printf("end: type %s %llu %llu size %llu\n", m_type,
|
|
705 m_start, m_end,
|
|
706 m_size));
|
|
707 //use64 = m_pFile->Use64Bits();
|
|
708 if (use64) {
|
|
709 m_pFile->SetPosition(m_start + 8);
|
|
710 m_pFile->WriteUInt64(m_size);
|
|
711 } else {
|
|
712 ASSERT(m_size <= (u_int64_t)0xFFFFFFFF);
|
|
713 m_pFile->SetPosition(m_start);
|
|
714 m_pFile->WriteUInt32(m_size);
|
|
715 }
|
|
716 m_pFile->SetPosition(m_end);
|
|
717
|
|
718 // adjust size to just reflect data portion of atom
|
|
719 m_size -= (use64 ? 16 : 8);
|
|
720 if (ATOMID(m_type) == ATOMID("uuid")) {
|
|
721 m_size -= sizeof(m_extendedType);
|
|
722 }
|
|
723 }
|
|
724
|
|
725 void MP4Atom::WriteProperties(u_int32_t startIndex, u_int32_t count)
|
|
726 {
|
|
727 u_int32_t numProperties = MIN(count, m_pProperties.Size() - startIndex);
|
|
728
|
|
729 VERBOSE_WRITE(GetVerbosity(),
|
|
730 printf("Write: type %s\n", m_type));
|
|
731
|
|
732 for (u_int32_t i = startIndex; i < startIndex + numProperties; i++) {
|
|
733 m_pProperties[i]->Write(m_pFile);
|
|
734
|
|
735 if (m_pProperties[i]->GetType() == TableProperty) {
|
|
736 VERBOSE_WRITE_TABLE(GetVerbosity(),
|
|
737 printf("Write: "); m_pProperties[i]->Dump(stdout, 0, false));
|
|
738 } else {
|
|
739 VERBOSE_WRITE(GetVerbosity(),
|
|
740 printf("Write: "); m_pProperties[i]->Dump(stdout, 0, false));
|
|
741 }
|
|
742 }
|
|
743 }
|
|
744
|
|
745 void MP4Atom::WriteChildAtoms()
|
|
746 {
|
|
747 u_int32_t size = m_pChildAtoms.Size();
|
|
748 for (u_int32_t i = 0; i < size; i++) {
|
|
749 m_pChildAtoms[i]->Write();
|
|
750 }
|
|
751
|
|
752 VERBOSE_WRITE(GetVerbosity(),
|
|
753 printf("Write: finished %s\n", m_type));
|
|
754 }
|
|
755
|
|
756 void MP4Atom::AddProperty(MP4Property* pProperty)
|
|
757 {
|
|
758 ASSERT(pProperty);
|
|
759 m_pProperties.Add(pProperty);
|
|
760 pProperty->SetParentAtom(this);
|
|
761 }
|
|
762
|
|
763 void MP4Atom::AddVersionAndFlags()
|
|
764 {
|
|
765 AddProperty(new MP4Integer8Property("version"));
|
|
766 AddProperty(new MP4Integer24Property("flags"));
|
|
767 }
|
|
768
|
|
769 void MP4Atom::AddReserved(char* name, u_int32_t size)
|
|
770 {
|
|
771 MP4BytesProperty* pReserved = new MP4BytesProperty(name, size);
|
|
772 pReserved->SetReadOnly();
|
|
773 AddProperty(pReserved);
|
|
774 }
|
|
775
|
|
776 void MP4Atom::ExpectChildAtom(const char* name, bool mandatory, bool onlyOne)
|
|
777 {
|
|
778 m_pChildAtomInfos.Add(new MP4AtomInfo(name, mandatory, onlyOne));
|
|
779 }
|
|
780
|
|
781 u_int8_t MP4Atom::GetVersion()
|
|
782 {
|
|
783 if (strcmp("version", m_pProperties[0]->GetName())) {
|
|
784 return 0;
|
|
785 }
|
|
786 return ((MP4Integer8Property*)m_pProperties[0])->GetValue();
|
|
787 }
|
|
788
|
|
789 void MP4Atom::SetVersion(u_int8_t version)
|
|
790 {
|
|
791 if (strcmp("version", m_pProperties[0]->GetName())) {
|
|
792 return;
|
|
793 }
|
|
794 ((MP4Integer8Property*)m_pProperties[0])->SetValue(version);
|
|
795 }
|
|
796
|
|
797 u_int32_t MP4Atom::GetFlags()
|
|
798 {
|
|
799 if (strcmp("flags", m_pProperties[1]->GetName())) {
|
|
800 return 0;
|
|
801 }
|
|
802 return ((MP4Integer24Property*)m_pProperties[1])->GetValue();
|
|
803 }
|
|
804
|
|
805 void MP4Atom::SetFlags(u_int32_t flags)
|
|
806 {
|
|
807 if (strcmp("flags", m_pProperties[1]->GetName())) {
|
|
808 return;
|
|
809 }
|
|
810 ((MP4Integer24Property*)m_pProperties[1])->SetValue(flags);
|
|
811 }
|
|
812
|
|
813 void MP4Atom::Dump(FILE* pFile, u_int8_t indent, bool dumpImplicits)
|
|
814 {
|
|
815 if (m_type[0] != '\0') {
|
|
816 Indent(pFile, indent);
|
|
817 fprintf(pFile, "type %s\n", m_type);
|
|
818 fflush(pFile);
|
|
819 }
|
|
820
|
|
821 u_int32_t i;
|
|
822 u_int32_t size;
|
|
823
|
|
824 // dump our properties
|
|
825 size = m_pProperties.Size();
|
|
826 for (i = 0; i < size; i++) {
|
|
827
|
|
828 /* skip details of tables unless we're told to be verbose */
|
|
829 if (m_pProperties[i]->GetType() == TableProperty
|
|
830 && !(GetVerbosity() & MP4_DETAILS_TABLE)) {
|
|
831 Indent(pFile, indent + 1);
|
|
832 fprintf(pFile, "<table entries suppressed>\n");
|
|
833 continue;
|
|
834 }
|
|
835
|
|
836 m_pProperties[i]->Dump(pFile, indent + 1, dumpImplicits);
|
|
837 }
|
|
838
|
|
839 // dump our children
|
|
840 size = m_pChildAtoms.Size();
|
|
841 for (i = 0; i < size; i++) {
|
|
842 m_pChildAtoms[i]->Dump(pFile, indent + 1, dumpImplicits);
|
|
843 }
|
|
844 }
|
|
845
|
|
846 u_int32_t MP4Atom::GetVerbosity()
|
|
847 {
|
|
848 ASSERT(m_pFile);
|
|
849 return m_pFile->GetVerbosity();
|
|
850 }
|
|
851
|
|
852 u_int8_t MP4Atom::GetDepth()
|
|
853 {
|
|
854 if (m_depth < 0xFF) {
|
|
855 return m_depth;
|
|
856 }
|
|
857
|
|
858 MP4Atom *pAtom = this;
|
|
859 m_depth = 0;
|
|
860
|
|
861 while ((pAtom = pAtom->GetParentAtom()) != NULL) {
|
|
862 m_depth++;
|
|
863 ASSERT(m_depth < 255);
|
|
864 }
|
|
865 return m_depth;
|
|
866 }
|
|
867
|