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. All Rights Reserved.
17 *
18 * Contributor(s):
19 * Dave Mackie dmackie@cisco.com
20 * Kona Blend kona8lend@@gmail.com
21 */
22
23 #include "src/impl.h"
24
25 namespace mp4v2 { namespace impl {
26
27 ///////////////////////////////////////////////////////////////////////////////
28
MP4Property(MP4Atom & parentAtom,const char * name)29 MP4Property::MP4Property(MP4Atom& parentAtom, const char* name)
30 : m_parentAtom(parentAtom)
31 {
32 m_name = name;
33 m_readOnly = false;
34 m_implicit = false;
35 }
36
FindProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)37 bool MP4Property::FindProperty(const char* name,
38 MP4Property** ppProperty, uint32_t* pIndex)
39 {
40 if (name == NULL) {
41 return false;
42 }
43
44 if (!strcasecmp(m_name, name)) {
45 log.verbose1f("\"%s\": FindProperty: matched %s",
46 m_parentAtom.GetFile().GetFilename().c_str(), name);
47 *ppProperty = this;
48 return true;
49 }
50 return false;
51 }
52
53 // Integer Property
54
GetValue(uint32_t index)55 uint64_t MP4IntegerProperty::GetValue(uint32_t index)
56 {
57 switch (this->GetType()) {
58 case Integer8Property:
59 return ((MP4Integer8Property*)this)->GetValue(index);
60 case Integer16Property:
61 return ((MP4Integer16Property*)this)->GetValue(index);
62 case Integer24Property:
63 return ((MP4Integer24Property*)this)->GetValue(index);
64 case Integer32Property:
65 return ((MP4Integer32Property*)this)->GetValue(index);
66 case Integer64Property:
67 return ((MP4Integer64Property*)this)->GetValue(index);
68 default:
69 ASSERT(false);
70 }
71 return (0);
72 }
73
SetValue(uint64_t value,uint32_t index)74 void MP4IntegerProperty::SetValue(uint64_t value, uint32_t index)
75 {
76 switch (this->GetType()) {
77 case Integer8Property:
78 ((MP4Integer8Property*)this)->SetValue(value, index);
79 break;
80 case Integer16Property:
81 ((MP4Integer16Property*)this)->SetValue(value, index);
82 break;
83 case Integer24Property:
84 ((MP4Integer24Property*)this)->SetValue(value, index);
85 break;
86 case Integer32Property:
87 ((MP4Integer32Property*)this)->SetValue(value, index);
88 break;
89 case Integer64Property:
90 ((MP4Integer64Property*)this)->SetValue(value, index);
91 break;
92 default:
93 ASSERT(false);
94 }
95 }
96
InsertValue(uint64_t value,uint32_t index)97 void MP4IntegerProperty::InsertValue(uint64_t value, uint32_t index)
98 {
99 switch (this->GetType()) {
100 case Integer8Property:
101 ((MP4Integer8Property*)this)->InsertValue(value, index);
102 break;
103 case Integer16Property:
104 ((MP4Integer16Property*)this)->InsertValue(value, index);
105 break;
106 case Integer24Property:
107 ((MP4Integer24Property*)this)->InsertValue(value, index);
108 break;
109 case Integer32Property:
110 ((MP4Integer32Property*)this)->InsertValue(value, index);
111 break;
112 case Integer64Property:
113 ((MP4Integer64Property*)this)->InsertValue(value, index);
114 break;
115 default:
116 ASSERT(false);
117 }
118 }
119
DeleteValue(uint32_t index)120 void MP4IntegerProperty::DeleteValue(uint32_t index)
121 {
122 switch (this->GetType()) {
123 case Integer8Property:
124 ((MP4Integer8Property*)this)->DeleteValue(index);
125 break;
126 case Integer16Property:
127 ((MP4Integer16Property*)this)->DeleteValue(index);
128 break;
129 case Integer24Property:
130 ((MP4Integer24Property*)this)->DeleteValue(index);
131 break;
132 case Integer32Property:
133 ((MP4Integer32Property*)this)->DeleteValue(index);
134 break;
135 case Integer64Property:
136 ((MP4Integer64Property*)this)->DeleteValue(index);
137 break;
138 default:
139 ASSERT(false);
140 }
141 }
142
IncrementValue(int32_t increment,uint32_t index)143 void MP4IntegerProperty::IncrementValue(int32_t increment, uint32_t index)
144 {
145 SetValue(GetValue() + increment);
146 }
147
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)148 void MP4Integer8Property::Dump(uint8_t indent,
149 bool dumpImplicits, uint32_t index)
150 {
151 if (m_implicit && !dumpImplicits) {
152 return;
153 }
154 if (index != 0)
155 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%02x)",
156 m_parentAtom.GetFile().GetFilename().c_str(),
157 m_name, index, m_values[index], m_values[index]);
158 else
159 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%02x)",
160 m_parentAtom.GetFile().GetFilename().c_str(),
161 m_name, m_values[index], m_values[index]);
162 }
163
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)164 void MP4Integer16Property::Dump(uint8_t indent,
165 bool dumpImplicits, uint32_t index)
166 {
167 if (m_implicit && !dumpImplicits) {
168 return;
169 }
170 if (index != 0)
171 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%04x)",
172 m_parentAtom.GetFile().GetFilename().c_str(),
173 m_name, index, m_values[index], m_values[index]);
174 else
175 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%04x)",
176 m_parentAtom.GetFile().GetFilename().c_str(),
177 m_name, m_values[index], m_values[index]);
178 }
179
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)180 void MP4Integer24Property::Dump(uint8_t indent,
181 bool dumpImplicits, uint32_t index)
182 {
183 if (m_implicit && !dumpImplicits) {
184 return;
185 }
186 if (index != 0)
187 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%06x)",
188 m_parentAtom.GetFile().GetFilename().c_str(),
189 m_name, index, m_values[index], m_values[index]);
190 else
191 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%06x)",
192 m_parentAtom.GetFile().GetFilename().c_str(),
193 m_name, m_values[index], m_values[index]);
194 }
195
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)196 void MP4Integer32Property::Dump(uint8_t indent,
197 bool dumpImplicits, uint32_t index)
198 {
199 if (m_implicit && !dumpImplicits) {
200 return;
201 }
202 if (index != 0)
203 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%08x)",
204 m_parentAtom.GetFile().GetFilename().c_str(),
205 m_name, index, m_values[index], m_values[index]);
206 else
207 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%08x)",
208 m_parentAtom.GetFile().GetFilename().c_str(),
209 m_name, m_values[index], m_values[index]);
210 }
211
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)212 void MP4Integer64Property::Dump(uint8_t indent,
213 bool dumpImplicits, uint32_t index)
214 {
215 if (m_implicit && !dumpImplicits) {
216 return;
217 }
218 if (index != 0)
219 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %" PRIu64 " (0x%016" PRIx64 ")",
220 m_parentAtom.GetFile().GetFilename().c_str(),
221 m_name, index, m_values[index], m_values[index]);
222 else
223 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %" PRIu64 " (0x%016" PRIx64 ")",
224 m_parentAtom.GetFile().GetFilename().c_str(),
225 m_name, m_values[index], m_values[index]);
226 }
227
228 // MP4BitfieldProperty
229
Read(MP4File & file,uint32_t index)230 void MP4BitfieldProperty::Read(MP4File& file, uint32_t index)
231 {
232 if (m_implicit) {
233 return;
234 }
235 m_values[index] = file.ReadBits(m_numBits);
236 }
237
Write(MP4File & file,uint32_t index)238 void MP4BitfieldProperty::Write(MP4File& file, uint32_t index)
239 {
240 if (m_implicit) {
241 return;
242 }
243 file.WriteBits(m_values[index], m_numBits);
244 }
245
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)246 void MP4BitfieldProperty::Dump(uint8_t indent,
247 bool dumpImplicits, uint32_t index)
248 {
249 if (m_implicit && !dumpImplicits) {
250 return;
251 }
252 uint8_t hexWidth = m_numBits / 4;
253 if (hexWidth == 0 || (m_numBits % 4)) {
254 hexWidth++;
255 }
256 if (index != 0)
257 log.dump(indent, MP4_LOG_VERBOSE1,
258 "\"%s\": %s[%u] = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits>",
259 m_parentAtom.GetFile().GetFilename().c_str(),
260 m_name, index, m_values[index], (int)hexWidth, m_values[index], m_numBits);
261 else
262 log.dump(indent, MP4_LOG_VERBOSE1,
263 "\"%s\": %s = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits>",
264 m_parentAtom.GetFile().GetFilename().c_str(),
265 m_name, m_values[index], (int)hexWidth, m_values[index], m_numBits);
266 }
267
268 // MP4Float32Property
269
Read(MP4File & file,uint32_t index)270 void MP4Float32Property::Read(MP4File& file, uint32_t index)
271 {
272 if (m_implicit) {
273 return;
274 }
275 if (m_useFixed16Format) {
276 m_values[index] = file.ReadFixed16();
277 } else if (m_useFixed32Format) {
278 m_values[index] = file.ReadFixed32();
279 } else {
280 m_values[index] = file.ReadFloat();
281 }
282 }
283
Write(MP4File & file,uint32_t index)284 void MP4Float32Property::Write(MP4File& file, uint32_t index)
285 {
286 if (m_implicit) {
287 return;
288 }
289 if (m_useFixed16Format) {
290 file.WriteFixed16(m_values[index]);
291 } else if (m_useFixed32Format) {
292 file.WriteFixed32(m_values[index]);
293 } else {
294 file.WriteFloat(m_values[index]);
295 }
296 }
297
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)298 void MP4Float32Property::Dump(uint8_t indent,
299 bool dumpImplicits, uint32_t index)
300 {
301 if (m_implicit && !dumpImplicits) {
302 return;
303 }
304 if (index != 0)
305 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %f",
306 m_parentAtom.GetFile().GetFilename().c_str(),
307 m_name, index, m_values[index]);
308 else
309 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %f",
310 m_parentAtom.GetFile().GetFilename().c_str(),
311 m_name, m_values[index]);
312 }
313
314 // MP4StringProperty
315
MP4StringProperty(MP4Atom & parentAtom,const char * name,bool useCountedFormat,bool useUnicode,bool arrayMode)316 MP4StringProperty::MP4StringProperty(
317 MP4Atom& parentAtom,
318 const char* name,
319 bool useCountedFormat,
320 bool useUnicode,
321 bool arrayMode )
322
323 : MP4Property( parentAtom, name )
324 , m_arrayMode ( arrayMode )
325 , m_useCountedFormat ( useCountedFormat )
326 , m_useExpandedCount ( false )
327 , m_useUnicode ( useUnicode )
328 , m_fixedLength ( 0 )
329 {
330 SetCount( 1 );
331 m_values[0] = NULL;
332 }
333
~MP4StringProperty()334 MP4StringProperty::~MP4StringProperty()
335 {
336 uint32_t count = GetCount();
337 for (uint32_t i = 0; i < count; i++) {
338 MP4Free(m_values[i]);
339 }
340 }
341
SetCount(uint32_t count)342 void MP4StringProperty::SetCount(uint32_t count)
343 {
344 uint32_t oldCount = m_values.Size();
345
346 m_values.Resize(count);
347
348 for (uint32_t i = oldCount; i < count; i++) {
349 m_values[i] = NULL;
350 }
351 }
352
SetValue(const char * value,uint32_t index)353 void MP4StringProperty::SetValue(const char* value, uint32_t index)
354 {
355 if (m_readOnly) {
356 ostringstream msg;
357 msg << "property " << m_name << "is read-only";
358 throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
359 }
360
361 MP4Free(m_values[index]);
362
363 if (m_fixedLength) {
364 m_values[index] = (char*)MP4Calloc(m_fixedLength + 1);
365 if (value) {
366 strncpy(m_values[index], value, m_fixedLength);
367 }
368 } else {
369 if (value) {
370 m_values[index] = MP4Stralloc(value);
371 } else {
372 m_values[index] = NULL;
373 }
374 }
375 }
376
Read(MP4File & file,uint32_t index)377 void MP4StringProperty::Read( MP4File& file, uint32_t index )
378 {
379 if( m_implicit )
380 return;
381
382 uint32_t begin = index;
383 uint32_t max = index + 1;
384
385 if( m_arrayMode ) {
386 begin = 0;
387 max = GetCount();
388 }
389
390 for( uint32_t i = begin; i < max; i++ ) {
391 char*& value = m_values[i];
392
393 // Generally a default atom setting, e.g. see atom_avc1.cpp, "JVT/AVC Coding"; we'll leak this string if
394 // we don't free. Note that MP4Free checks for null.
395 MP4Free(value);
396
397 if( m_useCountedFormat ) {
398 value = file.ReadCountedString( (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
399 }
400 else if( m_fixedLength ) {
401 value = (char*)MP4Calloc( m_fixedLength + 1 );
402 file.ReadBytes( (uint8_t*)value, m_fixedLength );
403 }
404 else {
405 value = file.ReadString();
406 }
407 }
408 }
409
Write(MP4File & file,uint32_t index)410 void MP4StringProperty::Write( MP4File& file, uint32_t index )
411 {
412 if( m_implicit )
413 return;
414
415 uint32_t begin = index;
416 uint32_t max = index + 1;
417
418 if( m_arrayMode ) {
419 begin = 0;
420 max = GetCount();
421 }
422
423 for( uint32_t i = begin; i < max; i++ ) {
424 char*& value = m_values[i];
425
426 if( m_useCountedFormat ) {
427 file.WriteCountedString( value, (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
428 }
429 else if( m_fixedLength ) {
430 file.WriteBytes( (uint8_t*)value, m_fixedLength );
431 }
432 else {
433 file.WriteString( value );
434 }
435 }
436 }
437
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)438 void MP4StringProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
439 {
440 if( m_implicit && !dumpImplicits )
441 return;
442
443 if( !m_arrayMode ) {
444 char indexd[32];
445 if( index != 0 )
446 snprintf( indexd, 32, "[%u]", index );
447 else
448 indexd[0] = '\0';
449
450 if( m_useUnicode )
451 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s%s = %ls",
452 m_parentAtom.GetFile().GetFilename().c_str(),
453 m_name, indexd, (wchar_t*)m_values[index] );
454 else
455 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s%s = %s",
456 m_parentAtom.GetFile().GetFilename().c_str(),
457 m_name, indexd, m_values[index] );
458 }
459 else if( log.verbosity >= MP4_LOG_VERBOSE2 )
460 {
461 const uint32_t max = GetCount();
462
463 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s (size=%u)",
464 m_parentAtom.GetFile().GetFilename().c_str(),
465 m_name, max );
466
467 for( uint32_t i = 0; i < max; i++ ) {
468 char*& value = m_values[i];
469
470 if( m_useUnicode )
471 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s[%u] = %ls",
472 m_parentAtom.GetFile().GetFilename().c_str(),
473 m_name, i, (wchar_t*)value );
474 else
475 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s[%u] = %s",
476 m_parentAtom.GetFile().GetFilename().c_str(),
477 m_name, i, value );
478 }
479 }
480 else {
481 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": <table entries suppressed>",
482 m_parentAtom.GetFile().GetFilename().c_str() );
483 }
484 }
485
486 // MP4BytesProperty
487
MP4BytesProperty(MP4Atom & parentAtom,const char * name,uint32_t valueSize,uint32_t defaultValueSize)488 MP4BytesProperty::MP4BytesProperty(MP4Atom& parentAtom, const char* name, uint32_t valueSize,
489 uint32_t defaultValueSize)
490 : MP4Property(parentAtom, name)
491 , m_fixedValueSize(0)
492 , m_defaultValueSize(defaultValueSize)
493 {
494 SetCount(1);
495 m_values[0] = (uint8_t*)MP4Calloc(valueSize);
496 m_valueSizes[0] = valueSize;
497 }
498
~MP4BytesProperty()499 MP4BytesProperty::~MP4BytesProperty()
500 {
501 uint32_t count = GetCount();
502 for (uint32_t i = 0; i < count; i++) {
503 MP4Free(m_values[i]);
504 }
505 }
506
SetCount(uint32_t count)507 void MP4BytesProperty::SetCount(uint32_t count)
508 {
509 uint32_t oldCount = m_values.Size();
510
511 m_values.Resize(count);
512 m_valueSizes.Resize(count);
513
514 for (uint32_t i = oldCount; i < count; i++) {
515 m_values[i] = NULL;
516 m_valueSizes[i] = m_defaultValueSize;
517 }
518 }
519
SetValue(const uint8_t * pValue,uint32_t valueSize,uint32_t index)520 void MP4BytesProperty::SetValue(const uint8_t* pValue, uint32_t valueSize,
521 uint32_t index)
522 {
523 if (m_readOnly) {
524 ostringstream msg;
525 msg << "property " << m_name << "is read-only";
526 throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
527 }
528 if (m_fixedValueSize) {
529 if (valueSize > m_fixedValueSize) {
530 ostringstream msg;
531 msg << GetParentAtom().GetType() << "." << GetName() << " value size " << valueSize << " exceeds fixed value size " << m_fixedValueSize;
532 throw new Exception(msg.str().c_str(), __FILE__, __LINE__, __FUNCTION__ );
533 }
534 if (m_values[index] == NULL) {
535 m_values[index] = (uint8_t*)MP4Calloc(m_fixedValueSize);
536 m_valueSizes[index] = m_fixedValueSize;
537 }
538 if (pValue) {
539 memcpy(m_values[index], pValue, valueSize);
540 }
541 } else {
542 MP4Free(m_values[index]);
543 if (pValue) {
544 m_values[index] = (uint8_t*)MP4Malloc(valueSize);
545 memcpy(m_values[index], pValue, valueSize);
546 m_valueSizes[index] = valueSize;
547 } else {
548 m_values[index] = NULL;
549 m_valueSizes[index] = 0;
550 }
551 }
552 }
553
SetValueSize(uint32_t valueSize,uint32_t index)554 void MP4BytesProperty::SetValueSize(uint32_t valueSize, uint32_t index)
555 {
556 if (m_fixedValueSize) {
557 throw new Exception("can't change size of fixed sized property",
558 __FILE__, __LINE__, __FUNCTION__ );
559 }
560 if (m_values[index] != NULL) {
561 m_values[index] = (uint8_t*)MP4Realloc(m_values[index], valueSize);
562 }
563 m_valueSizes[index] = valueSize;
564 }
565
SetFixedSize(uint32_t fixedSize)566 void MP4BytesProperty::SetFixedSize(uint32_t fixedSize)
567 {
568 m_fixedValueSize = 0;
569 for (uint32_t i = 0; i < GetCount(); i++) {
570 SetValueSize(fixedSize, i);
571 }
572 m_fixedValueSize = fixedSize;
573 }
574
Read(MP4File & file,uint32_t index)575 void MP4BytesProperty::Read(MP4File& file, uint32_t index)
576 {
577 if (m_implicit) {
578 return;
579 }
580 MP4Free(m_values[index]);
581 m_values[index] = (uint8_t*)MP4Malloc(m_valueSizes[index]);
582 file.ReadBytes(m_values[index], m_valueSizes[index]);
583 }
584
Write(MP4File & file,uint32_t index)585 void MP4BytesProperty::Write(MP4File& file, uint32_t index)
586 {
587 if (m_implicit) {
588 return;
589 }
590 file.WriteBytes(m_values[index], m_valueSizes[index]);
591 }
592
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)593 void MP4BytesProperty::Dump(uint8_t indent,
594 bool dumpImplicits, uint32_t index)
595 {
596 if( m_implicit && !dumpImplicits )
597 return;
598
599 const uint32_t size = m_valueSizes[index];
600 const uint8_t* const value = m_values[index];
601
602 if( size == 0 ) {
603 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>",
604 m_parentAtom.GetFile().GetFilename().c_str(),
605 m_name, size );
606 return;
607 }
608
609 if( size <= 16 ) {
610 ostringstream oss;
611 ostringstream text;
612
613 oss << " ";
614 for( uint32_t i = 0; i < size; i++ ) {
615 if( i )
616 oss << ' ';
617 oss << hex << setw(2) << setfill('0') << right << static_cast<uint32_t>(value[i]);
618 text << (isprint( static_cast<int>(value[i]) ) ? static_cast<char>(value[i]) : '.');
619 }
620
621 oss << " |" << text.str() << "|";
622
623 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>%s",
624 m_parentAtom.GetFile().GetFilename().c_str(),
625 m_name, size, oss.str().c_str() );
626 return;
627 }
628
629 // specialization for ilst item data always show all bytes except for covr
630 bool showall = false;
631 MP4Atom* const datac = m_parentAtom.GetParentAtom(); // data container
632 MP4Atom* const datacc = datac->GetParentAtom();
633 if( datacc &&
634 ATOMID( datacc->GetType() ) == ATOMID( "ilst" ) &&
635 ATOMID( datac->GetType() ) != ATOMID( "covr" ) )
636 {
637 showall = true;
638 }
639
640 uint32_t adjsize;
641 bool supressed;
642
643 if( showall ||
644 size < 128 || log.verbosity >= MP4_LOG_VERBOSE2 )
645 {
646 adjsize = size;
647 supressed = false;
648 }
649 else {
650 adjsize = 128;
651 supressed = true;
652 }
653
654 ostringstream oss;
655 ostringstream text;
656
657 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>",
658 m_parentAtom.GetFile().GetFilename().c_str(),
659 m_name, size );
660 log.hexDump(indent, MP4_LOG_VERBOSE2, value, adjsize, "\"%s\": %s",
661 m_parentAtom.GetFile().GetFilename().c_str(),
662 m_name);
663
664 if( supressed ) {
665 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": <remaining bytes supressed>",
666 m_parentAtom.GetFile().GetFilename().c_str() );
667 }
668 }
669
670 // MP4TableProperty
671
MP4TableProperty(MP4Atom & parentAtom,const char * name,MP4IntegerProperty * pCountProperty)672 MP4TableProperty::MP4TableProperty(MP4Atom& parentAtom, const char* name, MP4IntegerProperty* pCountProperty)
673 : MP4Property(parentAtom, name)
674 {
675 m_pCountProperty = pCountProperty;
676 m_pCountProperty->SetReadOnly();
677 }
678
~MP4TableProperty()679 MP4TableProperty::~MP4TableProperty()
680 {
681 for (uint32_t i = 0; i < m_pProperties.Size(); i++) {
682 delete m_pProperties[i];
683 }
684 }
685
AddProperty(MP4Property * pProperty)686 void MP4TableProperty::AddProperty(MP4Property* pProperty)
687 {
688 ASSERT(pProperty);
689 ASSERT(pProperty->GetType() != TableProperty);
690 ASSERT(pProperty->GetType() != DescriptorProperty);
691 m_pProperties.Add(pProperty);
692 pProperty->SetCount(0);
693 }
694
FindProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)695 bool MP4TableProperty::FindProperty(const char *name,
696 MP4Property** ppProperty, uint32_t* pIndex)
697 {
698 ASSERT(m_name);
699
700 // check if first component of name matches ourselves
701 if (!MP4NameFirstMatches(m_name, name)) {
702 return false;
703 }
704
705 // check if the specified table entry exists
706 uint32_t index;
707 bool haveIndex = MP4NameFirstIndex(name, &index);
708 if (haveIndex) {
709 if (index >= GetCount()) {
710 return false;
711 }
712 if (pIndex) {
713 *pIndex = index;
714 }
715 }
716
717 log.verbose1f("\"%s\": FindProperty: matched %s",
718 m_parentAtom.GetFile().GetFilename().c_str(), name);
719
720 // get name of table property
721 const char *tablePropName = MP4NameAfterFirst(name);
722 if (tablePropName == NULL) {
723 if (!haveIndex) {
724 *ppProperty = this;
725 return true;
726 }
727 return false;
728 }
729
730 // check if this table property exists
731 return FindContainedProperty(tablePropName, ppProperty, pIndex);
732 }
733
FindContainedProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)734 bool MP4TableProperty::FindContainedProperty(const char *name,
735 MP4Property** ppProperty, uint32_t* pIndex)
736 {
737 uint32_t numProperties = m_pProperties.Size();
738
739 for (uint32_t i = 0; i < numProperties; i++) {
740 if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) {
741 return true;
742 }
743 }
744 return false;
745 }
746
Read(MP4File & file,uint32_t index)747 void MP4TableProperty::Read(MP4File& file, uint32_t index)
748 {
749 ASSERT(index == 0);
750
751 if (m_implicit) {
752 return;
753 }
754
755 uint32_t numProperties = m_pProperties.Size();
756
757 if (numProperties == 0) {
758 WARNING(numProperties == 0);
759 return;
760 }
761
762 uint32_t numEntries = GetCount();
763
764 /* for each property set size */
765 for (uint32_t j = 0; j < numProperties; j++) {
766 m_pProperties[j]->SetCount(numEntries);
767 }
768
769 for (uint32_t i = 0; i < numEntries; i++) {
770 ReadEntry(file, i);
771 }
772 }
773
ReadEntry(MP4File & file,uint32_t index)774 void MP4TableProperty::ReadEntry(MP4File& file, uint32_t index)
775 {
776 for (uint32_t j = 0; j < m_pProperties.Size(); j++) {
777 m_pProperties[j]->Read(file, index);
778 }
779 }
780
Write(MP4File & file,uint32_t index)781 void MP4TableProperty::Write(MP4File& file, uint32_t index)
782 {
783 ASSERT(index == 0);
784
785 if (m_implicit) {
786 return;
787 }
788
789 uint32_t numProperties = m_pProperties.Size();
790
791 if (numProperties == 0) {
792 WARNING(numProperties == 0);
793 return;
794 }
795
796 uint32_t numEntries = GetCount();
797
798 if (m_pProperties[0]->GetCount() != numEntries) {
799 log.errorf("%s: \"%s\": %s %s \"%s\"table entries %u doesn't match count %u",
800 __FUNCTION__, m_parentAtom.GetFile().GetFilename().c_str(),
801 GetParentAtom().GetType(),
802 GetName(), m_pProperties[0]->GetName(),
803 m_pProperties[0]->GetCount(), numEntries);
804
805 ASSERT(m_pProperties[0]->GetCount() == numEntries);
806 }
807
808 for (uint32_t i = 0; i < numEntries; i++) {
809 WriteEntry(file, i);
810 }
811 }
812
WriteEntry(MP4File & file,uint32_t index)813 void MP4TableProperty::WriteEntry(MP4File& file, uint32_t index)
814 {
815 for (uint32_t j = 0; j < m_pProperties.Size(); j++) {
816 m_pProperties[j]->Write(file, index);
817 }
818 }
819
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)820 void MP4TableProperty::Dump(uint8_t indent,
821 bool dumpImplicits, uint32_t index)
822 {
823 ASSERT(index == 0);
824
825 // implicit tables just can't be dumped
826 if (m_implicit) {
827 return;
828 }
829
830 uint32_t numProperties = m_pProperties.Size();
831
832 if (numProperties == 0) {
833 WARNING(numProperties == 0);
834 return;
835 }
836
837 uint32_t numEntries = GetCount();
838
839 for (uint32_t i = 0; i < numEntries; i++) {
840 for (uint32_t j = 0; j < numProperties; j++) {
841 m_pProperties[j]->Dump(indent + 1, dumpImplicits, i);
842 }
843 }
844 }
845
846 // MP4DescriptorProperty
847
MP4DescriptorProperty(MP4Atom & parentAtom,const char * name,uint8_t tagsStart,uint8_t tagsEnd,bool mandatory,bool onlyOne)848 MP4DescriptorProperty::MP4DescriptorProperty(MP4Atom& parentAtom, const char* name,
849 uint8_t tagsStart, uint8_t tagsEnd, bool mandatory, bool onlyOne)
850 : MP4Property(parentAtom, name)
851 {
852 SetTags(tagsStart, tagsEnd);
853 m_sizeLimit = 0;
854 m_mandatory = mandatory;
855 m_onlyOne = onlyOne;
856 }
857
~MP4DescriptorProperty()858 MP4DescriptorProperty::~MP4DescriptorProperty()
859 {
860 for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
861 delete m_pDescriptors[i];
862 }
863 }
864
AddDescriptor(uint8_t tag)865 MP4Descriptor* MP4DescriptorProperty::AddDescriptor(uint8_t tag)
866 {
867 // check that tag is in expected range
868 ASSERT(tag >= m_tagsStart && tag <= m_tagsEnd);
869
870 MP4Descriptor* pDescriptor = CreateDescriptor(m_parentAtom, tag);
871 ASSERT(pDescriptor);
872
873 m_pDescriptors.Add(pDescriptor);
874
875 return pDescriptor;
876 }
877
DeleteDescriptor(uint32_t index)878 void MP4DescriptorProperty::DeleteDescriptor(uint32_t index)
879 {
880 delete m_pDescriptors[index];
881 m_pDescriptors.Delete(index);
882 }
883
Generate()884 void MP4DescriptorProperty::Generate()
885 {
886 // generate a default descriptor
887 // if it is mandatory, and single
888 if (m_mandatory && m_onlyOne) {
889 MP4Descriptor* pDescriptor =
890 AddDescriptor(m_tagsStart);
891 pDescriptor->Generate();
892 }
893 }
894
FindProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)895 bool MP4DescriptorProperty::FindProperty(const char *name,
896 MP4Property** ppProperty, uint32_t* pIndex)
897 {
898 // we're unnamed, so just check contained properties
899 if (m_name == NULL || !strcmp(m_name, "")) {
900 return FindContainedProperty(name, ppProperty, pIndex);
901 }
902
903 // check if first component of name matches ourselves
904 if (!MP4NameFirstMatches(m_name, name)) {
905 return false;
906 }
907
908 // check if the specific descriptor entry exists
909 uint32_t descrIndex;
910 bool haveDescrIndex = MP4NameFirstIndex(name, &descrIndex);
911
912 if (haveDescrIndex && descrIndex >= GetCount()) {
913 return false;
914 }
915
916 log.verbose1f("\"%s\": matched %s",
917 m_parentAtom.GetFile().GetFilename().c_str(),
918 name);
919
920 // get name of descriptor property
921 name = MP4NameAfterFirst(name);
922 if (name == NULL) {
923 if (!haveDescrIndex) {
924 *ppProperty = this;
925 return true;
926 }
927 return false;
928 }
929
930 /* check rest of name */
931 if (haveDescrIndex) {
932 return m_pDescriptors[descrIndex]->FindProperty(name,
933 ppProperty, pIndex);
934 } else {
935 return FindContainedProperty(name, ppProperty, pIndex);
936 }
937 }
938
FindContainedProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)939 bool MP4DescriptorProperty::FindContainedProperty(const char *name,
940 MP4Property** ppProperty, uint32_t* pIndex)
941 {
942 for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
943 if (m_pDescriptors[i]->FindProperty(name, ppProperty, pIndex)) {
944 return true;
945 }
946 }
947 return false;
948 }
949
Read(MP4File & file,uint32_t index)950 void MP4DescriptorProperty::Read(MP4File& file, uint32_t index)
951 {
952 ASSERT(index == 0);
953
954 if (m_implicit) {
955 return;
956 }
957
958 uint64_t start = file.GetPosition();
959
960 while (true) {
961 // enforce size limitation
962 if (m_sizeLimit && file.GetPosition() >= start + m_sizeLimit) {
963 break;
964 }
965
966 uint8_t tag;
967 try {
968 file.PeekBytes(&tag, 1);
969 }
970 catch (Exception* x) {
971 if (file.GetPosition() >= file.GetSize()) {
972 // EOF
973 delete x;
974 break;
975 }
976 throw x;
977 }
978
979 // check if tag is in desired range
980 if (tag < m_tagsStart || tag > m_tagsEnd) {
981 break;
982 }
983
984 MP4Descriptor* pDescriptor =
985 AddDescriptor(tag);
986
987 pDescriptor->Read(file);
988 }
989
990 // warnings
991 if (m_mandatory && m_pDescriptors.Size() == 0) {
992 log.warningf("%s: \"%s\": Mandatory descriptor 0x%02x missing",
993 __FUNCTION__, GetParentAtom().GetFile().GetFilename().c_str(), m_tagsStart);
994 } else if (m_onlyOne && m_pDescriptors.Size() > 1) {
995 log.warningf("%s: \"%s\": Descriptor 0x%02x has more than one instance",
996 __FUNCTION__, GetParentAtom().GetFile().GetFilename().c_str(), m_tagsStart);
997 }
998 }
999
Write(MP4File & file,uint32_t index)1000 void MP4DescriptorProperty::Write(MP4File& file, uint32_t index)
1001 {
1002 ASSERT(index == 0);
1003
1004 if (m_implicit) {
1005 return;
1006 }
1007
1008 for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
1009 m_pDescriptors[i]->Write(file);
1010 }
1011 }
1012
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)1013 void MP4DescriptorProperty::Dump(uint8_t indent,
1014 bool dumpImplicits, uint32_t index)
1015 {
1016 ASSERT(index == 0);
1017
1018 if (m_implicit && !dumpImplicits) {
1019 return;
1020 }
1021
1022 if (m_name) {
1023 if (index != 0)
1024 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u]",
1025 m_parentAtom.GetFile().GetFilename().c_str(),
1026 m_name, index);
1027 else
1028 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s",
1029 m_parentAtom.GetFile().GetFilename().c_str(),
1030 m_name);
1031 indent++;
1032 }
1033
1034 for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
1035 m_pDescriptors[i]->Dump(indent, dumpImplicits);
1036 }
1037 }
1038
1039 ///////////////////////////////////////////////////////////////////////////////
1040
MP4LanguageCodeProperty(MP4Atom & parentAtom,const char * name,bmff::LanguageCode value)1041 MP4LanguageCodeProperty::MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* name, bmff::LanguageCode value )
1042 : MP4Property( parentAtom, name )
1043 {
1044 SetValue( value );
1045 }
1046
MP4LanguageCodeProperty(MP4Atom & parentAtom,const char * name,const string & code)1047 MP4LanguageCodeProperty::MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* name, const string& code )
1048 : MP4Property( parentAtom, name )
1049 {
1050 SetValue( bmff::enumLanguageCode.toType( code ));
1051 }
1052
1053 void
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)1054 MP4LanguageCodeProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
1055 {
1056 uint16_t data = 0;
1057
1058 string svalue;
1059 bmff::enumLanguageCode.toString( _value, svalue );
1060 if( svalue.length() == 3 ) {
1061 data = (((svalue[0] - 0x60) & 0x001f) << 10)
1062 | (((svalue[1] - 0x60) & 0x001f) << 5)
1063 | (((svalue[2] - 0x60) & 0x001f) );
1064 }
1065
1066 log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = %s (0x%04x)",
1067 m_parentAtom.GetFile().GetFilename().c_str(),
1068 m_name, bmff::enumLanguageCode.toString( _value, true ).c_str(), data );
1069 }
1070
1071 uint32_t
GetCount()1072 MP4LanguageCodeProperty::GetCount()
1073 {
1074 return 1;
1075 }
1076
1077 MP4PropertyType
GetType()1078 MP4LanguageCodeProperty::GetType()
1079 {
1080 return LanguageCodeProperty;
1081 }
1082
1083 bmff::LanguageCode
GetValue()1084 MP4LanguageCodeProperty::GetValue()
1085 {
1086 return _value;
1087 }
1088
1089 void
Read(MP4File & file,uint32_t index)1090 MP4LanguageCodeProperty::Read( MP4File& file, uint32_t index )
1091 {
1092 uint16_t data = file.ReadBits( 16 );
1093
1094 char code[3];
1095 code[0] = ((data & 0x7c00) >> 10) + 0x60;
1096 code[1] = ((data & 0x03e0) >> 5) + 0x60;
1097 code[2] = ((data & 0x001f) ) + 0x60;
1098
1099 SetValue( bmff::enumLanguageCode.toType( string( code, sizeof(code) )));
1100 }
1101
1102 void
SetCount(uint32_t count)1103 MP4LanguageCodeProperty::SetCount( uint32_t count )
1104 {
1105 // do nothing; count is always 1
1106 }
1107
1108 void
SetValue(bmff::LanguageCode value)1109 MP4LanguageCodeProperty::SetValue( bmff::LanguageCode value )
1110 {
1111 _value = value;
1112 }
1113
1114 void
Write(MP4File & file,uint32_t index)1115 MP4LanguageCodeProperty::Write( MP4File& file, uint32_t index )
1116 {
1117 uint16_t data = 0;
1118
1119 string svalue;
1120 bmff::enumLanguageCode.toString( _value, svalue );
1121 if( svalue.length() == 3 ) {
1122 data = (((svalue[0] - 0x60) & 0x001f) << 10)
1123 | (((svalue[1] - 0x60) & 0x001f) << 5)
1124 | (((svalue[2] - 0x60) & 0x001f) );
1125 }
1126
1127 file.WriteBits( data, 16 );
1128 }
1129
1130 ///////////////////////////////////////////////////////////////////////////////
1131
MP4BasicTypeProperty(MP4Atom & parentAtom,const char * name,itmf::BasicType type)1132 MP4BasicTypeProperty::MP4BasicTypeProperty( MP4Atom& parentAtom, const char* name, itmf::BasicType type )
1133 : MP4Property( parentAtom, name )
1134 {
1135 SetValue( type );
1136 }
1137
1138 void
Dump(uint8_t indent,bool dumpImplicits,uint32_t index)1139 MP4BasicTypeProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
1140 {
1141 log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %s (0x%02x)",
1142 m_parentAtom.GetFile().GetFilename().c_str(), m_name,
1143 itmf::enumBasicType.toString( _value, true ).c_str(), _value );
1144 }
1145
1146 uint32_t
GetCount()1147 MP4BasicTypeProperty::GetCount()
1148 {
1149 return 1;
1150 }
1151
1152 MP4PropertyType
GetType()1153 MP4BasicTypeProperty::GetType()
1154 {
1155 return BasicTypeProperty;
1156 }
1157
1158 itmf::BasicType
GetValue()1159 MP4BasicTypeProperty::GetValue()
1160 {
1161 return _value;
1162 }
1163
1164 void
Read(MP4File & file,uint32_t index)1165 MP4BasicTypeProperty::Read( MP4File& file, uint32_t index )
1166 {
1167 SetValue( static_cast<itmf::BasicType>( file.ReadBits( 8 )));
1168 }
1169
1170 void
SetCount(uint32_t count)1171 MP4BasicTypeProperty::SetCount( uint32_t count )
1172 {
1173 // do nothing; count is always 1
1174 }
1175
1176 void
SetValue(itmf::BasicType value)1177 MP4BasicTypeProperty::SetValue( itmf::BasicType value )
1178 {
1179 _value = value;
1180 }
1181
1182 void
Write(MP4File & file,uint32_t index)1183 MP4BasicTypeProperty::Write( MP4File& file, uint32_t index )
1184 {
1185 file.WriteBits( _value, 8 );
1186 }
1187
1188 ///////////////////////////////////////////////////////////////////////////////
1189
1190 }} // namespace mp4v2::impl
1191