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