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 - 2005.  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  *              Bill May                  wmay@cisco.com
29  */
30 
31 #include "src/impl.h"
32 
33 namespace mp4v2 { namespace impl {
34 
35 ///////////////////////////////////////////////////////////////////////////////
36 
MP4File()37 MP4File::MP4File( ) :
38     m_file             ( NULL )
39     , m_fileOriginalSize ( 0 )
40     , m_createFlags      ( 0 )
41 {
42     this->Init();
43 }
44 
45 /**
46  * Initialize member variables (shared among constructors)
47  */
Init()48 void MP4File::Init()
49 {
50     m_pRootAtom = NULL;
51     m_odTrackId = MP4_INVALID_TRACK_ID;
52 
53     m_useIsma = false;
54 
55     m_pModificationProperty = NULL;
56     m_pTimeScaleProperty = NULL;
57     m_pDurationProperty = NULL;
58 
59     m_memoryBuffer = NULL;
60     m_memoryBufferSize = 0;
61     m_memoryBufferPosition = 0;
62 
63     m_numReadBits = 0;
64     m_bufReadBits = 0;
65     m_numWriteBits = 0;
66     m_bufWriteBits = 0;
67     m_editName = NULL;
68     m_trakName[0] = '\0';
69 }
70 
~MP4File()71 MP4File::~MP4File()
72 {
73     delete m_pRootAtom;
74     for( uint32_t i = 0; i < m_pTracks.Size(); i++ )
75         delete m_pTracks[i];
76     MP4Free( m_memoryBuffer ); // just in case
77     CHECK_AND_FREE( m_editName );
78     delete m_file;
79 }
80 
81 const std::string &
GetFilename() const82 MP4File::GetFilename() const
83 {
84     // No one should call this unless Read, etc. has
85     // succeeded and m_file exists since this method really
86     // only exists for the public API.  This helps us
87     // guarantee that MP4GetFilename always returns a valid
88     // string given a valid MP4FileHandle
89     ASSERT(m_file);
90     return m_file->name;
91 }
92 
Read(const char * name,const MP4FileProvider * provider)93 void MP4File::Read( const char* name, const MP4FileProvider* provider )
94 {
95     Open( name, File::MODE_READ, provider );
96     ReadFromFile();
97     CacheProperties();
98 }
99 
Create(const char * fileName,uint32_t flags,int add_ftyp,int add_iods,char * majorBrand,uint32_t minorVersion,char ** supportedBrands,uint32_t supportedBrandsCount)100 void MP4File::Create( const char* fileName,
101                       uint32_t    flags,
102                       int         add_ftyp,
103                       int         add_iods,
104                       char*       majorBrand,
105                       uint32_t    minorVersion,
106                       char**      supportedBrands,
107                       uint32_t    supportedBrandsCount )
108 {
109     m_createFlags = flags;
110     Open( fileName, File::MODE_CREATE, NULL );
111 
112     // generate a skeletal atom tree
113     m_pRootAtom = MP4Atom::CreateAtom(*this, NULL, NULL);
114     m_pRootAtom->Generate();
115 
116     if (add_ftyp != 0) {
117         MakeFtypAtom(majorBrand, minorVersion,
118                      supportedBrands, supportedBrandsCount);
119     }
120 
121     CacheProperties();
122 
123     // create mdat, and insert it after ftyp, and before moov
124     (void)InsertChildAtom(m_pRootAtom, "mdat",
125                           add_ftyp != 0 ? 1 : 0);
126 
127     // start writing
128     m_pRootAtom->BeginWrite();
129     if (add_iods != 0) {
130         (void)AddChildAtom("moov", "iods");
131     }
132 }
133 
Use64Bits(const char * atomName)134 bool MP4File::Use64Bits (const char *atomName)
135 {
136     uint32_t atomid = ATOMID(atomName);
137     if (atomid == ATOMID("mdat") || atomid == ATOMID("stbl")) {
138         return (m_createFlags & MP4_CREATE_64BIT_DATA) == MP4_CREATE_64BIT_DATA;
139     }
140     if (atomid == ATOMID("mvhd") ||
141             atomid == ATOMID("tkhd") ||
142             atomid == ATOMID("mdhd")) {
143         return (m_createFlags & MP4_CREATE_64BIT_TIME) == MP4_CREATE_64BIT_TIME;
144     }
145     return false;
146 }
147 
Check64BitStatus(const char * atomName)148 void MP4File::Check64BitStatus (const char *atomName)
149 {
150     uint32_t atomid = ATOMID(atomName);
151 
152     if (atomid == ATOMID("mdat") || atomid == ATOMID("stbl")) {
153         m_createFlags |= MP4_CREATE_64BIT_DATA;
154     } else if (atomid == ATOMID("mvhd") ||
155                atomid == ATOMID("tkhd") ||
156                atomid == ATOMID("mdhd")) {
157         m_createFlags |= MP4_CREATE_64BIT_TIME;
158     }
159 }
160 
161 
Modify(const char * fileName)162 bool MP4File::Modify( const char* fileName )
163 {
164     Open( fileName, File::MODE_MODIFY, NULL );
165     ReadFromFile();
166 
167     // find the moov atom
168     MP4Atom* pMoovAtom = m_pRootAtom->FindAtom("moov");
169     uint32_t numAtoms;
170 
171     if (pMoovAtom == NULL) {
172         // there isn't one, odd but we can still proceed
173         log.warningf("%s: \"%s\": no moov atom, can't modify",
174                      __FUNCTION__, GetFilename().c_str());
175         return false;
176         //pMoovAtom = AddChildAtom(m_pRootAtom, "moov");
177     } else {
178         numAtoms = m_pRootAtom->GetNumberOfChildAtoms();
179 
180         // work backwards thru the top level atoms
181         int32_t i;
182         bool lastAtomIsMoov = true;
183         MP4Atom* pLastAtom = NULL;
184 
185         for (i = numAtoms - 1; i >= 0; i--) {
186             MP4Atom* pAtom = m_pRootAtom->GetChildAtom(i);
187             const char* type = pAtom->GetType();
188 
189             // get rid of any trailing free or skips
190             if (!strcmp(type, "free") || !strcmp(type, "skip")) {
191                 m_pRootAtom->DeleteChildAtom(pAtom);
192                 continue;
193             }
194 
195             if (strcmp(type, "moov")) {
196                 if (pLastAtom == NULL) {
197                     pLastAtom = pAtom;
198                     lastAtomIsMoov = false;
199                 }
200                 continue;
201             }
202 
203             // now at moov atom
204 
205             // multiple moov atoms?!?
206             if (pAtom != pMoovAtom) {
207                 throw new Exception(
208                     "Badly formed mp4 file, multiple moov atoms",
209                     __FILE__,__LINE__,__FUNCTION__);
210             }
211 
212             if (lastAtomIsMoov) {
213                 // position to start of moov atom,
214                 // effectively truncating file
215                 // prior to adding new mdat
216                 SetPosition(pMoovAtom->GetStart());
217 
218             } else { // last atom isn't moov
219                 // need to place a free atom
220                 MP4Atom* pFreeAtom = MP4Atom::CreateAtom(*this, NULL, "free");
221 
222                 // in existing position of the moov atom
223                 m_pRootAtom->InsertChildAtom(pFreeAtom, i);
224                 m_pRootAtom->DeleteChildAtom(pMoovAtom);
225                 m_pRootAtom->AddChildAtom(pMoovAtom);
226 
227                 // write free atom to disk
228                 SetPosition(pMoovAtom->GetStart());
229                 pFreeAtom->SetSize(pMoovAtom->GetSize());
230                 pFreeAtom->Write();
231 
232                 // finally set our file position to the end of the last atom
233                 SetPosition(pLastAtom->GetEnd());
234             }
235 
236             break;
237         }
238         ASSERT(i != -1);
239     }
240 
241     CacheProperties();  // of moov atom
242 
243     numAtoms = m_pRootAtom->GetNumberOfChildAtoms();
244 
245     // insert another mdat prior to moov atom (the last atom)
246     MP4Atom* pMdatAtom = InsertChildAtom(m_pRootAtom, "mdat", numAtoms - 1);
247 
248     // start writing new mdat
249     pMdatAtom->BeginWrite(Use64Bits("mdat"));
250     return true;
251 }
252 
Optimize(const char * srcFileName,const char * dstFileName)253 void MP4File::Optimize( const char* srcFileName, const char* dstFileName )
254 {
255     File* src = NULL;
256     File* dst = NULL;
257 
258     // compute destination filename
259     string dname;
260     if( dstFileName ) {
261         dname = dstFileName;
262     } else {
263         // No destination given, so let's kludge together a temporary file.
264         // We'll try to create it in the same directory as the srcFileName, since
265         // it's more likely that directory is writable.  In the absence of that,
266         // we'll create it in "./", which is the default pathnameTemp() provides.
267         string s(srcFileName);
268         size_t pos = s.find_last_of("\\/");
269         const char *d;
270         if (pos == string::npos) {
271             d = ".";
272         } else {
273             s = s.substr(0, pos);
274             d = s.c_str();
275         }
276         FileSystem::pathnameTemp( dname, d, "tmp", ".mp4" );
277     }
278 
279     try {
280         // file source to optimize
281         Open( srcFileName, File::MODE_READ, NULL );
282         ReadFromFile();
283         CacheProperties(); // of moov atom
284 
285         src = m_file;
286         m_file = NULL;
287 
288         // optimized file destination
289         Open( dname.c_str(), File::MODE_CREATE, NULL );
290         dst = m_file;
291 
292         SetIntegerProperty( "moov.mvhd.modificationTime", MP4GetAbsTimestamp() );
293 
294         // writing meta info in the optimal order
295         ((MP4RootAtom*)m_pRootAtom)->BeginOptimalWrite();
296 
297         // write data in optimal order
298         RewriteMdat( *src, *dst );
299 
300         // finish writing
301         ((MP4RootAtom*)m_pRootAtom)->FinishOptimalWrite();
302 
303     }
304     catch (...) {
305         // cleanup and rethrow.  Without this, we'd leak memory and an open file handle(s).
306        if(src == NULL && dst == NULL)
307             delete m_file;// We didn't make it far enough to have m_file go to src or dst.
308 
309         m_file = NULL;
310         delete dst;
311         delete src;
312         throw;
313     }
314 
315     // cleanup
316     delete dst;
317     delete src;
318     m_file = NULL;
319 
320     // move temporary file into place
321     if( !dstFileName )
322         Rename( dname.c_str(), srcFileName );
323 }
324 
RewriteMdat(File & src,File & dst)325 void MP4File::RewriteMdat( File& src, File& dst )
326 {
327     uint32_t numTracks = m_pTracks.Size();
328 
329     MP4ChunkId* chunkIds = new MP4ChunkId[numTracks];
330     MP4ChunkId* maxChunkIds = new MP4ChunkId[numTracks];
331     MP4Timestamp* nextChunkTimes = new MP4Timestamp[numTracks];
332 
333     for( uint32_t i = 0; i < numTracks; i++ ) {
334         chunkIds[i] = 1;
335         maxChunkIds[i] = m_pTracks[i]->GetNumberOfChunks();
336         nextChunkTimes[i] = MP4_INVALID_TIMESTAMP;
337     }
338 
339     for( ;; ) {
340         uint32_t nextTrackIndex = (uint32_t)-1;
341         MP4Timestamp nextTime = MP4_INVALID_TIMESTAMP;
342 
343         for( uint32_t i = 0; i < numTracks; i++ ) {
344             if( chunkIds[i] > maxChunkIds[i] )
345                 continue;
346 
347             if( nextChunkTimes[i] == MP4_INVALID_TIMESTAMP ) {
348                 MP4Timestamp chunkTime = m_pTracks[i]->GetChunkTime( chunkIds[i] );
349                 nextChunkTimes[i] = MP4ConvertTime( chunkTime, m_pTracks[i]->GetTimeScale(), GetTimeScale() );
350             }
351 
352             // time is not earliest so far
353             if( nextChunkTimes[i] > nextTime )
354                 continue;
355 
356             // prefer hint tracks to media tracks if times are equal
357             if( nextChunkTimes[i] == nextTime && strcmp( m_pTracks[i]->GetType(), MP4_HINT_TRACK_TYPE ))
358                 continue;
359 
360             // this is our current choice of tracks
361             nextTime = nextChunkTimes[i];
362             nextTrackIndex = i;
363         }
364 
365         if( nextTrackIndex == (uint32_t)-1 )
366             break;
367 
368         uint8_t* pChunk;
369         uint32_t chunkSize;
370 
371         // point into original mp4 file for read chunk call
372         m_file = &src;
373         m_pTracks[nextTrackIndex]->ReadChunk( chunkIds[nextTrackIndex], &pChunk, &chunkSize );
374 
375         // point back at the new mp4 file for write chunk
376         m_file = &dst;
377         m_pTracks[nextTrackIndex]->RewriteChunk( chunkIds[nextTrackIndex], pChunk, chunkSize );
378 
379         MP4Free( pChunk );
380 
381         chunkIds[nextTrackIndex]++;
382         nextChunkTimes[nextTrackIndex] = MP4_INVALID_TIMESTAMP;
383     }
384 
385     delete [] chunkIds;
386     delete [] maxChunkIds;
387     delete [] nextChunkTimes;
388 }
389 
Open(const char * name,File::Mode mode,const MP4FileProvider * provider)390 void MP4File::Open( const char* name, File::Mode mode, const MP4FileProvider* provider )
391 {
392     ASSERT( !m_file );
393 
394     m_file = new File( name, mode, provider ? new io::CustomFileProvider( *provider ) : NULL );
395     if( m_file->open() ) {
396         ostringstream msg;
397         msg << "open(" << name << ") failed";
398         throw new Exception( msg.str(), __FILE__, __LINE__, __FUNCTION__);
399     }
400 
401     switch( mode ) {
402         case File::MODE_READ:
403         case File::MODE_MODIFY:
404             m_fileOriginalSize = m_file->size;
405             break;
406 
407         case File::MODE_CREATE:
408         default:
409             m_fileOriginalSize = 0;
410             break;
411     }
412 }
413 
ReadFromFile()414 void MP4File::ReadFromFile()
415 {
416     // ensure we start at beginning of file
417     SetPosition(0);
418 
419     // create a new root atom
420     ASSERT(m_pRootAtom == NULL);
421     m_pRootAtom = MP4Atom::CreateAtom(*this, NULL, NULL);
422 
423     uint64_t fileSize = GetSize();
424 
425     m_pRootAtom->SetStart(0);
426     m_pRootAtom->SetSize(fileSize);
427     m_pRootAtom->SetEnd(fileSize);
428 
429     m_pRootAtom->Read();
430 
431     // create MP4Track's for any tracks in the file
432     GenerateTracks();
433 }
434 
GenerateTracks()435 void MP4File::GenerateTracks()
436 {
437     uint32_t trackIndex = 0;
438 
439     while (true) {
440         char trackName[32];
441         snprintf(trackName, sizeof(trackName), "moov.trak[%u]", trackIndex);
442 
443         // find next trak atom
444         MP4Atom* pTrakAtom = m_pRootAtom->FindAtom(trackName);
445 
446         // done, no more trak atoms
447         if (pTrakAtom == NULL) {
448             break;
449         }
450 
451         // find track id property
452         MP4Integer32Property* pTrackIdProperty = NULL;
453         (void)pTrakAtom->FindProperty(
454             "trak.tkhd.trackId",
455             (MP4Property**)&pTrackIdProperty);
456 
457         // find track type property
458         MP4StringProperty* pTypeProperty = NULL;
459         (void)pTrakAtom->FindProperty(
460             "trak.mdia.hdlr.handlerType",
461             (MP4Property**)&pTypeProperty);
462 
463         // ensure we have the basics properties
464         if (pTrackIdProperty && pTypeProperty) {
465 
466             m_trakIds.Add(pTrackIdProperty->GetValue());
467 
468             MP4Track* pTrack = NULL;
469             try {
470                 if (!strcmp(pTypeProperty->GetValue(), MP4_HINT_TRACK_TYPE)) {
471                     pTrack = new MP4RtpHintTrack(*this, *pTrakAtom);
472                 } else {
473                     pTrack = new MP4Track(*this, *pTrakAtom);
474                 }
475                 m_pTracks.Add(pTrack);
476             }
477             catch( Exception* x ) {
478                 log.errorf(*x);
479                 delete x;
480             }
481 
482             // remember when we encounter the OD track
483             if (pTrack && !strcmp(pTrack->GetType(), MP4_OD_TRACK_TYPE)) {
484                 if (m_odTrackId == MP4_INVALID_TRACK_ID) {
485                     m_odTrackId = pTrackIdProperty->GetValue();
486                 } else {
487                     log.warningf("%s: \"%s\": multiple OD tracks present",
488                                  __FUNCTION__, GetFilename().c_str() );
489                 }
490             }
491         } else {
492             m_trakIds.Add(0);
493         }
494 
495         trackIndex++;
496     }
497 }
498 
CacheProperties()499 void MP4File::CacheProperties()
500 {
501     FindIntegerProperty("moov.mvhd.modificationTime",
502                         (MP4Property**)&m_pModificationProperty);
503 
504     FindIntegerProperty("moov.mvhd.timeScale",
505                         (MP4Property**)&m_pTimeScaleProperty);
506 
507     FindIntegerProperty("moov.mvhd.duration",
508                         (MP4Property**)&m_pDurationProperty);
509 }
510 
BeginWrite()511 void MP4File::BeginWrite()
512 {
513     m_pRootAtom->BeginWrite();
514 }
515 
FinishWrite(uint32_t options)516 void MP4File::FinishWrite(uint32_t options)
517 {
518     // remove empty moov.udta.meta.ilst
519     {
520         MP4Atom* ilst = FindAtom( "moov.udta.meta.ilst" );
521         if( ilst ) {
522             if( ilst->GetNumberOfChildAtoms() == 0 ) {
523                 ilst->GetParentAtom()->DeleteChildAtom( ilst );
524                 delete ilst;
525             }
526         }
527     }
528 
529     // remove empty moov.udta.meta
530     {
531         MP4Atom* meta = FindAtom( "moov.udta.meta" );
532         if( meta ) {
533             if( meta->GetNumberOfChildAtoms() == 0 ) {
534                 meta->GetParentAtom()->DeleteChildAtom( meta );
535                 delete meta;
536             }
537             else if( meta->GetNumberOfChildAtoms() == 1 ) {
538                 if( ATOMID( meta->GetChildAtom( 0 )->GetType() ) == ATOMID( "hdlr" )) {
539                     meta->GetParentAtom()->DeleteChildAtom( meta );
540                     delete meta;
541                 }
542             }
543         }
544     }
545 
546     // remove empty moov.udta.name
547     {
548         MP4Atom* name = FindAtom( "moov.udta.name" );
549         if( name ) {
550             unsigned char *val = NULL;
551             uint32_t valSize = 0;
552             GetBytesProperty("moov.udta.name.value", (uint8_t**)&val, &valSize);
553             if( valSize == 0 ) {
554                 name->GetParentAtom()->DeleteChildAtom( name );
555                 delete name;
556             }
557         }
558     }
559 
560     // remove empty moov.udta
561     {
562         MP4Atom* udta = FindAtom( "moov.udta" );
563         if( udta ) {
564             if( udta->GetNumberOfChildAtoms() == 0 ) {
565                 udta->GetParentAtom()->DeleteChildAtom( udta );
566                 delete udta;
567             }
568         }
569     }
570 
571     // for all tracks, flush chunking buffers
572     for( uint32_t i = 0; i < m_pTracks.Size(); i++ ) {
573         ASSERT( m_pTracks[i] );
574         m_pTracks[i]->FinishWrite(options);
575     }
576 
577     // ask root atom to write
578     m_pRootAtom->FinishWrite();
579 
580     // finished all writes, if position < size then file has shrunk and
581     // we mark remaining bytes as free atom; otherwise trailing garbage remains.
582     if( GetPosition() < GetSize() ) {
583         MP4RootAtom* root = (MP4RootAtom*)FindAtom( "" );
584         ASSERT( root );
585 
586         // compute size of free atom; always has 8 bytes of overhead
587         uint64_t size = GetSize() - GetPosition();
588         if( size < 8 )
589             size = 0;
590         else
591             size -= 8;
592 
593         MP4FreeAtom* freeAtom = (MP4FreeAtom*)MP4Atom::CreateAtom( *this, NULL, "free" );
594         ASSERT( freeAtom );
595         freeAtom->SetSize( size );
596         root->AddChildAtom( freeAtom );
597         freeAtom->Write();
598     }
599 }
600 
UpdateDuration(MP4Duration duration)601 void MP4File::UpdateDuration(MP4Duration duration)
602 {
603     MP4Duration currentDuration = GetDuration();
604     if (duration > currentDuration) {
605         SetDuration(duration);
606     }
607 }
608 
Dump(bool dumpImplicits)609 void MP4File::Dump( bool dumpImplicits )
610 {
611     log.dump(0, MP4_LOG_VERBOSE1, "\"%s\": Dumping meta-information...", m_file->name.c_str() );
612     m_pRootAtom->Dump( 0, dumpImplicits);
613 }
614 
Close(uint32_t options)615 void MP4File::Close(uint32_t options)
616 {
617     if( IsWriteMode() ) {
618         SetIntegerProperty( "moov.mvhd.modificationTime", MP4GetAbsTimestamp() );
619         FinishWrite(options);
620     }
621 
622     delete m_file;
623     m_file = NULL;
624 }
625 
Rename(const char * oldFileName,const char * newFileName)626 void MP4File::Rename(const char* oldFileName, const char* newFileName)
627 {
628     if( FileSystem::rename( oldFileName, newFileName ))
629         throw new PlatformException( sys::getLastErrorStr(), sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ );
630 }
631 
ProtectWriteOperation(const char * file,int line,const char * func)632 void MP4File::ProtectWriteOperation(const char* file,
633                                     int         line,
634                                     const char* func )
635 {
636     if( !IsWriteMode() )
637         throw new Exception( "operation not permitted in read mode", file, line, func );
638 }
639 
GetTrack(MP4TrackId trackId)640 MP4Track* MP4File::GetTrack(MP4TrackId trackId)
641 {
642     return m_pTracks[FindTrackIndex(trackId)];
643 }
644 
FindAtom(const char * name)645 MP4Atom* MP4File::FindAtom(const char* name)
646 {
647     MP4Atom* pAtom = NULL;
648     if (!name || !strcmp(name, "")) {
649         pAtom = m_pRootAtom;
650     } else {
651         pAtom = m_pRootAtom->FindAtom(name);
652     }
653     return pAtom;
654 }
655 
AddChildAtom(const char * parentName,const char * childName)656 MP4Atom* MP4File::AddChildAtom(
657     const char* parentName,
658     const char* childName)
659 {
660     return AddChildAtom(FindAtom(parentName), childName);
661 }
662 
AddChildAtom(MP4Atom * pParentAtom,const char * childName)663 MP4Atom* MP4File::AddChildAtom(
664     MP4Atom* pParentAtom,
665     const char* childName)
666 {
667     return InsertChildAtom(pParentAtom, childName,
668                            pParentAtom->GetNumberOfChildAtoms());
669 }
670 
InsertChildAtom(const char * parentName,const char * childName,uint32_t index)671 MP4Atom* MP4File::InsertChildAtom(
672     const char* parentName,
673     const char* childName,
674     uint32_t index)
675 {
676     return InsertChildAtom(FindAtom(parentName), childName, index);
677 }
678 
InsertChildAtom(MP4Atom * pParentAtom,const char * childName,uint32_t index)679 MP4Atom* MP4File::InsertChildAtom(
680     MP4Atom* pParentAtom,
681     const char* childName,
682     uint32_t index)
683 {
684     MP4Atom* pChildAtom = MP4Atom::CreateAtom(*this, pParentAtom, childName);
685 
686     ASSERT(pParentAtom);
687     pParentAtom->InsertChildAtom(pChildAtom, index);
688 
689     pChildAtom->Generate();
690 
691     return pChildAtom;
692 }
693 
AddDescendantAtoms(const char * ancestorName,const char * descendantNames)694 MP4Atom* MP4File::AddDescendantAtoms(
695     const char* ancestorName,
696     const char* descendantNames)
697 {
698     return AddDescendantAtoms(FindAtom(ancestorName), descendantNames);
699 }
700 
AddDescendantAtoms(MP4Atom * pAncestorAtom,const char * descendantNames)701 MP4Atom* MP4File::AddDescendantAtoms(
702     MP4Atom* pAncestorAtom, const char* descendantNames)
703 {
704     ASSERT(pAncestorAtom);
705 
706     MP4Atom* pParentAtom = pAncestorAtom;
707     MP4Atom* pChildAtom = NULL;
708 
709     while (true) {
710         char* childName = MP4NameFirst(descendantNames);
711 
712         if (childName == NULL) {
713             break;
714         }
715 
716         descendantNames = MP4NameAfterFirst(descendantNames);
717 
718         pChildAtom = pParentAtom->FindChildAtom(childName);
719 
720         if (pChildAtom == NULL) {
721             pChildAtom = AddChildAtom(pParentAtom, childName);
722         }
723 
724         pParentAtom = pChildAtom;
725 
726         MP4Free(childName);
727     }
728 
729     return pChildAtom;
730 }
731 
FindProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)732 bool MP4File::FindProperty(const char* name,
733                            MP4Property** ppProperty, uint32_t* pIndex)
734 {
735     if( pIndex )
736         *pIndex = 0; // set the default answer for index
737     return m_pRootAtom->FindProperty(name, ppProperty, pIndex);
738 }
739 
FindIntegerProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)740 void MP4File::FindIntegerProperty(const char* name,
741                                   MP4Property** ppProperty, uint32_t* pIndex)
742 {
743     if (!FindProperty(name, ppProperty, pIndex)) {
744         ostringstream msg;
745         msg << "no such property - " << name;
746         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
747     }
748 
749     switch ((*ppProperty)->GetType()) {
750     case Integer8Property:
751     case Integer16Property:
752     case Integer24Property:
753     case Integer32Property:
754     case Integer64Property:
755         break;
756     default:
757         ostringstream msg;
758         msg << "type mismatch - property " << name << " type " << (*ppProperty)->GetType();
759         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
760     }
761 }
762 
GetIntegerProperty(const char * name)763 uint64_t MP4File::GetIntegerProperty(const char* name)
764 {
765     MP4Property* pProperty;
766     uint32_t index;
767 
768     FindIntegerProperty(name, &pProperty, &index);
769 
770     return ((MP4IntegerProperty*)pProperty)->GetValue(index);
771 }
772 
SetIntegerProperty(const char * name,uint64_t value)773 void MP4File::SetIntegerProperty(const char* name, uint64_t value)
774 {
775     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
776 
777     MP4Property* pProperty = NULL;
778     uint32_t index = 0;
779 
780     FindIntegerProperty(name, &pProperty, &index);
781 
782     ((MP4IntegerProperty*)pProperty)->SetValue(value, index);
783 }
784 
FindFloatProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)785 void MP4File::FindFloatProperty(const char* name,
786                                 MP4Property** ppProperty, uint32_t* pIndex)
787 {
788     if (!FindProperty(name, ppProperty, pIndex)) {
789         ostringstream msg;
790         msg << "no such property - " << name;
791         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
792     }
793     if ((*ppProperty)->GetType() != Float32Property) {
794         ostringstream msg;
795         msg << "type mismatch - property " << name << " type " << (*ppProperty)->GetType();
796         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
797     }
798 }
799 
GetFloatProperty(const char * name)800 float MP4File::GetFloatProperty(const char* name)
801 {
802     MP4Property* pProperty;
803     uint32_t index;
804 
805     FindFloatProperty(name, &pProperty, &index);
806 
807     return ((MP4Float32Property*)pProperty)->GetValue(index);
808 }
809 
SetFloatProperty(const char * name,float value)810 void MP4File::SetFloatProperty(const char* name, float value)
811 {
812     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
813 
814     MP4Property* pProperty;
815     uint32_t index;
816 
817     FindFloatProperty(name, &pProperty, &index);
818 
819     ((MP4Float32Property*)pProperty)->SetValue(value, index);
820 }
821 
FindStringProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)822 void MP4File::FindStringProperty(const char* name,
823                                  MP4Property** ppProperty, uint32_t* pIndex)
824 {
825     if (!FindProperty(name, ppProperty, pIndex)) {
826         ostringstream msg;
827         msg << "no such property - " << name;
828         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
829     }
830     if ((*ppProperty)->GetType() != StringProperty) {
831         ostringstream msg;
832         msg << "type mismatch - property " << name << " type " << (*ppProperty)->GetType();
833         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
834     }
835 }
836 
GetStringProperty(const char * name)837 const char* MP4File::GetStringProperty(const char* name)
838 {
839     MP4Property* pProperty;
840     uint32_t index;
841 
842     FindStringProperty(name, &pProperty, &index);
843 
844     return ((MP4StringProperty*)pProperty)->GetValue(index);
845 }
846 
SetStringProperty(const char * name,const char * value)847 void MP4File::SetStringProperty(const char* name, const char* value)
848 {
849     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
850 
851     MP4Property* pProperty;
852     uint32_t index;
853 
854     FindStringProperty(name, &pProperty, &index);
855 
856     ((MP4StringProperty*)pProperty)->SetValue(value, index);
857 }
858 
FindBytesProperty(const char * name,MP4Property ** ppProperty,uint32_t * pIndex)859 void MP4File::FindBytesProperty(const char* name,
860                                 MP4Property** ppProperty, uint32_t* pIndex)
861 {
862     if (!FindProperty(name, ppProperty, pIndex)) {
863         ostringstream msg;
864         msg << "no such property " << name;
865         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
866     }
867     if ((*ppProperty)->GetType() != BytesProperty) {
868         ostringstream msg;
869         msg << "type mismatch - property " << name << " - type " <<  (*ppProperty)->GetType();
870         throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
871     }
872 }
873 
GetBytesProperty(const char * name,uint8_t ** ppValue,uint32_t * pValueSize)874 void MP4File::GetBytesProperty(const char* name,
875                                uint8_t** ppValue, uint32_t* pValueSize)
876 {
877     MP4Property* pProperty;
878     uint32_t index;
879 
880     FindBytesProperty(name, &pProperty, &index);
881 
882     ((MP4BytesProperty*)pProperty)->GetValue(ppValue, pValueSize, index);
883 }
884 
SetBytesProperty(const char * name,const uint8_t * pValue,uint32_t valueSize)885 void MP4File::SetBytesProperty(const char* name,
886                                const uint8_t* pValue, uint32_t valueSize)
887 {
888     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
889 
890     MP4Property* pProperty;
891     uint32_t index;
892 
893     FindBytesProperty(name, &pProperty, &index);
894 
895     ((MP4BytesProperty*)pProperty)->SetValue(pValue, valueSize, index);
896 }
897 
898 
899 // track functions
900 
AddTrack(const char * type,uint32_t timeScale)901 MP4TrackId MP4File::AddTrack(const char* type, uint32_t timeScale)
902 {
903     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
904 
905     // create and add new trak atom
906     MP4Atom* pTrakAtom = AddChildAtom("moov", "trak");
907     ASSERT(pTrakAtom);
908 
909     // allocate a new track id
910     MP4TrackId trackId = AllocTrackId();
911 
912     m_trakIds.Add(trackId);
913 
914     // set track id
915     MP4Integer32Property* pInteger32Property = NULL;
916     (void)pTrakAtom->FindProperty("trak.tkhd.trackId",
917                                   (MP4Property**)&pInteger32Property);
918     ASSERT(pInteger32Property);
919     pInteger32Property->SetValue(trackId);
920 
921     // set track type
922     const char* normType = MP4NormalizeTrackType(type);
923 
924     // sanity check for user defined types
925     if (strlen(normType) > 4) {
926         log.warningf("%s: \"%s\": type truncated to four characters",
927                      __FUNCTION__, GetFilename().c_str());
928         // StringProperty::SetValue() will do the actual truncation
929     }
930 
931     MP4StringProperty* pStringProperty = NULL;
932     (void)pTrakAtom->FindProperty("trak.mdia.hdlr.handlerType",
933                                   (MP4Property**)&pStringProperty);
934     ASSERT(pStringProperty);
935     pStringProperty->SetValue(normType);
936 
937     // set track time scale
938     pInteger32Property = NULL;
939     (void)pTrakAtom->FindProperty("trak.mdia.mdhd.timeScale",
940                                   (MP4Property**)&pInteger32Property);
941     ASSERT(pInteger32Property);
942     pInteger32Property->SetValue(timeScale ? timeScale : 1000);
943 
944     // now have enough to create MP4Track object
945     MP4Track* pTrack = NULL;
946     if (!strcmp(normType, MP4_HINT_TRACK_TYPE)) {
947         pTrack = new MP4RtpHintTrack(*this, *pTrakAtom);
948     } else {
949         pTrack = new MP4Track(*this, *pTrakAtom);
950     }
951     m_pTracks.Add(pTrack);
952 
953     // mark non-hint tracks as enabled
954     if (strcmp(normType, MP4_HINT_TRACK_TYPE)) {
955         SetTrackIntegerProperty(trackId, "tkhd.flags", 1);
956     }
957 
958     // mark track as contained in this file
959     // LATER will provide option for external data references
960     AddDataReference(trackId, NULL);
961 
962     return trackId;
963 }
964 
AddTrackToIod(MP4TrackId trackId)965 void MP4File::AddTrackToIod(MP4TrackId trackId)
966 {
967     MP4DescriptorProperty* pDescriptorProperty = NULL;
968     (void)m_pRootAtom->FindProperty("moov.iods.esIds",
969                                     (MP4Property**)&pDescriptorProperty);
970     ASSERT(pDescriptorProperty);
971 
972     MP4Descriptor* pDescriptor =
973         pDescriptorProperty->AddDescriptor(MP4ESIDIncDescrTag);
974     ASSERT(pDescriptor);
975 
976     MP4Integer32Property* pIdProperty = NULL;
977     (void)pDescriptor->FindProperty("id",
978                                     (MP4Property**)&pIdProperty);
979     ASSERT(pIdProperty);
980 
981     pIdProperty->SetValue(trackId);
982 }
983 
RemoveTrackFromIod(MP4TrackId trackId,bool shallHaveIods)984 void MP4File::RemoveTrackFromIod(MP4TrackId trackId, bool shallHaveIods)
985 {
986     MP4DescriptorProperty* pDescriptorProperty = NULL;
987     if (!m_pRootAtom->FindProperty("moov.iods.esIds",(MP4Property**)&pDescriptorProperty)
988         || pDescriptorProperty == NULL)
989         return;
990 
991     for (uint32_t i = 0; i < pDescriptorProperty->GetCount(); i++) {
992         /* static */
993         char name[32];
994         snprintf(name, sizeof(name), "esIds[%u].id", i);
995 
996         MP4Integer32Property* pIdProperty = NULL;
997         (void)pDescriptorProperty->FindProperty(name,
998                                                 (MP4Property**)&pIdProperty);
999         // wmay ASSERT(pIdProperty);
1000 
1001         if (pIdProperty != NULL &&
1002                 pIdProperty->GetValue() == trackId) {
1003             pDescriptorProperty->DeleteDescriptor(i);
1004             break;
1005         }
1006     }
1007 }
1008 
AddTrackToOd(MP4TrackId trackId)1009 void MP4File::AddTrackToOd(MP4TrackId trackId)
1010 {
1011     if (!m_odTrackId) {
1012         return;
1013     }
1014 
1015     AddTrackReference(MakeTrackName(m_odTrackId, "tref.mpod"), trackId);
1016 }
1017 
RemoveTrackFromOd(MP4TrackId trackId)1018 void MP4File::RemoveTrackFromOd(MP4TrackId trackId)
1019 {
1020     if (!m_odTrackId) {
1021         return;
1022     }
1023 
1024     RemoveTrackReference(MakeTrackName(m_odTrackId, "tref.mpod"), trackId);
1025 }
1026 
1027 /*
1028  * Try to obtain the properties of this reference track, if not found then return
1029  * NULL in *ppCountProperty and *ppTrackIdProperty.
1030  */
GetTrackReferenceProperties(const char * trefName,MP4Property ** ppCountProperty,MP4Property ** ppTrackIdProperty)1031 void MP4File::GetTrackReferenceProperties(const char* trefName,
1032         MP4Property** ppCountProperty, MP4Property** ppTrackIdProperty)
1033 {
1034     char propName[1024];
1035 
1036     snprintf(propName, sizeof(propName), "%s.%s", trefName, "entryCount");
1037     (void)m_pRootAtom->FindProperty(propName, ppCountProperty);
1038 
1039     snprintf(propName, sizeof(propName), "%s.%s", trefName, "entries.trackId");
1040     (void)m_pRootAtom->FindProperty(propName, ppTrackIdProperty);
1041 }
1042 
AddTrackReference(const char * trefName,MP4TrackId refTrackId)1043 void MP4File::AddTrackReference(const char* trefName, MP4TrackId refTrackId)
1044 {
1045     MP4Integer32Property* pCountProperty = NULL;
1046     MP4Integer32Property* pTrackIdProperty = NULL;
1047 
1048     GetTrackReferenceProperties(trefName,
1049                                 (MP4Property**)&pCountProperty,
1050                                 (MP4Property**)&pTrackIdProperty);
1051 
1052     if (pCountProperty && pTrackIdProperty) {
1053         pTrackIdProperty->AddValue(refTrackId);
1054         pCountProperty->IncrementValue();
1055     }
1056 }
1057 
FindTrackReference(const char * trefName,MP4TrackId refTrackId)1058 uint32_t MP4File::FindTrackReference(const char* trefName,
1059                                      MP4TrackId refTrackId)
1060 {
1061     MP4Integer32Property* pCountProperty = NULL;
1062     MP4Integer32Property* pTrackIdProperty = NULL;
1063 
1064     GetTrackReferenceProperties(trefName,
1065                                 (MP4Property**)&pCountProperty,
1066                                 (MP4Property**)&pTrackIdProperty);
1067 
1068     if (pCountProperty && pTrackIdProperty) {
1069         for (uint32_t i = 0; i < pCountProperty->GetValue(); i++) {
1070             if (refTrackId == pTrackIdProperty->GetValue(i)) {
1071                 return i + 1;   // N.B. 1 not 0 based index
1072             }
1073         }
1074     }
1075     return 0;
1076 }
1077 
RemoveTrackReference(const char * trefName,MP4TrackId refTrackId)1078 void MP4File::RemoveTrackReference(const char* trefName, MP4TrackId refTrackId)
1079 {
1080     MP4Integer32Property* pCountProperty = NULL;
1081     MP4Integer32Property* pTrackIdProperty = NULL;
1082 
1083     GetTrackReferenceProperties(trefName,
1084                                 (MP4Property**)&pCountProperty,
1085                                 (MP4Property**)&pTrackIdProperty);
1086 
1087     if (pCountProperty && pTrackIdProperty) {
1088         for (uint32_t i = 0; i < pCountProperty->GetValue(); i++) {
1089             if (refTrackId == pTrackIdProperty->GetValue(i)) {
1090                 pTrackIdProperty->DeleteValue(i);
1091                 pCountProperty->IncrementValue(-1);
1092             }
1093         }
1094     }
1095 }
1096 
AddDataReference(MP4TrackId trackId,const char * url)1097 void MP4File::AddDataReference(MP4TrackId trackId, const char* url)
1098 {
1099     MP4Atom* pDrefAtom =
1100         FindAtom(MakeTrackName(trackId, "mdia.minf.dinf.dref"));
1101     ASSERT(pDrefAtom);
1102 
1103     MP4Integer32Property* pCountProperty = NULL;
1104     (void)pDrefAtom->FindProperty("dref.entryCount",
1105                                   (MP4Property**)&pCountProperty);
1106     ASSERT(pCountProperty);
1107     pCountProperty->IncrementValue();
1108 
1109     MP4Atom* pUrlAtom = AddChildAtom(pDrefAtom, "url ");
1110 
1111     if (url && url[0] != '\0') {
1112         pUrlAtom->SetFlags(pUrlAtom->GetFlags() & 0xFFFFFE);
1113 
1114         MP4StringProperty* pUrlProperty = NULL;
1115         (void)pUrlAtom->FindProperty("url .location",
1116                                      (MP4Property**)&pUrlProperty);
1117         ASSERT(pUrlProperty);
1118         pUrlProperty->SetValue(url);
1119     } else {
1120         pUrlAtom->SetFlags(pUrlAtom->GetFlags() | 1);
1121     }
1122 }
1123 
AddSystemsTrack(const char * type,uint32_t timeScale)1124 MP4TrackId MP4File::AddSystemsTrack(const char* type, uint32_t timeScale)
1125 {
1126     const char* normType = MP4NormalizeTrackType(type);
1127 
1128     // TBD if user type, fix name to four chars, and warn
1129 
1130     MP4TrackId trackId = AddTrack(type, timeScale);
1131 
1132     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
1133 
1134     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "mp4s");
1135 
1136     AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name");
1137 
1138     // stsd is a unique beast in that it has a count of the number
1139     // of child atoms that needs to be incremented after we add the mp4s atom
1140     MP4Integer32Property* pStsdCountProperty;
1141     FindIntegerProperty(
1142         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1143         (MP4Property**)&pStsdCountProperty);
1144     pStsdCountProperty->IncrementValue();
1145 
1146     SetTrackIntegerProperty(trackId,
1147                             "mdia.minf.stbl.stsd.mp4s.esds.ESID",
1148                             0
1149                            );
1150 
1151     SetTrackIntegerProperty(trackId,
1152                             "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId",
1153                             MP4SystemsV1ObjectType);
1154 
1155     SetTrackIntegerProperty(trackId,
1156                             "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.streamType",
1157                             ConvertTrackTypeToStreamType(normType));
1158 
1159     return trackId;
1160 }
1161 
AddODTrack()1162 MP4TrackId MP4File::AddODTrack()
1163 {
1164     // until a demonstrated need emerges
1165     // we limit ourselves to one object description track
1166     if (m_odTrackId != MP4_INVALID_TRACK_ID) {
1167         throw new Exception("object description track already exists",__FILE__, __LINE__, __FUNCTION__);
1168     }
1169 
1170     m_odTrackId = AddSystemsTrack(MP4_OD_TRACK_TYPE);
1171 
1172     AddTrackToIod(m_odTrackId);
1173 
1174     (void)AddDescendantAtoms(MakeTrackName(m_odTrackId, NULL), "tref.mpod");
1175 
1176     return m_odTrackId;
1177 }
1178 
AddSceneTrack()1179 MP4TrackId MP4File::AddSceneTrack()
1180 {
1181     MP4TrackId trackId = AddSystemsTrack(MP4_SCENE_TRACK_TYPE);
1182 
1183     AddTrackToIod(trackId);
1184     AddTrackToOd(trackId);
1185 
1186     return trackId;
1187 }
1188 
ShallHaveIods()1189 bool MP4File::ShallHaveIods()
1190 {
1191     // NULL terminated list of brands which require the IODS atom
1192     const char* brandsWithIods[] = {
1193         "mp42",
1194         "isom",
1195         NULL
1196     };
1197 
1198     MP4FtypAtom* ftyp = (MP4FtypAtom*)m_pRootAtom->FindAtom( "ftyp" );
1199     if( !ftyp )
1200         return false;
1201 
1202     // check major brand
1203     const char* brand = ftyp->majorBrand.GetValue();
1204     for( uint32_t i = 0; brandsWithIods[i] != NULL; i++ ) {
1205         if( !strcasecmp( brandsWithIods[i], brand ))
1206             return true;
1207     }
1208 
1209     // check compatible brands
1210     uint32_t max = ftyp->compatibleBrands.GetCount();
1211     for( uint32_t i = 0; i < max; i++ ) {
1212         brand = ftyp->compatibleBrands.GetValue( i );
1213         for( uint32_t j = 0; brandsWithIods[j] != NULL ; j++) {
1214             if( !strcasecmp( brandsWithIods[j], brand ))
1215                 return true;
1216         }
1217     }
1218 
1219     return false;
1220 }
1221 
SetAmrVendor(MP4TrackId trackId,uint32_t vendor)1222 void MP4File::SetAmrVendor(
1223     MP4TrackId trackId,
1224     uint32_t vendor)
1225 {
1226     SetTrackIntegerProperty(trackId,
1227                             "mdia.minf.stbl.stsd.*.damr.vendor",
1228                             vendor);
1229 }
1230 
SetAmrDecoderVersion(MP4TrackId trackId,uint8_t decoderVersion)1231 void MP4File::SetAmrDecoderVersion(
1232     MP4TrackId trackId,
1233     uint8_t decoderVersion)
1234 {
1235 
1236     SetTrackIntegerProperty(trackId,
1237                             "mdia.minf.stbl.stsd.*.damr.decoderVersion",
1238                             decoderVersion);
1239 }
1240 
SetAmrModeSet(MP4TrackId trackId,uint16_t modeSet)1241 void MP4File::SetAmrModeSet(
1242     MP4TrackId trackId,
1243     uint16_t modeSet)
1244 {
1245     SetTrackIntegerProperty(trackId,
1246                             "mdia.minf.stbl.stsd.*.damr.modeSet",
1247                             modeSet);
1248 }
GetAmrModeSet(MP4TrackId trackId)1249 uint16_t MP4File::GetAmrModeSet(MP4TrackId trackId)
1250 {
1251     return GetTrackIntegerProperty(trackId,
1252                                    "mdia.minf.stbl.stsd.*.damr.modeSet");
1253 }
1254 
AddAmrAudioTrack(uint32_t timeScale,uint16_t modeSet,uint8_t modeChangePeriod,uint8_t framesPerSample,bool isAmrWB)1255 MP4TrackId MP4File::AddAmrAudioTrack(
1256     uint32_t timeScale,
1257     uint16_t modeSet,
1258     uint8_t modeChangePeriod,
1259     uint8_t framesPerSample,
1260     bool isAmrWB)
1261 {
1262 
1263     uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample
1264 
1265     MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
1266 
1267     AddTrackToOd(trackId);
1268 
1269     SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
1270 
1271     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
1272 
1273     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), isAmrWB ? "sawb" : "samr");
1274 
1275     // stsd is a unique beast in that it has a count of the number
1276     // of child atoms that needs to be incremented after we add the mp4a atom
1277     MP4Integer32Property* pStsdCountProperty;
1278     FindIntegerProperty(
1279         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1280         (MP4Property**)&pStsdCountProperty);
1281     pStsdCountProperty->IncrementValue();
1282 
1283     SetTrackIntegerProperty(trackId,
1284                             "mdia.minf.stbl.stsd.*.timeScale",
1285                             timeScale);
1286 
1287     SetTrackIntegerProperty(trackId,
1288                             "mdia.minf.stbl.stsd.*.damr.modeSet",
1289                             modeSet);
1290 
1291     SetTrackIntegerProperty(trackId,
1292                             "mdia.minf.stbl.stsd.*.damr.modeChangePeriod",
1293                             modeChangePeriod);
1294 
1295     SetTrackIntegerProperty(trackId,
1296                             "mdia.minf.stbl.stsd.*.damr.framesPerSample",
1297                             framesPerSample);
1298 
1299 
1300     m_pTracks[FindTrackIndex(trackId)]->
1301     SetFixedSampleDuration(fixedSampleDuration);
1302 
1303     return trackId;
1304 }
1305 
AddULawAudioTrack(uint32_t timeScale)1306 MP4TrackId MP4File::AddULawAudioTrack(    uint32_t timeScale)
1307 {
1308     uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample
1309 
1310     MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
1311 
1312     AddTrackToOd(trackId);
1313 
1314     SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
1315 
1316     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
1317 
1318     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "ulaw");
1319 
1320     // stsd is a unique beast in that it has a count of the number
1321     // of child atoms that needs to be incremented after we add the mp4a atom
1322     MP4Integer32Property* pStsdCountProperty;
1323     FindIntegerProperty(
1324         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1325         (MP4Property**)&pStsdCountProperty);
1326     pStsdCountProperty->IncrementValue();
1327 
1328     SetTrackIntegerProperty(trackId,
1329                             "mdia.minf.stbl.stsd.ulaw.timeScale",
1330                             timeScale<<16);
1331 
1332     m_pTracks[FindTrackIndex(trackId)]->SetFixedSampleDuration(fixedSampleDuration);
1333 
1334     return trackId;
1335 }
1336 
AddALawAudioTrack(uint32_t timeScale)1337 MP4TrackId MP4File::AddALawAudioTrack(    uint32_t timeScale)
1338 {
1339     uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample
1340 
1341     MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
1342 
1343     AddTrackToOd(trackId);
1344 
1345     SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
1346 
1347     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
1348 
1349     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "alaw");
1350 
1351     // stsd is a unique beast in that it has a count of the number
1352     // of child atoms that needs to be incremented after we add the mp4a atom
1353     MP4Integer32Property* pStsdCountProperty;
1354     FindIntegerProperty(
1355         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1356         (MP4Property**)&pStsdCountProperty);
1357     pStsdCountProperty->IncrementValue();
1358 
1359     SetTrackIntegerProperty(trackId,
1360                             "mdia.minf.stbl.stsd.alaw.timeScale",
1361                             timeScale<<16);
1362 
1363     m_pTracks[FindTrackIndex(trackId)]->SetFixedSampleDuration(fixedSampleDuration);
1364 
1365     return trackId;
1366 }
1367 
AddAudioTrack(uint32_t timeScale,MP4Duration sampleDuration,uint8_t audioType)1368 MP4TrackId MP4File::AddAudioTrack(
1369     uint32_t timeScale,
1370     MP4Duration sampleDuration,
1371     uint8_t audioType)
1372 {
1373     MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
1374 
1375     AddTrackToOd(trackId);
1376 
1377     SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
1378 
1379     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
1380 
1381     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "mp4a");
1382 
1383     AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name");
1384 
1385     // stsd is a unique beast in that it has a count of the number
1386     // of child atoms that needs to be incremented after we add the mp4a atom
1387     MP4Integer32Property* pStsdCountProperty;
1388     FindIntegerProperty(
1389         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1390         (MP4Property**)&pStsdCountProperty);
1391     pStsdCountProperty->IncrementValue();
1392 
1393     SetTrackIntegerProperty(trackId,
1394                             "mdia.minf.stbl.stsd.mp4a.timeScale", timeScale << 16);
1395 
1396     SetTrackIntegerProperty(trackId,
1397                             "mdia.minf.stbl.stsd.mp4a.esds.ESID",
1398                             0
1399                            );
1400 
1401     SetTrackIntegerProperty(trackId,
1402                             "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.objectTypeId",
1403                             audioType);
1404 
1405     SetTrackIntegerProperty(trackId,
1406                             "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.streamType",
1407                             MP4AudioStreamType);
1408 
1409     m_pTracks[FindTrackIndex(trackId)]->
1410     SetFixedSampleDuration(sampleDuration);
1411 
1412     return trackId;
1413 }
1414 
AddAC3AudioTrack(uint32_t samplingRate,uint8_t fscod,uint8_t bsid,uint8_t bsmod,uint8_t acmod,uint8_t lfeon,uint8_t bit_rate_code)1415 MP4TrackId MP4File::AddAC3AudioTrack(
1416     uint32_t samplingRate,
1417     uint8_t fscod,
1418     uint8_t bsid,
1419     uint8_t bsmod,
1420     uint8_t acmod,
1421     uint8_t lfeon,
1422     uint8_t bit_rate_code)
1423 {
1424     MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, samplingRate);
1425 
1426     AddTrackToOd(trackId);
1427 
1428     SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
1429 
1430     InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
1431 
1432     AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "ac-3");
1433 
1434     // Set Ac3 settings
1435     MP4Integer16Property* pSampleRateProperty = NULL;
1436     FindIntegerProperty(
1437         MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.samplingRate"),
1438         (MP4Property**)&pSampleRateProperty);
1439     if (pSampleRateProperty) {
1440         pSampleRateProperty->SetValue(samplingRate);
1441     } else {
1442         throw new Exception("no ac-3.samplingRate property", __FILE__, __LINE__, __FUNCTION__);
1443     }
1444 
1445     MP4BitfieldProperty* pBitfieldProperty = NULL;
1446 
1447     FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.fscod"),
1448                                (MP4Property**)&pBitfieldProperty);
1449     if (pBitfieldProperty) {
1450         pBitfieldProperty->SetValue(fscod);
1451         pBitfieldProperty = NULL;
1452     } else {
1453         throw new Exception("no dac3.fscod property", __FILE__, __LINE__, __FUNCTION__);
1454     }
1455 
1456     FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.bsid"),
1457                                (MP4Property**)&pBitfieldProperty);
1458     if (pBitfieldProperty) {
1459         pBitfieldProperty->SetValue(bsid);
1460         pBitfieldProperty = NULL;
1461     } else {
1462         throw new Exception("no dac3.bsid property", __FILE__, __LINE__, __FUNCTION__);
1463     }
1464 
1465     FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.bsmod"),
1466                                (MP4Property**)&pBitfieldProperty);
1467     if (pBitfieldProperty) {
1468         pBitfieldProperty->SetValue(bsmod);
1469         pBitfieldProperty = NULL;
1470     } else {
1471         throw new Exception("no dac3.bsmod property", __FILE__, __LINE__, __FUNCTION__);
1472     }
1473 
1474     FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.acmod"),
1475                                (MP4Property**)&pBitfieldProperty);
1476     if (pBitfieldProperty) {
1477         pBitfieldProperty->SetValue(acmod);
1478         pBitfieldProperty = NULL;
1479     } else {
1480         throw new Exception("no dac3.acmod property", __FILE__, __LINE__, __FUNCTION__);
1481     }
1482 
1483     FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.lfeon"),
1484                                (MP4Property**)&pBitfieldProperty);
1485     if (pBitfieldProperty) {
1486         pBitfieldProperty->SetValue(lfeon);
1487         pBitfieldProperty = NULL;
1488     } else {
1489         throw new Exception("no dac3.lfeon property", __FILE__, __LINE__, __FUNCTION__);
1490     }
1491 
1492     FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.bit_rate_code"),
1493                                (MP4Property**)&pBitfieldProperty);
1494     if (pBitfieldProperty) {
1495         pBitfieldProperty->SetValue(bit_rate_code);
1496         pBitfieldProperty = NULL;
1497     } else {
1498         throw new Exception("no dac3.bit_rate_code property", __FILE__, __LINE__, __FUNCTION__);
1499     }
1500 
1501     AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name");
1502 
1503     // stsd is a unique beast in that it has a count of the number
1504     // of child atoms that needs to be incremented after we add the mp4a atom
1505     MP4Integer32Property* pStsdCountProperty;
1506     FindIntegerProperty(
1507         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1508         (MP4Property**)&pStsdCountProperty);
1509     pStsdCountProperty->IncrementValue();
1510 
1511     m_pTracks[FindTrackIndex(trackId)]->
1512         SetFixedSampleDuration(1536);
1513 
1514     return trackId;
1515 }
1516 
AddEncAudioTrack(uint32_t timeScale,MP4Duration sampleDuration,uint8_t audioType,uint32_t scheme_type,uint16_t scheme_version,uint8_t key_ind_len,uint8_t iv_len,bool selective_enc,const char * kms_uri,bool use_ismacryp)1517 MP4TrackId MP4File::AddEncAudioTrack(uint32_t timeScale,
1518                                      MP4Duration sampleDuration,
1519                                      uint8_t audioType,
1520                                      uint32_t scheme_type,
1521                                      uint16_t scheme_version,
1522                                      uint8_t  key_ind_len,
1523                                      uint8_t  iv_len,
1524                                      bool      selective_enc,
1525                                      const char *kms_uri,
1526                                      bool use_ismacryp
1527                                     )
1528 {
1529     uint32_t original_fmt = 0;
1530 
1531     MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
1532 
1533     AddTrackToOd(trackId);
1534 
1535     SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
1536 
1537     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
1538 
1539     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "enca");
1540 
1541     // stsd is a unique beast in that it has a count of the number
1542     // of child atoms that needs to be incremented after we add the enca atom
1543     MP4Integer32Property* pStsdCountProperty;
1544     FindIntegerProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1545                         (MP4Property**)&pStsdCountProperty);
1546     pStsdCountProperty->IncrementValue();
1547 
1548 
1549     /* set all the ismacryp-specific values */
1550     // original format is mp4a
1551     if (use_ismacryp) {
1552         original_fmt = ATOMID("mp4a");
1553         SetTrackIntegerProperty(trackId,
1554                                 "mdia.minf.stbl.stsd.enca.sinf.frma.data-format",
1555                                 original_fmt);
1556 
1557         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf"),
1558                            "schm");
1559         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf"),
1560                            "schi");
1561         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf.schi"),
1562                            "iKMS");
1563         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf.schi"),
1564                            "iSFM");
1565         SetTrackIntegerProperty(trackId,
1566                                 "mdia.minf.stbl.stsd.enca.sinf.schm.scheme_type",
1567                                 scheme_type);
1568 
1569         SetTrackIntegerProperty(trackId,
1570                                 "mdia.minf.stbl.stsd.enca.sinf.schm.scheme_version",
1571                                 scheme_version);
1572 
1573         SetTrackStringProperty(trackId,
1574                                "mdia.minf.stbl.stsd.enca.sinf.schi.iKMS.kms_URI",
1575                                kms_uri);
1576 
1577         SetTrackIntegerProperty(trackId,
1578                                 "mdia.minf.stbl.stsd.enca.sinf.schi.iSFM.selective-encryption",
1579                                 selective_enc);
1580 
1581         SetTrackIntegerProperty(trackId,
1582                                 "mdia.minf.stbl.stsd.enca.sinf.schi.iSFM.key-indicator-length",
1583                                 key_ind_len);
1584 
1585         SetTrackIntegerProperty(trackId,
1586                                 "mdia.minf.stbl.stsd.enca.sinf.schi.iSFM.IV-length",
1587                                 iv_len);
1588         /* end ismacryp */
1589     }
1590 
1591     SetTrackIntegerProperty(trackId,
1592                             "mdia.minf.stbl.stsd.enca.timeScale", timeScale);
1593 
1594     SetTrackIntegerProperty(trackId,
1595                             "mdia.minf.stbl.stsd.enca.esds.ESID",
1596                             0
1597                            );
1598 
1599     SetTrackIntegerProperty(trackId,
1600                             "mdia.minf.stbl.stsd.enca.esds.decConfigDescr.objectTypeId",
1601                             audioType);
1602 
1603     SetTrackIntegerProperty(trackId,
1604                             "mdia.minf.stbl.stsd.enca.esds.decConfigDescr.streamType",
1605                             MP4AudioStreamType);
1606 
1607     m_pTracks[FindTrackIndex(trackId)]->
1608     SetFixedSampleDuration(sampleDuration);
1609 
1610     return trackId;
1611 }
1612 
AddCntlTrackDefault(uint32_t timeScale,MP4Duration sampleDuration,const char * type)1613 MP4TrackId MP4File::AddCntlTrackDefault (uint32_t timeScale,
1614         MP4Duration sampleDuration,
1615         const char *type)
1616 {
1617     MP4TrackId trackId = AddTrack(MP4_CNTL_TRACK_TYPE, timeScale);
1618 
1619     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
1620     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), type);
1621 
1622     // stsd is a unique beast in that it has a count of the number
1623     // of child atoms that needs to be incremented after we add the mp4v atom
1624     MP4Integer32Property* pStsdCountProperty;
1625     FindIntegerProperty(
1626         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1627         (MP4Property**)&pStsdCountProperty);
1628     pStsdCountProperty->IncrementValue();
1629 
1630     SetTrackIntegerProperty(trackId,
1631                             "mdia.minf.stbl.stsz.sampleSize", sampleDuration);
1632 
1633     m_pTracks[FindTrackIndex(trackId)]->
1634     SetFixedSampleDuration(sampleDuration);
1635 
1636     return trackId;
1637 }
1638 
AddHrefTrack(uint32_t timeScale,MP4Duration sampleDuration,const char * base_url)1639 MP4TrackId MP4File::AddHrefTrack (uint32_t timeScale,
1640                                   MP4Duration sampleDuration,
1641                                   const char *base_url)
1642 {
1643     MP4TrackId trackId = AddCntlTrackDefault(timeScale, sampleDuration, "href");
1644 
1645     if (base_url != NULL) {
1646         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.href"),
1647                            "burl");
1648         SetTrackStringProperty(trackId, "mdia.minf.stbl.stsd.href.burl.base_url",
1649                                base_url);
1650     }
1651 
1652     return trackId;
1653 }
1654 
AddVideoTrackDefault(uint32_t timeScale,MP4Duration sampleDuration,uint16_t width,uint16_t height,const char * videoType)1655 MP4TrackId MP4File::AddVideoTrackDefault(
1656     uint32_t timeScale,
1657     MP4Duration sampleDuration,
1658     uint16_t width,
1659     uint16_t height,
1660     const char *videoType)
1661 {
1662     MP4TrackId trackId = AddTrack(MP4_VIDEO_TRACK_TYPE, timeScale);
1663 
1664     AddTrackToOd(trackId);
1665 
1666     SetTrackFloatProperty(trackId, "tkhd.width", width);
1667     SetTrackFloatProperty(trackId, "tkhd.height", height);
1668 
1669     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "vmhd", 0);
1670 
1671     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), videoType);
1672 
1673     // stsd is a unique beast in that it has a count of the number
1674     // of child atoms that needs to be incremented after we add the mp4v atom
1675     MP4Integer32Property* pStsdCountProperty;
1676     FindIntegerProperty(
1677         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
1678         (MP4Property**)&pStsdCountProperty);
1679     pStsdCountProperty->IncrementValue();
1680 
1681     SetTrackIntegerProperty(trackId,
1682                             "mdia.minf.stbl.stsz.sampleSize", sampleDuration);
1683 
1684     m_pTracks[FindTrackIndex(trackId)]->
1685     SetFixedSampleDuration(sampleDuration);
1686 
1687     return trackId;
1688 }
AddMP4VideoTrack(uint32_t timeScale,MP4Duration sampleDuration,uint16_t width,uint16_t height,uint8_t videoType)1689 MP4TrackId MP4File::AddMP4VideoTrack(
1690     uint32_t timeScale,
1691     MP4Duration sampleDuration,
1692     uint16_t width,
1693     uint16_t height,
1694     uint8_t videoType)
1695 {
1696     MP4TrackId trackId = AddVideoTrackDefault(timeScale,
1697                          sampleDuration,
1698                          width,
1699                          height,
1700                          "mp4v");
1701 
1702     SetTrackIntegerProperty(trackId,
1703                             "mdia.minf.stbl.stsd.mp4v.width", width);
1704     SetTrackIntegerProperty(trackId,
1705                             "mdia.minf.stbl.stsd.mp4v.height", height);
1706 
1707     SetTrackIntegerProperty(trackId,
1708                             "mdia.minf.stbl.stsd.mp4v.esds.ESID",
1709                             0
1710                            );
1711 
1712     SetTrackIntegerProperty(trackId,
1713                             "mdia.minf.stbl.stsd.mp4v.esds.decConfigDescr.objectTypeId",
1714                             videoType);
1715 
1716     SetTrackIntegerProperty(trackId,
1717                             "mdia.minf.stbl.stsd.mp4v.esds.decConfigDescr.streamType",
1718                             MP4VisualStreamType);
1719 
1720     return trackId;
1721 }
1722 
1723 // ismacrypted
AddEncVideoTrack(uint32_t timeScale,MP4Duration sampleDuration,uint16_t width,uint16_t height,uint8_t videoType,mp4v2_ismacrypParams * icPp,const char * oFormat)1724 MP4TrackId MP4File::AddEncVideoTrack(uint32_t timeScale,
1725                                      MP4Duration sampleDuration,
1726                                      uint16_t width,
1727                                      uint16_t height,
1728                                      uint8_t videoType,
1729                                      mp4v2_ismacrypParams *icPp,
1730                                      const char *oFormat
1731                                     )
1732 {
1733     uint32_t original_fmt = 0;
1734 
1735     MP4TrackId trackId = AddVideoTrackDefault(timeScale,
1736                          sampleDuration,
1737                          width,
1738                          height,
1739                          "encv");
1740 
1741     SetTrackIntegerProperty(trackId,
1742                             "mdia.minf.stbl.stsd.encv.width", width);
1743     SetTrackIntegerProperty(trackId,
1744                             "mdia.minf.stbl.stsd.encv.height", height);
1745 
1746     /* set all the ismacryp-specific values */
1747 
1748     original_fmt = ATOMID(oFormat);
1749 
1750     SetTrackIntegerProperty(trackId,
1751                             "mdia.minf.stbl.stsd.encv.sinf.frma.data-format",
1752                             original_fmt);
1753 
1754     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"),
1755                        "schm");
1756     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"),
1757                        "schi");
1758     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"),
1759                        "iKMS");
1760     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"),
1761                        "iSFM");
1762 
1763     SetTrackIntegerProperty(trackId,
1764                             "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_type",
1765                             icPp->scheme_type);
1766 
1767     SetTrackIntegerProperty(trackId,
1768                             "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_version",
1769                             icPp->scheme_version);
1770 
1771     SetTrackStringProperty(trackId,
1772                            "mdia.minf.stbl.stsd.encv.sinf.schi.iKMS.kms_URI",
1773                            icPp->kms_uri);
1774 
1775     SetTrackIntegerProperty(trackId,
1776                             "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.selective-encryption",
1777                             icPp->selective_enc);
1778 
1779     SetTrackIntegerProperty(trackId,
1780                             "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.key-indicator-length",
1781                             icPp->key_ind_len);
1782 
1783     SetTrackIntegerProperty(trackId,
1784                             "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.IV-length",
1785                             icPp->iv_len);
1786 
1787     SetTrackIntegerProperty(trackId,
1788                             "mdia.minf.stbl.stsd.encv.esds.ESID",
1789                             0
1790                            );
1791 
1792     SetTrackIntegerProperty(trackId,
1793                             "mdia.minf.stbl.stsd.encv.esds.decConfigDescr.objectTypeId",
1794                             videoType);
1795 
1796     SetTrackIntegerProperty(trackId,
1797                             "mdia.minf.stbl.stsd.encv.esds.decConfigDescr.streamType",
1798                             MP4VisualStreamType);
1799 
1800     return trackId;
1801 }
1802 
AddH264VideoTrack(uint32_t timeScale,MP4Duration sampleDuration,uint16_t width,uint16_t height,uint8_t AVCProfileIndication,uint8_t profile_compat,uint8_t AVCLevelIndication,uint8_t sampleLenFieldSizeMinusOne)1803 MP4TrackId MP4File::AddH264VideoTrack(
1804     uint32_t timeScale,
1805     MP4Duration sampleDuration,
1806     uint16_t width,
1807     uint16_t height,
1808     uint8_t AVCProfileIndication,
1809     uint8_t profile_compat,
1810     uint8_t AVCLevelIndication,
1811     uint8_t sampleLenFieldSizeMinusOne)
1812 {
1813     MP4TrackId trackId = AddVideoTrackDefault(timeScale,
1814                          sampleDuration,
1815                          width,
1816                          height,
1817                          "avc1");
1818 
1819     SetTrackIntegerProperty(trackId,
1820                             "mdia.minf.stbl.stsd.avc1.width", width);
1821     SetTrackIntegerProperty(trackId,
1822                             "mdia.minf.stbl.stsd.avc1.height", height);
1823 
1824     SetTrackIntegerProperty(trackId,
1825                             "mdia.minf.stbl.stsd.avc1.avcC.AVCProfileIndication",
1826                             AVCProfileIndication);
1827     SetTrackIntegerProperty(trackId,
1828                             "mdia.minf.stbl.stsd.avc1.avcC.profile_compatibility",
1829                             profile_compat);
1830     SetTrackIntegerProperty(trackId,
1831                             "mdia.minf.stbl.stsd.avc1.avcC.AVCLevelIndication",
1832                             AVCLevelIndication);
1833     SetTrackIntegerProperty(trackId,
1834                             "mdia.minf.stbl.stsd.avc1.avcC.lengthSizeMinusOne",
1835                             sampleLenFieldSizeMinusOne);
1836 
1837     return trackId;
1838 }
1839 
AddEncH264VideoTrack(uint32_t timeScale,MP4Duration sampleDuration,uint16_t width,uint16_t height,MP4Atom * srcAtom,mp4v2_ismacrypParams * icPp)1840 MP4TrackId MP4File::AddEncH264VideoTrack(
1841     uint32_t timeScale,
1842     MP4Duration sampleDuration,
1843     uint16_t width,
1844     uint16_t height,
1845     MP4Atom *srcAtom,
1846     mp4v2_ismacrypParams *icPp)
1847 
1848 {
1849 
1850     uint32_t original_fmt = 0;
1851     MP4Atom *avcCAtom;
1852 
1853     MP4TrackId trackId = AddVideoTrackDefault(timeScale,
1854                          sampleDuration,
1855                          width,
1856                          height,
1857                          "encv");
1858 
1859     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.width", width);
1860     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.height", height);
1861 
1862     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv"), "avcC");
1863 
1864     // create default values
1865     avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.avcC"));
1866 
1867     // export source atom
1868     ((MP4AvcCAtom *) srcAtom)->Clone((MP4AvcCAtom *)avcCAtom);
1869 
1870     /* set all the ismacryp-specific values */
1871 
1872     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"), "schm");
1873     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"), "schi");
1874     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"), "iKMS");
1875     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"), "iSFM");
1876 
1877     // per ismacrypt E&A V1.1 section 9.1.2.1 'avc1' is renamed '264b'
1878     // avc1 must not appear as a sample entry name or original format name
1879     original_fmt = ATOMID("264b");
1880     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.frma.data-format",
1881                             original_fmt);
1882 
1883     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_type",
1884                             icPp->scheme_type);
1885 
1886     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_version",
1887                             icPp->scheme_version);
1888 
1889     SetTrackStringProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iKMS.kms_URI",
1890                            icPp->kms_uri);
1891 
1892     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.selective-encryption",
1893                             icPp->selective_enc);
1894 
1895     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.key-indicator-length",
1896                             icPp->key_ind_len);
1897 
1898     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.IV-length",
1899                             icPp->iv_len);
1900 
1901 
1902     return trackId;
1903 }
1904 
1905 
AddH264SequenceParameterSet(MP4TrackId trackId,const uint8_t * pSequence,uint16_t sequenceLen)1906 void MP4File::AddH264SequenceParameterSet (MP4TrackId trackId,
1907         const uint8_t *pSequence,
1908         uint16_t sequenceLen)
1909 {
1910     const char *format;
1911     MP4Atom *avcCAtom;
1912 
1913     // get 4cc media format - can be avc1 or encv for ismacrypted track
1914     format = GetTrackMediaDataName(trackId);
1915 
1916     if (!strcasecmp(format, "avc1"))
1917         avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1.avcC"));
1918     else if (!strcasecmp(format, "encv"))
1919         avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.avcC"));
1920     else
1921         // huh?  unknown track format
1922         return;
1923 
1924 
1925     MP4BitfieldProperty *pCount;
1926     MP4Integer16Property *pLength;
1927     MP4BytesProperty *pUnit;
1928     if ((avcCAtom->FindProperty("avcC.numOfSequenceParameterSets",
1929                                 (MP4Property **)&pCount) == false) ||
1930             (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetLength",
1931                                     (MP4Property **)&pLength) == false) ||
1932             (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetNALUnit",
1933                                     (MP4Property **)&pUnit) == false)) {
1934         log.errorf("%s: \"%s\": Could not find avcC properties",
1935                    __FUNCTION__, GetFilename().c_str() );
1936         return;
1937     }
1938     uint32_t count = pCount->GetValue();
1939 
1940     if (count > 0) {
1941         // see if we already exist
1942         for (uint32_t index = 0; index < count; index++) {
1943             if (pLength->GetValue(index) == sequenceLen) {
1944                 uint8_t *seq;
1945                 uint32_t seqlen;
1946                 pUnit->GetValue(&seq, &seqlen, index);
1947                 if (memcmp(seq, pSequence, sequenceLen) == 0) {
1948                     free(seq);
1949                     return;
1950                 }
1951                 free(seq);
1952             }
1953         }
1954     }
1955     pLength->AddValue(sequenceLen);
1956     pUnit->AddValue(pSequence, sequenceLen);
1957     pCount->IncrementValue();
1958 
1959     return;
1960 }
AddH264PictureParameterSet(MP4TrackId trackId,const uint8_t * pPict,uint16_t pictLen)1961 void MP4File::AddH264PictureParameterSet (MP4TrackId trackId,
1962         const uint8_t *pPict,
1963         uint16_t pictLen)
1964 {
1965     MP4Atom *avcCAtom =
1966         FindAtom(MakeTrackName(trackId,
1967                                "mdia.minf.stbl.stsd.avc1.avcC"));
1968     MP4Integer8Property *pCount;
1969     MP4Integer16Property *pLength;
1970     MP4BytesProperty *pUnit;
1971     if ((avcCAtom->FindProperty("avcC.numOfPictureParameterSets",
1972                                 (MP4Property **)&pCount) == false) ||
1973             (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetLength",
1974                                     (MP4Property **)&pLength) == false) ||
1975             (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetNALUnit",
1976                                     (MP4Property **)&pUnit) == false)) {
1977         log.errorf("%s: \"%s\": Could not find avcC picture table properties",
1978                    __FUNCTION__, GetFilename().c_str());
1979         return;
1980     }
1981 
1982     ASSERT(pCount);
1983     uint32_t count = pCount->GetValue();
1984 
1985     if (count > 0) {
1986         // see if we already exist
1987         for (uint32_t index = 0; index < count; index++) {
1988             if (pLength->GetValue(index) == pictLen) {
1989                 uint8_t *seq;
1990                 uint32_t seqlen;
1991                 pUnit->GetValue(&seq, &seqlen, index);
1992                 if (memcmp(seq, pPict, pictLen) == 0) {
1993                     log.verbose1f("\"%s\": picture matches %d",
1994                                   GetFilename().c_str(), index);
1995                     free(seq);
1996                     return;
1997                 }
1998                 free(seq);
1999             }
2000         }
2001     }
2002     pLength->AddValue(pictLen);
2003     pUnit->AddValue(pPict, pictLen);
2004     pCount->IncrementValue();
2005     log.verbose1f("\"%s\": new picture added %d", GetFilename().c_str(),
2006                   pCount->GetValue());
2007 
2008     return;
2009 }
SetH263Vendor(MP4TrackId trackId,uint32_t vendor)2010 void  MP4File::SetH263Vendor(
2011     MP4TrackId trackId,
2012     uint32_t vendor)
2013 {
2014     SetTrackIntegerProperty(trackId,
2015                             "mdia.minf.stbl.stsd.s263.d263.vendor",
2016                             vendor);
2017 }
2018 
SetH263DecoderVersion(MP4TrackId trackId,uint8_t decoderVersion)2019 void MP4File::SetH263DecoderVersion(
2020     MP4TrackId trackId,
2021     uint8_t decoderVersion)
2022 {
2023     SetTrackIntegerProperty(trackId,
2024                             "mdia.minf.stbl.stsd.s263.d263.decoderVersion",
2025                             decoderVersion);
2026 }
2027 
SetH263Bitrates(MP4TrackId trackId,uint32_t avgBitrate,uint32_t maxBitrate)2028 void MP4File::SetH263Bitrates(
2029     MP4TrackId trackId,
2030     uint32_t avgBitrate,
2031     uint32_t maxBitrate)
2032 {
2033     SetTrackIntegerProperty(trackId,
2034                             "mdia.minf.stbl.stsd.s263.d263.bitr.avgBitrate",
2035                             avgBitrate);
2036 
2037     SetTrackIntegerProperty(trackId,
2038                             "mdia.minf.stbl.stsd.s263.d263.bitr.maxBitrate",
2039                             maxBitrate);
2040 
2041 }
2042 
AddH263VideoTrack(uint32_t timeScale,MP4Duration sampleDuration,uint16_t width,uint16_t height,uint8_t h263Level,uint8_t h263Profile,uint32_t avgBitrate,uint32_t maxBitrate)2043 MP4TrackId MP4File::AddH263VideoTrack(
2044     uint32_t timeScale,
2045     MP4Duration sampleDuration,
2046     uint16_t width,
2047     uint16_t height,
2048     uint8_t h263Level,
2049     uint8_t h263Profile,
2050     uint32_t avgBitrate,
2051     uint32_t maxBitrate)
2052 
2053 {
2054     MP4TrackId trackId = AddVideoTrackDefault(timeScale,
2055                          sampleDuration,
2056                          width,
2057                          height,
2058                          "s263");
2059 
2060     SetTrackIntegerProperty(trackId,
2061                             "mdia.minf.stbl.stsd.s263.width", width);
2062     SetTrackIntegerProperty(trackId,
2063                             "mdia.minf.stbl.stsd.s263.height", height);
2064 
2065     SetTrackIntegerProperty(trackId,
2066                             "mdia.minf.stbl.stsd.s263.d263.h263Level", h263Level);
2067 
2068     SetTrackIntegerProperty(trackId,
2069                             "mdia.minf.stbl.stsd.s263.d263.h263Profile", h263Profile);
2070 
2071     // Add the bitr atom
2072     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.s263.d263"),
2073                        "bitr");
2074 
2075     SetTrackIntegerProperty(trackId,
2076                             "mdia.minf.stbl.stsd.s263.d263.bitr.avgBitrate", avgBitrate);
2077 
2078     SetTrackIntegerProperty(trackId,
2079                             "mdia.minf.stbl.stsd.s263.d263.bitr.maxBitrate", maxBitrate);
2080 
2081 
2082     SetTrackIntegerProperty(trackId,
2083                             "mdia.minf.stbl.stsz.sampleSize", sampleDuration);
2084 
2085     return trackId;
2086 
2087 }
2088 
AddHintTrack(MP4TrackId refTrackId)2089 MP4TrackId MP4File::AddHintTrack(MP4TrackId refTrackId)
2090 {
2091     // validate reference track id
2092     (void)FindTrackIndex(refTrackId);
2093 
2094     MP4TrackId trackId =
2095         AddTrack(MP4_HINT_TRACK_TYPE, GetTrackTimeScale(refTrackId));
2096 
2097     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "hmhd", 0);
2098 
2099     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "rtp ");
2100 
2101     // stsd is a unique beast in that it has a count of the number
2102     // of child atoms that needs to be incremented after we add the rtp atom
2103     MP4Integer32Property* pStsdCountProperty;
2104     FindIntegerProperty(
2105         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
2106         (MP4Property**)&pStsdCountProperty);
2107     pStsdCountProperty->IncrementValue();
2108 
2109     SetTrackIntegerProperty(trackId,
2110                             "mdia.minf.stbl.stsd.rtp .tims.timeScale",
2111                             GetTrackTimeScale(trackId));
2112 
2113     (void)AddDescendantAtoms(MakeTrackName(trackId, NULL), "tref.hint");
2114 
2115     AddTrackReference(MakeTrackName(trackId, "tref.hint"), refTrackId);
2116 
2117     (void)AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.hnti.sdp ");
2118 
2119     (void)AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.hinf");
2120 
2121     return trackId;
2122 }
2123 
AddTextTrack(MP4TrackId refTrackId)2124 MP4TrackId MP4File::AddTextTrack(MP4TrackId refTrackId)
2125 {
2126     // validate reference track id
2127     (void)FindTrackIndex(refTrackId);
2128 
2129     MP4TrackId trackId =
2130         AddTrack(MP4_TEXT_TRACK_TYPE, GetTrackTimeScale(refTrackId));
2131 
2132     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "gmhd", 0);
2133 
2134     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "text");
2135 
2136     // stsd is a unique beast in that it has a count of the number
2137     // of child atoms that needs to be incremented after we add the text atom
2138     MP4Integer32Property* pStsdCountProperty;
2139     FindIntegerProperty(
2140         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
2141         (MP4Property**)&pStsdCountProperty);
2142     pStsdCountProperty->IncrementValue();
2143 
2144     return trackId;
2145 }
2146 
AddSubtitleTrack(uint32_t timescale,uint16_t width,uint16_t height)2147 MP4TrackId MP4File::AddSubtitleTrack(uint32_t timescale,
2148                                      uint16_t width,
2149                                      uint16_t height)
2150 {
2151     MP4TrackId trackId =
2152         AddTrack(MP4_SUBTITLE_TRACK_TYPE, timescale);
2153 
2154     InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
2155 
2156     AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "tx3g");
2157 
2158     SetTrackFloatProperty(trackId, "tkhd.width", width);
2159     SetTrackFloatProperty(trackId, "tkhd.height", height);
2160 
2161     // Hardcoded crap... add the ftab atom and add one font entry
2162     MP4Atom* pFtabAtom = AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.tx3g"), "ftab");
2163 
2164     ((MP4Integer16Property*)pFtabAtom->GetProperty(0))->IncrementValue();
2165 
2166     MP4Integer16Property* pfontID = (MP4Integer16Property*)((MP4TableProperty*)pFtabAtom->GetProperty(1))->GetProperty(0);
2167     pfontID->AddValue(1);
2168 
2169     MP4StringProperty* pName = (MP4StringProperty*)((MP4TableProperty*)pFtabAtom->GetProperty(1))->GetProperty(1);
2170     pName->AddValue("Arial");
2171 
2172     SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
2173 
2174     // stsd is a unique beast in that it has a count of the number
2175     // of child atoms that needs to be incremented after we add the tx3g atom
2176     MP4Integer32Property* pStsdCountProperty;
2177     FindIntegerProperty(
2178         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
2179         (MP4Property**)&pStsdCountProperty);
2180     pStsdCountProperty->IncrementValue();
2181 
2182     return trackId;
2183 }
2184 
AddSubpicTrack(uint32_t timescale,uint16_t width,uint16_t height)2185 MP4TrackId MP4File::AddSubpicTrack(uint32_t timescale,
2186                                      uint16_t width,
2187                                      uint16_t height)
2188 {
2189     MP4TrackId trackId =
2190         AddTrack(MP4_SUBPIC_TRACK_TYPE, timescale);
2191 
2192     InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
2193 
2194     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "mp4s");
2195 
2196     SetTrackFloatProperty(trackId, "tkhd.width", width);
2197     SetTrackFloatProperty(trackId, "tkhd.height", height);
2198     SetTrackIntegerProperty(trackId, "tkhd.layer", 0);
2199 
2200     // stsd is a unique beast in that it has a count of the number
2201     // of child atoms that needs to be incremented after we add the mp4s atom
2202     MP4Integer32Property* pStsdCountProperty;
2203     FindIntegerProperty(
2204         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
2205         (MP4Property**)&pStsdCountProperty);
2206     pStsdCountProperty->IncrementValue();
2207 
2208     SetTrackIntegerProperty(trackId,
2209                             "mdia.minf.stbl.stsd.mp4s.esds.ESID",
2210                             0
2211                            );
2212 
2213     SetTrackIntegerProperty(trackId,
2214                             "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId",
2215                             MP4SubpicObjectType);
2216 
2217     SetTrackIntegerProperty(trackId,
2218                             "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.streamType",
2219                             MP4NeroSubpicStreamType);
2220     return trackId;
2221 }
2222 
AddChapterTextTrack(MP4TrackId refTrackId,uint32_t timescale)2223 MP4TrackId MP4File::AddChapterTextTrack(MP4TrackId refTrackId, uint32_t timescale)
2224 {
2225     // validate reference track id
2226     (void)FindTrackIndex(refTrackId);
2227 
2228     if (0 == timescale)
2229     {
2230         timescale = GetTrackTimeScale(refTrackId);
2231     }
2232 
2233     MP4TrackId trackId = AddTrack(MP4_TEXT_TRACK_TYPE, timescale);
2234 
2235     (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "gmhd", 0);
2236 
2237     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "text");
2238 
2239     // stsd is a unique beast in that it has a count of the number
2240     // of child atoms that needs to be incremented after we add the text atom
2241     MP4Integer32Property* pStsdCountProperty;
2242     FindIntegerProperty(
2243         MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
2244         (MP4Property**)&pStsdCountProperty);
2245     pStsdCountProperty->IncrementValue();
2246 
2247     // add a "text" atom to the generic media header
2248     // this is different to the stsd "text" atom added above
2249     // truth be told, it's not clear what this second "text" atom does,
2250     // but all iTunes Store movies (with chapter markers) have it,
2251     // as do all movies with chapter tracks made by hand in QuickTime Pro
2252     (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.gmhd"), "text");
2253 
2254     // disable the chapter text track
2255     // it won't display anyway, as it has zero display size,
2256     // but nonetheless it's good to disable it
2257     // the track still operates as a chapter track when disabled
2258     MP4Atom *pTkhdAtom = FindAtom(MakeTrackName(trackId, "tkhd"));
2259     if (pTkhdAtom) {
2260         pTkhdAtom->SetFlags(0xE);
2261     }
2262 
2263     // add a "chapter" track reference to our reference track,
2264     // pointing to this new chapter track
2265     (void)AddDescendantAtoms(MakeTrackName(refTrackId, NULL), "tref.chap");
2266     AddTrackReference(MakeTrackName(refTrackId, "tref.chap"), trackId);
2267 
2268     return trackId;
2269 }
2270 
AddPixelAspectRatio(MP4TrackId trackId,uint32_t hSpacing,uint32_t vSpacing)2271 MP4TrackId MP4File::AddPixelAspectRatio(MP4TrackId trackId, uint32_t hSpacing, uint32_t vSpacing)
2272 {
2273     // validate reference track id
2274     (void)FindTrackIndex(trackId);
2275     const char *format = GetTrackMediaDataName (trackId);
2276 
2277     if (!strcasecmp(format, "avc1"))
2278     {
2279         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1"), "pasp");
2280         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.pasp.hSpacing", hSpacing);
2281         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.pasp.vSpacing", vSpacing);
2282     }
2283     else if (!strcasecmp(format, "mp4v"))
2284     {
2285         (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.mp4v"), "pasp");
2286         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.pasp.hSpacing", hSpacing);
2287         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.pasp.vSpacing", vSpacing);
2288     }
2289 
2290     return trackId;
2291 }
2292 
AddColr(MP4TrackId trackId,uint16_t primariesIndex,uint16_t transferFunctionIndex,uint16_t matrixIndex)2293 MP4TrackId MP4File::AddColr(MP4TrackId trackId,
2294                             uint16_t primariesIndex,
2295                             uint16_t transferFunctionIndex,
2296                             uint16_t matrixIndex)
2297 {
2298     // validate reference track id
2299     (void)FindTrackIndex(trackId);
2300     const char *format = GetTrackMediaDataName (trackId);
2301 
2302     if (!strcasecmp(format, "avc1"))
2303     {
2304         AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1"), "colr");
2305         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.colr.primariesIndex", primariesIndex);
2306         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.colr.transferFunctionIndex", transferFunctionIndex);
2307         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.colr.matrixIndex", matrixIndex);
2308     }
2309     else if (!strcasecmp(format, "mp4v"))
2310     {
2311         AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.mp4v"), "colr");
2312         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.colr.primariesIndex", primariesIndex);
2313         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.colr.transferFunctionIndex", transferFunctionIndex);
2314         SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.colr.matrixIndex", matrixIndex);
2315     }
2316 
2317     return trackId;
2318 }
2319 
2320 
AddChapter(MP4TrackId chapterTrackId,MP4Duration chapterDuration,const char * chapterTitle)2321 void MP4File::AddChapter(MP4TrackId chapterTrackId, MP4Duration chapterDuration, const char *chapterTitle)
2322 {
2323     if (MP4_INVALID_TRACK_ID == chapterTrackId)
2324     {
2325         throw new Exception("No chapter track given",__FILE__, __LINE__, __FUNCTION__);
2326     }
2327 
2328     uint32_t sampleLength = 0;
2329     uint8_t  sample[1040] = {0};
2330     int textLen = 0;
2331     char *text = (char *)&(sample[2]);
2332 
2333     if(chapterTitle != NULL)
2334     {
2335         textLen = min((uint32_t)strlen(chapterTitle), (uint32_t)MP4V2_CHAPTER_TITLE_MAX);
2336         if (0 < textLen)
2337         {
2338             strncpy(text, chapterTitle, textLen);
2339         }
2340     }
2341     else
2342     {
2343         MP4Track * pChapterTrack = GetTrack(chapterTrackId);
2344         snprintf( text, 1023, "Chapter %03d", pChapterTrack->GetNumberOfSamples() + 1 );
2345         textLen = (uint32_t)strlen(text);
2346     }
2347 
2348     sampleLength = textLen + 2 + 12; // Account for text length code and other marker
2349 
2350     // 2-byte length marker
2351     sample[0] = (textLen >> 8) & 0xff;
2352     sample[1] = textLen & 0xff;
2353 
2354     int x = 2 + textLen;
2355 
2356     // Modifier Length Marker
2357     sample[x] = 0x00;
2358     sample[x+1] = 0x00;
2359     sample[x+2] = 0x00;
2360     sample[x+3] = 0x0C;
2361 
2362     // Modifier Type Code
2363     sample[x+4] = 'e';
2364     sample[x+5] = 'n';
2365     sample[x+6] = 'c';
2366     sample[x+7] = 'd';
2367 
2368     // Modifier Value
2369     sample[x+8] = 0x00;
2370     sample[x+9] = 0x00;
2371     sample[x+10] = (256 >> 8) & 0xff;
2372     sample[x+11] = 256 & 0xff;
2373 
2374     WriteSample(chapterTrackId, sample, sampleLength, chapterDuration);
2375 }
2376 
AddNeroChapter(MP4Timestamp chapterStart,const char * chapterTitle)2377 void MP4File::AddNeroChapter(MP4Timestamp chapterStart, const char * chapterTitle)
2378 {
2379     MP4Atom * pChpl = FindAtom("moov.udta.chpl");
2380     if (!pChpl)
2381     {
2382         pChpl = AddDescendantAtoms("", "moov.udta.chpl");
2383     }
2384 
2385     MP4Integer32Property * pCount = (MP4Integer32Property*)pChpl->GetProperty(3);
2386     pCount->IncrementValue();
2387 
2388     char buffer[256];
2389 
2390     if (0 == chapterTitle)
2391     {
2392         snprintf( buffer, 255, "Chapter %03d", pCount->GetValue() );
2393     }
2394     else
2395     {
2396         int len = min((uint32_t)strlen(chapterTitle), (uint32_t)255);
2397         strncpy( buffer, chapterTitle, len );
2398         buffer[len] = 0;
2399     }
2400 
2401     MP4TableProperty * pTable;
2402     if (pChpl->FindProperty("chpl.chapters", (MP4Property **)&pTable))
2403     {
2404         MP4Integer64Property * pStartTime = (MP4Integer64Property *) pTable->GetProperty(0);
2405         MP4StringProperty * pName = (MP4StringProperty *) pTable->GetProperty(1);
2406         if (pStartTime && pTable)
2407         {
2408             pStartTime->AddValue(chapterStart);
2409             pName->AddValue(buffer);
2410         }
2411     }
2412 }
2413 
FindChapterReferenceTrack(MP4TrackId chapterTrackId,char * trackName,int trackNameSize)2414 MP4TrackId MP4File::FindChapterReferenceTrack(MP4TrackId chapterTrackId, char * trackName, int trackNameSize)
2415 {
2416     for (uint32_t i = 0; i < m_pTracks.Size(); i++)
2417     {
2418         if( MP4_IS_VIDEO_TRACK_TYPE( m_pTracks[i]->GetType() ) ||
2419             MP4_IS_AUDIO_TRACK_TYPE( m_pTracks[i]->GetType() ) )
2420         {
2421             MP4TrackId refTrackId = m_pTracks[i]->GetId();
2422             char *name = MakeTrackName(refTrackId, "tref.chap");
2423             if( FindTrackReference( name, chapterTrackId ) )
2424             {
2425                 if( 0 != trackName )
2426                 {
2427                     int nameLen = min((uint32_t)strlen(name), (uint32_t)trackNameSize);
2428                     strncpy(trackName, name, nameLen);
2429                     trackName[nameLen] = 0;
2430                 }
2431 
2432                 return m_pTracks[i]->GetId();
2433             }
2434         }
2435     }
2436 
2437     return MP4_INVALID_TRACK_ID;
2438 }
2439 
FindChapterTrack(char * trackName,int trackNameSize)2440 MP4TrackId MP4File::FindChapterTrack(char * trackName, int trackNameSize)
2441 {
2442     for (uint32_t i = 0; i < m_pTracks.Size(); i++)
2443     {
2444         if( !strcasecmp(MP4_TEXT_TRACK_TYPE, m_pTracks[i]->GetType()) )
2445         {
2446             MP4TrackId refTrackId = FindChapterReferenceTrack(m_pTracks[i]->GetId(), trackName, trackNameSize);
2447             if (MP4_INVALID_TRACK_ID != refTrackId)
2448             {
2449                 return m_pTracks[i]->GetId();
2450             }
2451         }
2452     }
2453 
2454     return MP4_INVALID_TRACK_ID;
2455 }
2456 
DeleteChapters(MP4ChapterType chapterType,MP4TrackId chapterTrackId)2457 MP4ChapterType MP4File::DeleteChapters(MP4ChapterType chapterType, MP4TrackId chapterTrackId)
2458 {
2459     MP4ChapterType deletedType = MP4ChapterTypeNone;
2460 
2461     if (MP4ChapterTypeAny == chapterType || MP4ChapterTypeNero == chapterType)
2462     {
2463         MP4Atom * pChpl = FindAtom("moov.udta.chpl");
2464         if (pChpl)
2465         {
2466             MP4Atom * pParent = pChpl->GetParentAtom();
2467             pParent->DeleteChildAtom(pChpl);
2468             deletedType = MP4ChapterTypeNero;
2469         }
2470     }
2471 
2472     if (MP4ChapterTypeAny == chapterType || MP4ChapterTypeQt == chapterType)
2473     {
2474         char trackName[128] = {0};
2475 
2476         // no text track given, find a suitable
2477         if (MP4_INVALID_TRACK_ID == chapterTrackId)
2478         {
2479             chapterTrackId = FindChapterTrack(trackName, 127);
2480         }
2481 
2482         if (MP4_INVALID_TRACK_ID != chapterTrackId)
2483         {
2484             FindChapterReferenceTrack(chapterTrackId, trackName, 127);
2485         }
2486 
2487         if (MP4_INVALID_TRACK_ID != chapterTrackId && 0 != trackName[0])
2488         {
2489             // remove the reference
2490             MP4Atom * pChap = FindAtom( trackName );
2491             if( pChap )
2492             {
2493                 MP4Atom * pTref = pChap->GetParentAtom();
2494                 if( pTref )
2495                 {
2496                     pTref->DeleteChildAtom( pChap );
2497 
2498                     MP4Atom* pParent = pTref->GetParentAtom();
2499                     pParent->DeleteChildAtom( pTref );
2500                 }
2501             }
2502 
2503             // remove the chapter track
2504             DeleteTrack(chapterTrackId);
2505             deletedType = MP4ChapterTypeNone == deletedType ? MP4ChapterTypeQt : MP4ChapterTypeAny;
2506         }
2507     }
2508     return deletedType;
2509 }
2510 
GetChapters(MP4Chapter_t ** chapterList,uint32_t * chapterCount,MP4ChapterType fromChapterType)2511 MP4ChapterType MP4File::GetChapters(MP4Chapter_t ** chapterList, uint32_t * chapterCount, MP4ChapterType fromChapterType)
2512 {
2513     *chapterList = 0;
2514     *chapterCount = 0;
2515 
2516     if (MP4ChapterTypeAny == fromChapterType || MP4ChapterTypeQt == fromChapterType)
2517     {
2518         uint8_t * sample = 0;
2519         uint32_t sampleSize = 0;
2520         MP4Timestamp startTime = 0;
2521         MP4Duration duration = 0;
2522 
2523         // get the chapter track
2524         MP4TrackId chapterTrackId = FindChapterTrack();
2525         if (MP4_INVALID_TRACK_ID == chapterTrackId)
2526         {
2527             if (MP4ChapterTypeQt == fromChapterType)
2528             {
2529                 return MP4ChapterTypeNone;
2530             }
2531         }
2532         else
2533         {
2534             // get infos about the chapters
2535             MP4Track * pChapterTrack = GetTrack(chapterTrackId);
2536             uint32_t counter = pChapterTrack->GetNumberOfSamples();
2537 
2538             if (0 < counter)
2539             {
2540                 uint32_t timescale = pChapterTrack->GetTimeScale();
2541                 MP4Chapter_t * chapters = (MP4Chapter_t*)MP4Malloc(sizeof(MP4Chapter_t) * counter);
2542 
2543                 // process all chapter sample
2544                 for (uint32_t i = 0; i < counter; ++i)
2545                 {
2546                     // get the sample corresponding to the starttime
2547                     MP4SampleId sampleId = pChapterTrack->GetSampleIdFromTime(startTime + duration, true);
2548                     pChapterTrack->ReadSample(sampleId, &sample, &sampleSize);
2549 
2550                     // get the starttime and duration
2551                     pChapterTrack->GetSampleTimes(sampleId, &startTime, &duration);
2552 
2553                     // we know that sample+2 contains the title (sample[0] and sample[1] is the length)
2554                     const char * title = (const char *)&(sample[2]);
2555                     int titleLen = min((uint32_t)((sample[0] << 8) | sample[1]), (uint32_t)MP4V2_CHAPTER_TITLE_MAX);
2556                     strncpy(chapters[i].title, title, titleLen);
2557                     chapters[i].title[titleLen] = 0;
2558 
2559                     // write the duration (in milliseconds)
2560                     chapters[i].duration = MP4ConvertTime(duration, timescale, MP4_MILLISECONDS_TIME_SCALE);
2561 
2562                     // we're done with this sample
2563                     MP4Free(sample);
2564                     sample = 0;
2565                 }
2566 
2567                 *chapterList = chapters;
2568                 *chapterCount = counter;
2569 
2570                 // we got chapters so we are done
2571                 return MP4ChapterTypeQt;
2572             }
2573         }
2574     }
2575 
2576     if (MP4ChapterTypeAny == fromChapterType || MP4ChapterTypeNero == fromChapterType)
2577     {
2578         MP4Atom * pChpl = FindAtom("moov.udta.chpl");
2579         if (!pChpl)
2580         {
2581             return MP4ChapterTypeNone;
2582         }
2583 
2584         MP4Integer32Property * pCounter = 0;
2585         if (!pChpl->FindProperty("chpl.chaptercount", (MP4Property **)&pCounter))
2586         {
2587             log.warningf("%s: \"%s\": Nero chapter count does not exist",
2588                          __FUNCTION__, GetFilename().c_str());
2589             return MP4ChapterTypeNone;
2590         }
2591 
2592         uint32_t counter = pCounter->GetValue();
2593         if (0 == counter)
2594         {
2595             log.warningf("%s: \"%s\": No Nero chapters available",
2596                          __FUNCTION__, GetFilename().c_str());
2597             return MP4ChapterTypeNone;
2598         }
2599 
2600         MP4TableProperty * pTable = 0;
2601         MP4Integer64Property * pStartTime = 0;
2602         MP4StringProperty * pName = 0;
2603         MP4Duration chapterDurationSum = 0;
2604         const char * name = 0;
2605 
2606         if (!pChpl->FindProperty("chpl.chapters", (MP4Property **)&pTable))
2607         {
2608             log.warningf("%s: \"%s\": Nero chapter list does not exist",
2609                          __FUNCTION__, GetFilename().c_str());
2610             return MP4ChapterTypeNone;
2611         }
2612 
2613         if (0 == (pStartTime = (MP4Integer64Property *) pTable->GetProperty(0)))
2614         {
2615             log.warningf("%s: \"%s\": List of Chapter starttimes does not exist",
2616                          __FUNCTION__, GetFilename().c_str());
2617             return MP4ChapterTypeNone;
2618         }
2619         if (0 == (pName = (MP4StringProperty *) pTable->GetProperty(1)))
2620         {
2621             log.warningf("%s: \"%s\": List of Chapter titles does not exist",
2622                          __FUNCTION__, GetFilename().c_str());
2623             return MP4ChapterTypeNone;
2624         }
2625 
2626         MP4Chapter_t * chapters = (MP4Chapter_t*)MP4Malloc(sizeof(MP4Chapter_t) * counter);
2627 
2628         // get the name of the first chapter
2629         name = pName->GetValue();
2630 
2631         // process remaining chapters
2632         uint32_t i, j;
2633         for (i = 0, j = 1; i < counter; ++i, ++j)
2634         {
2635             // insert the chapter title
2636             uint32_t len = min((uint32_t)strlen(name), (uint32_t)MP4V2_CHAPTER_TITLE_MAX);
2637             strncpy(chapters[i].title, name, len);
2638             chapters[i].title[len] = 0;
2639 
2640             // calculate the duration
2641             MP4Duration duration = 0;
2642             if (j < counter)
2643             {
2644                 duration = MP4ConvertTime(pStartTime->GetValue(j),
2645                                           (MP4_NANOSECONDS_TIME_SCALE / 100),
2646                                           MP4_MILLISECONDS_TIME_SCALE) - chapterDurationSum;
2647 
2648                 // now get the name of the chapter (to be written next)
2649                 name = pName->GetValue(j);
2650             }
2651             else
2652             {
2653                 // last chapter
2654                 duration = MP4ConvertTime(GetDuration(), GetTimeScale(), MP4_MILLISECONDS_TIME_SCALE) - chapterDurationSum;
2655             }
2656 
2657             // sum up the chapter duration
2658             chapterDurationSum += duration;
2659 
2660             // insert the chapter duration
2661             chapters[i].duration = duration;
2662         }
2663 
2664         *chapterList = chapters;
2665         *chapterCount = counter;
2666 
2667         return MP4ChapterTypeNero;
2668     }
2669 
2670     return MP4ChapterTypeNone;
2671 }
2672 
SetChapters(MP4Chapter_t * chapterList,uint32_t chapterCount,MP4ChapterType toChapterType)2673 MP4ChapterType MP4File::SetChapters(MP4Chapter_t * chapterList, uint32_t chapterCount, MP4ChapterType toChapterType)
2674 {
2675     MP4ChapterType setType = MP4ChapterTypeNone;
2676 
2677     // first remove any existing chapters
2678     DeleteChapters(toChapterType, MP4_INVALID_TRACK_ID);
2679 
2680     if( MP4ChapterTypeAny == toChapterType || MP4ChapterTypeNero == toChapterType )
2681     {
2682         MP4Duration duration = 0;
2683         for( uint32_t i = 0; i < chapterCount; ++i )
2684         {
2685             AddNeroChapter(duration, chapterList[i].title);
2686             duration += 10 * MP4_MILLISECONDS_TIME_SCALE * chapterList[i].duration;
2687         }
2688 
2689         setType = MP4ChapterTypeNero;
2690     }
2691 
2692     if (MP4ChapterTypeAny == toChapterType || MP4ChapterTypeQt == toChapterType)
2693     {
2694         // find the first video or audio track
2695         MP4TrackId refTrack = MP4_INVALID_TRACK_ID;
2696         for( uint32_t i = 0; i < m_pTracks.Size(); i++ )
2697         {
2698             if( MP4_IS_VIDEO_TRACK_TYPE( m_pTracks[i]->GetType() ) ||
2699                 MP4_IS_AUDIO_TRACK_TYPE( m_pTracks[i]->GetType() ) )
2700             {
2701                 refTrack = m_pTracks[i]->GetId();
2702                 break;
2703             }
2704         }
2705 
2706         if( refTrack == MP4_INVALID_TRACK_ID )
2707         {
2708             return setType;
2709         }
2710 
2711         // create the chapter track
2712         MP4TrackId chapterTrack = AddChapterTextTrack(refTrack, MP4_MILLISECONDS_TIME_SCALE);
2713 
2714         for( uint32_t i = 0 ; i < chapterCount; ++i )
2715         {
2716             // create and write the chapter track sample
2717             AddChapter( chapterTrack, chapterList[i].duration, chapterList[i].title );
2718         }
2719 
2720         setType = MP4ChapterTypeNone == setType ? MP4ChapterTypeQt : MP4ChapterTypeAny;
2721     }
2722 
2723     return setType;
2724 }
2725 
ConvertChapters(MP4ChapterType toChapterType)2726 MP4ChapterType MP4File::ConvertChapters(MP4ChapterType toChapterType)
2727 {
2728     MP4ChapterType sourceType = MP4ChapterTypeNone;
2729     const char* errMsg = 0;
2730 
2731     if( MP4ChapterTypeQt == toChapterType )
2732     {
2733         sourceType = MP4ChapterTypeNero;
2734         errMsg = "Could not find Nero chapter markers";
2735     }
2736     else if( MP4ChapterTypeNero == toChapterType )
2737     {
2738         sourceType = MP4ChapterTypeQt;
2739         errMsg = "Could not find QuickTime chapter markers";
2740     }
2741     else
2742     {
2743         return MP4ChapterTypeNone;
2744     }
2745 
2746     MP4Chapter_t * chapters = 0;
2747     uint32_t chapterCount = 0;
2748 
2749     GetChapters(&chapters, &chapterCount, sourceType);
2750     if (0 == chapterCount)
2751     {
2752         log.warningf("%s: \"%s\": %s", __FUNCTION__, GetFilename().c_str(),
2753                      errMsg);
2754         return MP4ChapterTypeNone;
2755     }
2756 
2757     SetChapters(chapters, chapterCount, toChapterType);
2758 
2759     MP4Free(chapters);
2760 
2761     return toChapterType;
2762 }
2763 
ChangeMovieTimeScale(uint32_t timescale)2764 void MP4File::ChangeMovieTimeScale(uint32_t timescale)
2765 {
2766     uint32_t origTimeScale = GetTimeScale();
2767     if (timescale == origTimeScale) {
2768         // already done
2769         return;
2770     }
2771 
2772     MP4Duration movieDuration = GetDuration();
2773 
2774     // set movie header timescale and duration
2775     SetTimeScale(timescale);
2776     SetDuration(MP4ConvertTime(movieDuration, origTimeScale, timescale));
2777 
2778     // set track header duration (calculated with movie header timescale)
2779     uint32_t trackCount = GetNumberOfTracks();
2780     for (uint32_t i = 0; i < trackCount; ++i)
2781     {
2782         MP4Track * track = GetTrack(FindTrackId(i));
2783         MP4Atom & trackAtom = track->GetTrakAtom();
2784         MP4IntegerProperty * duration;
2785 
2786         if (trackAtom.FindProperty("trak.tkhd.duration", (MP4Property**)&duration))
2787         {
2788             duration->SetValue(MP4ConvertTime(duration->GetValue(), origTimeScale, timescale));
2789         }
2790     }
2791 }
2792 
DeleteTrack(MP4TrackId trackId)2793 void MP4File::DeleteTrack(MP4TrackId trackId)
2794 {
2795     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
2796 
2797     uint32_t trakIndex = FindTrakAtomIndex(trackId);
2798     uint16_t trackIndex = FindTrackIndex(trackId);
2799     MP4Track* pTrack = m_pTracks[trackIndex];
2800 
2801     MP4Atom& trakAtom = pTrack->GetTrakAtom();
2802 
2803     MP4Atom* pMoovAtom = FindAtom("moov");
2804     ASSERT(pMoovAtom);
2805 
2806     RemoveTrackFromIod(trackId, ShallHaveIods());
2807     RemoveTrackFromOd(trackId);
2808 
2809     if (trackId == m_odTrackId) {
2810         m_odTrackId = 0;
2811     }
2812 
2813     pMoovAtom->DeleteChildAtom(&trakAtom);
2814 
2815     m_trakIds.Delete(trakIndex);
2816 
2817     m_pTracks.Delete(trackIndex);
2818 
2819     delete pTrack;
2820     delete &trakAtom;
2821 }
2822 
GetNumberOfTracks(const char * type,uint8_t subType)2823 uint32_t MP4File::GetNumberOfTracks(const char* type, uint8_t subType)
2824 {
2825     if (type == NULL) {
2826         return m_pTracks.Size();
2827     }
2828 
2829     uint32_t typeSeen = 0;
2830     const char* normType = MP4NormalizeTrackType(type);
2831 
2832     for (uint32_t i = 0; i < m_pTracks.Size(); i++) {
2833         if (!strcmp(normType, m_pTracks[i]->GetType())) {
2834             if (subType) {
2835                 if (strcmp(normType, MP4_AUDIO_TRACK_TYPE) == 0) {
2836                     if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
2837                         continue;
2838                     }
2839                 } else if (strcmp(normType, MP4_VIDEO_TRACK_TYPE)) {
2840                     if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
2841                         continue;
2842                     }
2843                 }
2844                 // else unknown subtype, ignore it
2845             }
2846             typeSeen++;
2847         }
2848     }
2849     return typeSeen;
2850 }
2851 
AllocTrackId()2852 MP4TrackId MP4File::AllocTrackId()
2853 {
2854     MP4TrackId trackId =
2855         GetIntegerProperty("moov.mvhd.nextTrackId");
2856 
2857     if (trackId <= 0xFFFF) {
2858         // check that nextTrackid is correct
2859         try {
2860             (void)FindTrackIndex(trackId);
2861             // ERROR, this trackId is in use
2862         }
2863         catch (Exception* x) {
2864             // OK, this trackId is not in use, proceed
2865             delete x;
2866             SetIntegerProperty("moov.mvhd.nextTrackId", trackId + 1);
2867             return trackId;
2868         }
2869     }
2870 
2871     // we need to search for a track id
2872     for (trackId = 1; trackId <= 0xFFFF; trackId++) {
2873         try {
2874             (void)FindTrackIndex(trackId);
2875             // KEEP LOOKING, this trackId is in use
2876         }
2877         catch (Exception* x) {
2878             // OK, this trackId is not in use, proceed
2879             delete x;
2880             return trackId;
2881         }
2882     }
2883 
2884     // extreme case where mp4 file has 2^16 tracks in it
2885     throw new Exception("too many existing tracks", __FILE__, __LINE__, __FUNCTION__);
2886     return MP4_INVALID_TRACK_ID;        // to keep MSVC happy
2887 }
2888 
FindTrackId(uint16_t trackIndex,const char * type,uint8_t subType)2889 MP4TrackId MP4File::FindTrackId(uint16_t trackIndex,
2890                                 const char* type, uint8_t subType)
2891 {
2892     if (type == NULL) {
2893         return m_pTracks[trackIndex]->GetId();
2894     }
2895 
2896     uint32_t typeSeen = 0;
2897     const char* normType = MP4NormalizeTrackType(type);
2898 
2899     for (uint32_t i = 0; i < m_pTracks.Size(); i++) {
2900         if (!strcmp(normType, m_pTracks[i]->GetType())) {
2901             if (subType) {
2902                 if (strcmp(normType, MP4_AUDIO_TRACK_TYPE) == 0) {
2903                     if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
2904                         continue;
2905                     }
2906                 } else if (strcmp(normType, MP4_VIDEO_TRACK_TYPE) == 0) {
2907                     if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
2908                         continue;
2909                     }
2910                 }
2911                 // else unknown subtype, ignore it
2912             }
2913 
2914             if (trackIndex == typeSeen) {
2915                 return m_pTracks[i]->GetId();
2916             }
2917 
2918             typeSeen++;
2919         }
2920     }
2921 
2922     ostringstream msg;
2923     msg << "Track index doesn't exist - track " << trackIndex << " type " << type;
2924     throw new Exception(msg.str(),__FILE__, __LINE__, __FUNCTION__);
2925     return MP4_INVALID_TRACK_ID; // satisfy MS compiler
2926 }
2927 
FindTrackIndex(MP4TrackId trackId)2928 uint16_t MP4File::FindTrackIndex(MP4TrackId trackId)
2929 {
2930     for (uint32_t i = 0; i < m_pTracks.Size() && i <= 0xFFFF; i++) {
2931         if (m_pTracks[i]->GetId() == trackId) {
2932             return (uint16_t)i;
2933         }
2934     }
2935 
2936     ostringstream msg;
2937     msg << "Track id " << trackId << " doesn't exist";
2938     throw new Exception(msg.str(),__FILE__, __LINE__, __FUNCTION__);
2939     return (uint16_t)-1; // satisfy MS compiler
2940 }
2941 
FindTrakAtomIndex(MP4TrackId trackId)2942 uint16_t MP4File::FindTrakAtomIndex(MP4TrackId trackId)
2943 {
2944     if (trackId) {
2945         for (uint32_t i = 0; i < m_trakIds.Size(); i++) {
2946             if (m_trakIds[i] == trackId) {
2947                 return i;
2948             }
2949         }
2950     }
2951 
2952     ostringstream msg;
2953     msg << "Track id " << trackId << " doesn't exist";
2954     throw new Exception(msg.str(),__FILE__, __LINE__, __FUNCTION__);
2955     return (uint16_t)-1; // satisfy MS compiler
2956 }
2957 
GetSampleSize(MP4TrackId trackId,MP4SampleId sampleId)2958 uint32_t MP4File::GetSampleSize(MP4TrackId trackId, MP4SampleId sampleId)
2959 {
2960     return m_pTracks[FindTrackIndex(trackId)]->GetSampleSize(sampleId);
2961 }
2962 
GetTrackMaxSampleSize(MP4TrackId trackId)2963 uint32_t MP4File::GetTrackMaxSampleSize(MP4TrackId trackId)
2964 {
2965     return m_pTracks[FindTrackIndex(trackId)]->GetMaxSampleSize();
2966 }
2967 
GetSampleIdFromTime(MP4TrackId trackId,MP4Timestamp when,bool wantSyncSample)2968 MP4SampleId MP4File::GetSampleIdFromTime(MP4TrackId trackId,
2969         MP4Timestamp when, bool wantSyncSample)
2970 {
2971     return m_pTracks[FindTrackIndex(trackId)]->
2972            GetSampleIdFromTime(when, wantSyncSample);
2973 }
2974 
GetSampleTime(MP4TrackId trackId,MP4SampleId sampleId)2975 MP4Timestamp MP4File::GetSampleTime(
2976     MP4TrackId trackId, MP4SampleId sampleId)
2977 {
2978     MP4Timestamp timestamp;
2979     m_pTracks[FindTrackIndex(trackId)]->
2980     GetSampleTimes(sampleId, &timestamp, NULL);
2981     return timestamp;
2982 }
2983 
GetSampleDuration(MP4TrackId trackId,MP4SampleId sampleId)2984 MP4Duration MP4File::GetSampleDuration(
2985     MP4TrackId trackId, MP4SampleId sampleId)
2986 {
2987     MP4Duration duration;
2988     m_pTracks[FindTrackIndex(trackId)]->
2989     GetSampleTimes(sampleId, NULL, &duration);
2990     return duration;
2991 }
2992 
GetSampleRenderingOffset(MP4TrackId trackId,MP4SampleId sampleId)2993 MP4Duration MP4File::GetSampleRenderingOffset(
2994     MP4TrackId trackId, MP4SampleId sampleId)
2995 {
2996     return m_pTracks[FindTrackIndex(trackId)]->
2997            GetSampleRenderingOffset(sampleId);
2998 }
2999 
GetSampleSync(MP4TrackId trackId,MP4SampleId sampleId)3000 bool MP4File::GetSampleSync(MP4TrackId trackId, MP4SampleId sampleId)
3001 {
3002     return m_pTracks[FindTrackIndex(trackId)]->IsSyncSample(sampleId);
3003 }
3004 
ReadSample(MP4TrackId trackId,MP4SampleId sampleId,uint8_t ** ppBytes,uint32_t * pNumBytes,MP4Timestamp * pStartTime,MP4Duration * pDuration,MP4Duration * pRenderingOffset,bool * pIsSyncSample,bool * hasDependencyFlags,uint32_t * dependencyFlags)3005 void MP4File::ReadSample(
3006     MP4TrackId    trackId,
3007     MP4SampleId   sampleId,
3008     uint8_t**     ppBytes,
3009     uint32_t*     pNumBytes,
3010     MP4Timestamp* pStartTime,
3011     MP4Duration*  pDuration,
3012     MP4Duration*  pRenderingOffset,
3013     bool*         pIsSyncSample,
3014     bool*         hasDependencyFlags,
3015     uint32_t*     dependencyFlags )
3016 {
3017     m_pTracks[FindTrackIndex(trackId)]->ReadSample(
3018         sampleId,
3019         ppBytes,
3020         pNumBytes,
3021         pStartTime,
3022         pDuration,
3023         pRenderingOffset,
3024         pIsSyncSample,
3025         hasDependencyFlags,
3026         dependencyFlags );
3027 }
3028 
WriteSample(MP4TrackId trackId,const uint8_t * pBytes,uint32_t numBytes,MP4Duration duration,MP4Duration renderingOffset,bool isSyncSample)3029 void MP4File::WriteSample(
3030     MP4TrackId     trackId,
3031     const uint8_t* pBytes,
3032     uint32_t       numBytes,
3033     MP4Duration    duration,
3034     MP4Duration    renderingOffset,
3035     bool           isSyncSample )
3036 {
3037     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3038     m_pTracks[FindTrackIndex(trackId)]->WriteSample(
3039         pBytes, numBytes, duration, renderingOffset, isSyncSample );
3040     m_pModificationProperty->SetValue( MP4GetAbsTimestamp() );
3041 }
3042 
WriteSampleDependency(MP4TrackId trackId,const uint8_t * pBytes,uint32_t numBytes,MP4Duration duration,MP4Duration renderingOffset,bool isSyncSample,uint32_t dependencyFlags)3043 void MP4File::WriteSampleDependency(
3044     MP4TrackId     trackId,
3045     const uint8_t* pBytes,
3046     uint32_t       numBytes,
3047     MP4Duration    duration,
3048     MP4Duration    renderingOffset,
3049     bool           isSyncSample,
3050     uint32_t       dependencyFlags )
3051 {
3052     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3053     m_pTracks[FindTrackIndex(trackId)]->WriteSampleDependency(
3054         pBytes, numBytes, duration, renderingOffset, isSyncSample, dependencyFlags );
3055     m_pModificationProperty->SetValue( MP4GetAbsTimestamp() );
3056 }
3057 
SetSampleRenderingOffset(MP4TrackId trackId,MP4SampleId sampleId,MP4Duration renderingOffset)3058 void MP4File::SetSampleRenderingOffset(MP4TrackId trackId,
3059                                        MP4SampleId sampleId, MP4Duration renderingOffset)
3060 {
3061     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3062     m_pTracks[FindTrackIndex(trackId)]->
3063     SetSampleRenderingOffset(sampleId, renderingOffset);
3064 
3065     m_pModificationProperty->SetValue(MP4GetAbsTimestamp());
3066 }
3067 
MakeFtypAtom(char * majorBrand,uint32_t minorVersion,char ** compatibleBrands,uint32_t compatibleBrandsCount)3068 void MP4File::MakeFtypAtom(
3069     char*    majorBrand,
3070     uint32_t minorVersion,
3071     char**   compatibleBrands,
3072     uint32_t compatibleBrandsCount)
3073 {
3074     MP4FtypAtom* ftyp = (MP4FtypAtom*)m_pRootAtom->FindAtom( "ftyp" );
3075     if (ftyp == NULL)
3076         ftyp = (MP4FtypAtom*)InsertChildAtom( m_pRootAtom, "ftyp", 0 );
3077 
3078     // bail if majorbrand is not specified; defaults suffice.
3079     if (majorBrand == NULL)
3080         return;
3081 
3082     ftyp->majorBrand.SetValue( majorBrand );
3083     ftyp->minorVersion.SetValue( minorVersion );
3084 
3085     ftyp->compatibleBrands.SetCount( compatibleBrandsCount );
3086     for( uint32_t i = 0; i < compatibleBrandsCount; i++ )
3087         ftyp->compatibleBrands.SetValue( compatibleBrands[i], i );
3088 }
3089 
MakeTrackName(MP4TrackId trackId,const char * name)3090 char* MP4File::MakeTrackName(MP4TrackId trackId, const char* name)
3091 {
3092     uint16_t trakIndex = FindTrakAtomIndex(trackId);
3093 
3094     if (name == NULL || name[0] == '\0') {
3095         snprintf(m_trakName, sizeof(m_trakName),
3096                  "moov.trak[%u]", trakIndex);
3097     } else {
3098         snprintf(m_trakName, sizeof(m_trakName),
3099                  "moov.trak[%u].%s", trakIndex, name);
3100     }
3101     return m_trakName;
3102 }
3103 
FindTrackAtom(MP4TrackId trackId,const char * name)3104 MP4Atom *MP4File::FindTrackAtom (MP4TrackId trackId, const char *name)
3105 {
3106     return FindAtom(MakeTrackName(trackId, name));
3107 }
3108 
GetTrackIntegerProperty(MP4TrackId trackId,const char * name)3109 uint64_t MP4File::GetTrackIntegerProperty(MP4TrackId trackId, const char* name)
3110 {
3111     return GetIntegerProperty(MakeTrackName(trackId, name));
3112 }
3113 
SetTrackIntegerProperty(MP4TrackId trackId,const char * name,int64_t value)3114 void MP4File::SetTrackIntegerProperty(MP4TrackId trackId, const char* name,
3115                                       int64_t value)
3116 {
3117     SetIntegerProperty(MakeTrackName(trackId, name), value);
3118 }
3119 
GetTrackFloatProperty(MP4TrackId trackId,const char * name)3120 float MP4File::GetTrackFloatProperty(MP4TrackId trackId, const char* name)
3121 {
3122     return GetFloatProperty(MakeTrackName(trackId, name));
3123 }
3124 
SetTrackFloatProperty(MP4TrackId trackId,const char * name,float value)3125 void MP4File::SetTrackFloatProperty(MP4TrackId trackId, const char* name,
3126                                     float value)
3127 {
3128     SetFloatProperty(MakeTrackName(trackId, name), value);
3129 }
3130 
GetTrackStringProperty(MP4TrackId trackId,const char * name)3131 const char* MP4File::GetTrackStringProperty(MP4TrackId trackId, const char* name)
3132 {
3133     return GetStringProperty(MakeTrackName(trackId, name));
3134 }
3135 
SetTrackStringProperty(MP4TrackId trackId,const char * name,const char * value)3136 void MP4File::SetTrackStringProperty(MP4TrackId trackId, const char* name,
3137                                      const char* value)
3138 {
3139     SetStringProperty(MakeTrackName(trackId, name), value);
3140 }
3141 
GetTrackBytesProperty(MP4TrackId trackId,const char * name,uint8_t ** ppValue,uint32_t * pValueSize)3142 void MP4File::GetTrackBytesProperty(MP4TrackId trackId, const char* name,
3143                                     uint8_t** ppValue, uint32_t* pValueSize)
3144 {
3145     GetBytesProperty(MakeTrackName(trackId, name), ppValue, pValueSize);
3146 }
3147 
SetTrackBytesProperty(MP4TrackId trackId,const char * name,const uint8_t * pValue,uint32_t valueSize)3148 void MP4File::SetTrackBytesProperty(MP4TrackId trackId, const char* name,
3149                                     const uint8_t* pValue, uint32_t valueSize)
3150 {
3151     SetBytesProperty(MakeTrackName(trackId, name), pValue, valueSize);
3152 }
3153 
GetTrackLanguage(MP4TrackId trackId,char * code)3154 bool MP4File::GetTrackLanguage( MP4TrackId trackId, char* code )
3155 {
3156     ostringstream oss;
3157     oss << "moov.trak[" << FindTrakAtomIndex(trackId) << "].mdia.mdhd.language";
3158 
3159     MP4Property* prop;
3160     if( !m_pRootAtom->FindProperty( oss.str().c_str(), &prop ))
3161         return false;
3162 
3163     if( prop->GetType() != LanguageCodeProperty )
3164         return false;
3165 
3166     MP4LanguageCodeProperty& lang = *static_cast<MP4LanguageCodeProperty*>(prop);
3167     string slang;
3168     bmff::enumLanguageCode.toString( lang.GetValue(), slang );
3169     if( slang.length() != 3 ) {
3170         memset( code, '\0', 4 );
3171     }
3172     else {
3173         memcpy( code, slang.c_str(), 3 );
3174         code[3] = '\0';
3175     }
3176 
3177     return true;
3178 }
3179 
SetTrackLanguage(MP4TrackId trackId,const char * code)3180 bool MP4File::SetTrackLanguage( MP4TrackId trackId, const char* code )
3181 {
3182     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3183 
3184     ostringstream oss;
3185     oss << "moov.trak[" << FindTrakAtomIndex(trackId) << "].mdia.mdhd.language";
3186 
3187     MP4Property* prop;
3188     if( !m_pRootAtom->FindProperty( oss.str().c_str(), &prop ))
3189         return false;
3190 
3191     if( prop->GetType() != LanguageCodeProperty )
3192         return false;
3193 
3194     MP4LanguageCodeProperty& lang = *static_cast<MP4LanguageCodeProperty*>(prop);
3195     lang.SetValue( bmff::enumLanguageCode.toType( code ));
3196 
3197     return true;
3198 }
3199 
3200 
GetTrackName(MP4TrackId trackId,char ** name)3201 bool MP4File::GetTrackName( MP4TrackId trackId, char** name )
3202 {
3203     unsigned char *val = NULL;
3204     uint32_t valSize = 0;
3205     MP4Atom *pMetaAtom;
3206 
3207     pMetaAtom = m_pRootAtom->FindAtom(MakeTrackName(trackId,"udta.name"));
3208 
3209     if (pMetaAtom)
3210     {
3211         GetBytesProperty(MakeTrackName(trackId,"udta.name.value"), (uint8_t**)&val, &valSize);
3212     }
3213     if (valSize > 0)
3214     {
3215         *name = (char*)malloc((valSize+1)*sizeof(char));
3216         if (*name == NULL) {
3217             free(val);
3218             return false;
3219         }
3220         memcpy(*name, val, valSize*sizeof(unsigned char));
3221         free(val);
3222         (*name)[valSize] = '\0';
3223         return true;
3224     }
3225 
3226     return false;
3227 }
3228 
SetTrackName(MP4TrackId trackId,const char * name)3229 bool MP4File::SetTrackName( MP4TrackId trackId, const char* name )
3230 {
3231     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3232     char atomstring[40];
3233     MP4Atom *pMetaAtom;
3234     MP4BytesProperty *pMetadataProperty = NULL;
3235     snprintf(atomstring, 40, "%s", MakeTrackName(trackId,"udta.name"));
3236 
3237     pMetaAtom = m_pRootAtom->FindAtom(atomstring);
3238 
3239     if (!pMetaAtom)
3240     {
3241         if (!AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name"))
3242             return false;
3243 
3244         pMetaAtom = m_pRootAtom->FindAtom(atomstring);
3245         if (pMetaAtom == NULL) return false;
3246     }
3247 
3248     ASSERT(pMetaAtom->FindProperty("name.value",
3249                                    (MP4Property**)&pMetadataProperty));
3250     ASSERT(pMetadataProperty);
3251 
3252     pMetadataProperty->SetValue((uint8_t*)name, (uint32_t)strlen(name));
3253 
3254     return true;
3255 }
3256 
3257 // file level convenience functions
3258 
GetDuration()3259 MP4Duration MP4File::GetDuration()
3260 {
3261     return m_pDurationProperty->GetValue();
3262 }
3263 
SetDuration(MP4Duration value)3264 void MP4File::SetDuration(MP4Duration value)
3265 {
3266     m_pDurationProperty->SetValue(value);
3267 }
3268 
GetTimeScale()3269 uint32_t MP4File::GetTimeScale()
3270 {
3271     return m_pTimeScaleProperty->GetValue();
3272 }
3273 
SetTimeScale(uint32_t value)3274 void MP4File::SetTimeScale(uint32_t value)
3275 {
3276     if (value == 0) {
3277         throw new Exception("invalid value", __FILE__, __LINE__, __FUNCTION__);
3278     }
3279     m_pTimeScaleProperty->SetValue(value);
3280 }
3281 
GetODProfileLevel()3282 uint8_t MP4File::GetODProfileLevel()
3283 {
3284     return GetIntegerProperty("moov.iods.ODProfileLevelId");
3285 }
3286 
SetODProfileLevel(uint8_t value)3287 void MP4File::SetODProfileLevel(uint8_t value)
3288 {
3289     SetIntegerProperty("moov.iods.ODProfileLevelId", value);
3290 }
3291 
GetSceneProfileLevel()3292 uint8_t MP4File::GetSceneProfileLevel()
3293 {
3294     return GetIntegerProperty("moov.iods.sceneProfileLevelId");
3295 }
3296 
SetSceneProfileLevel(uint8_t value)3297 void MP4File::SetSceneProfileLevel(uint8_t value)
3298 {
3299     SetIntegerProperty("moov.iods.sceneProfileLevelId", value);
3300 }
3301 
GetVideoProfileLevel()3302 uint8_t MP4File::GetVideoProfileLevel()
3303 {
3304     return GetIntegerProperty("moov.iods.visualProfileLevelId");
3305 }
3306 
SetVideoProfileLevel(uint8_t value)3307 void MP4File::SetVideoProfileLevel(uint8_t value)
3308 {
3309     SetIntegerProperty("moov.iods.visualProfileLevelId", value);
3310 }
3311 
GetAudioProfileLevel()3312 uint8_t MP4File::GetAudioProfileLevel()
3313 {
3314     return GetIntegerProperty("moov.iods.audioProfileLevelId");
3315 }
3316 
SetAudioProfileLevel(uint8_t value)3317 void MP4File::SetAudioProfileLevel(uint8_t value)
3318 {
3319     SetIntegerProperty("moov.iods.audioProfileLevelId", value);
3320 }
3321 
GetGraphicsProfileLevel()3322 uint8_t MP4File::GetGraphicsProfileLevel()
3323 {
3324     return GetIntegerProperty("moov.iods.graphicsProfileLevelId");
3325 }
3326 
SetGraphicsProfileLevel(uint8_t value)3327 void MP4File::SetGraphicsProfileLevel(uint8_t value)
3328 {
3329     SetIntegerProperty("moov.iods.graphicsProfileLevelId", value);
3330 }
3331 
GetSessionSdp()3332 const char* MP4File::GetSessionSdp()
3333 {
3334     return GetStringProperty("moov.udta.hnti.rtp .sdpText");
3335 }
3336 
SetSessionSdp(const char * sdpString)3337 void MP4File::SetSessionSdp(const char* sdpString)
3338 {
3339     (void)AddDescendantAtoms("moov", "udta.hnti.rtp ");
3340 
3341     SetStringProperty("moov.udta.hnti.rtp .sdpText", sdpString);
3342 }
3343 
AppendSessionSdp(const char * sdpFragment)3344 void MP4File::AppendSessionSdp(const char* sdpFragment)
3345 {
3346     const char* oldSdpString = NULL;
3347     try {
3348         oldSdpString = GetSessionSdp();
3349     }
3350     catch (Exception* x) {
3351         delete x;
3352         SetSessionSdp(sdpFragment);
3353         return;
3354     }
3355 
3356     char* newSdpString =
3357         (char*)MP4Malloc(strlen(oldSdpString) + strlen(sdpFragment) + 1);
3358     strcpy(newSdpString, oldSdpString);
3359     strcat(newSdpString, sdpFragment);
3360     SetSessionSdp(newSdpString);
3361     MP4Free(newSdpString);
3362 }
3363 
3364 //
3365 // ismacrypt API - retrieve OriginalFormatBox
3366 //
3367 // parameters are assumed to have been sanity tested in mp4.cpp
3368 // don't call this unless media data name is 'encv',
3369 // results may otherwise be unpredictable.
3370 //
3371 // input:
3372 // trackID - valid encv track ID for this file
3373 // buflen  - length of oFormat, minimum is 5 (4cc plus null terminator)
3374 //
3375 // output:
3376 // oFormat - buffer to return null terminated string containing
3377 //           track original format
3378 // return:
3379 // 0       - original format returned OK
3380 // 1       - buffer length error or problem retrieving track property
3381 //
3382 //
GetTrackMediaDataOriginalFormat(MP4TrackId trackId,char * originalFormat,uint32_t buflen)3383 bool MP4File::GetTrackMediaDataOriginalFormat(MP4TrackId trackId,
3384         char *originalFormat, uint32_t buflen)
3385 {
3386     uint32_t format;
3387 
3388     if (buflen < 5)
3389         return false;
3390 
3391     format = GetTrackIntegerProperty(trackId,
3392                                      "mdia.minf.stbl.stsd.*.sinf.frma.data-format");
3393 
3394     IDATOM(format, originalFormat);
3395     return true;
3396 
3397 }
3398 
3399 
3400 // track level convenience functions
3401 
GetTrackNumberOfSamples(MP4TrackId trackId)3402 MP4SampleId MP4File::GetTrackNumberOfSamples(MP4TrackId trackId)
3403 {
3404     return m_pTracks[FindTrackIndex(trackId)]->GetNumberOfSamples();
3405 }
3406 
GetTrackType(MP4TrackId trackId)3407 const char* MP4File::GetTrackType(MP4TrackId trackId)
3408 {
3409     return m_pTracks[FindTrackIndex(trackId)]->GetType();
3410 }
3411 
GetTrackMediaDataName(MP4TrackId trackId)3412 const char *MP4File::GetTrackMediaDataName (MP4TrackId trackId)
3413 {
3414     MP4Atom *pChild;
3415     MP4Atom *pAtom =
3416         FindAtom(MakeTrackName(trackId,
3417                                "mdia.minf.stbl.stsd"));
3418     if (pAtom->GetNumberOfChildAtoms() != 1) {
3419         log.errorf("%s: \"%s\": track %d has more than 1 child atoms in stsd",
3420                    __FUNCTION__, GetFilename().c_str(), trackId);
3421         return NULL;
3422     }
3423     pChild = pAtom->GetChildAtom(0);
3424     return pChild->GetType();
3425 }
3426 
3427 
GetTrackTimeScale(MP4TrackId trackId)3428 uint32_t MP4File::GetTrackTimeScale(MP4TrackId trackId)
3429 {
3430     return m_pTracks[FindTrackIndex(trackId)]->GetTimeScale();
3431 }
3432 
SetTrackTimeScale(MP4TrackId trackId,uint32_t value)3433 void MP4File::SetTrackTimeScale(MP4TrackId trackId, uint32_t value)
3434 {
3435     if (value == 0) {
3436         throw new Exception("invalid value", __FILE__, __LINE__, __FUNCTION__);
3437     }
3438     SetTrackIntegerProperty(trackId, "mdia.mdhd.timeScale", value);
3439 }
3440 
GetTrackDuration(MP4TrackId trackId)3441 MP4Duration MP4File::GetTrackDuration(MP4TrackId trackId)
3442 {
3443     return GetTrackIntegerProperty(trackId, "mdia.mdhd.duration");
3444 }
3445 
GetTrackEsdsObjectTypeId(MP4TrackId trackId)3446 uint8_t MP4File::GetTrackEsdsObjectTypeId(MP4TrackId trackId)
3447 {
3448     // changed mp4a to * to handle enca case
3449     try {
3450         return GetTrackIntegerProperty(trackId,
3451                                        "mdia.minf.stbl.stsd.*.esds.decConfigDescr.objectTypeId");
3452     } catch (Exception *x) {
3453         delete x;
3454         return GetTrackIntegerProperty(trackId,
3455                                        "mdia.minf.stbl.stsd.*.*.esds.decConfigDescr.objectTypeId");
3456     }
3457 }
3458 
GetTrackAudioMpeg4Type(MP4TrackId trackId)3459 uint8_t MP4File::GetTrackAudioMpeg4Type(MP4TrackId trackId)
3460 {
3461     // verify that track is an MPEG-4 audio track
3462     if (GetTrackEsdsObjectTypeId(trackId) != MP4_MPEG4_AUDIO_TYPE) {
3463         return MP4_MPEG4_INVALID_AUDIO_TYPE;
3464     }
3465 
3466     uint8_t* pEsConfig = NULL;
3467     uint32_t esConfigSize;
3468 
3469     // The Mpeg4 audio type (AAC, CELP, HXVC, ...)
3470     // is the first 5 bits of the ES configuration
3471 
3472     GetTrackESConfiguration(trackId, &pEsConfig, &esConfigSize);
3473 
3474     if (esConfigSize < 1) {
3475         free(pEsConfig);
3476         return MP4_MPEG4_INVALID_AUDIO_TYPE;
3477     }
3478 
3479     uint8_t mpeg4Type = ((pEsConfig[0] >> 3) & 0x1f);
3480     // TTTT TXXX XXX  potentially 6 bits of extension.
3481     if (mpeg4Type == 0x1f) {
3482         if (esConfigSize < 2) {
3483             free(pEsConfig);
3484             return MP4_MPEG4_INVALID_AUDIO_TYPE;
3485         }
3486         mpeg4Type = 32 +
3487                     (((pEsConfig[0] & 0x7) << 3) | ((pEsConfig[1] >> 5) & 0x7));
3488     }
3489 
3490     free(pEsConfig);
3491 
3492     return mpeg4Type;
3493 }
3494 
3495 
GetTrackFixedSampleDuration(MP4TrackId trackId)3496 MP4Duration MP4File::GetTrackFixedSampleDuration(MP4TrackId trackId)
3497 {
3498     return m_pTracks[FindTrackIndex(trackId)]->GetFixedSampleDuration();
3499 }
3500 
GetTrackVideoFrameRate(MP4TrackId trackId)3501 double MP4File::GetTrackVideoFrameRate(MP4TrackId trackId)
3502 {
3503     MP4SampleId numSamples =
3504         GetTrackNumberOfSamples(trackId);
3505     uint64_t
3506     msDuration =
3507         ConvertFromTrackDuration(trackId,
3508                                  GetTrackDuration(trackId), MP4_MSECS_TIME_SCALE);
3509 
3510     if (msDuration == 0) {
3511         return 0.0;
3512     }
3513 
3514     return ((double)numSamples / double(msDuration)) * MP4_MSECS_TIME_SCALE;
3515 }
3516 
GetTrackAudioChannels(MP4TrackId trackId)3517 int MP4File::GetTrackAudioChannels (MP4TrackId trackId)
3518 {
3519     return GetTrackIntegerProperty(trackId,
3520                                    "mdia.minf.stbl.stsd.*[0].channels");
3521 }
3522 
3523 // true if media track encrypted according to ismacryp
IsIsmaCrypMediaTrack(MP4TrackId trackId)3524 bool MP4File::IsIsmaCrypMediaTrack(MP4TrackId trackId)
3525 {
3526     if (GetTrackIntegerProperty(trackId,
3527                                 "mdia.minf.stbl.stsd.*.sinf.frma.data-format")
3528             != (uint64_t)-1) {
3529         return true;
3530     }
3531     return false;
3532 }
3533 
IsWriteMode()3534 bool MP4File::IsWriteMode()
3535 {
3536     if( !m_file )
3537         return false;
3538 
3539     switch( m_file->mode ) {
3540         case File::MODE_READ:
3541             return false;
3542 
3543         case File::MODE_MODIFY:
3544         case File::MODE_CREATE:
3545         default:
3546             break;
3547     }
3548 
3549     return true;
3550 }
3551 
GetTrackESConfiguration(MP4TrackId trackId,uint8_t ** ppConfig,uint32_t * pConfigSize)3552 void MP4File::GetTrackESConfiguration(MP4TrackId trackId,
3553                                       uint8_t** ppConfig, uint32_t* pConfigSize)
3554 {
3555     try {
3556         GetTrackBytesProperty(trackId,
3557                               "mdia.minf.stbl.stsd.*[0].esds.decConfigDescr.decSpecificInfo[0].info",
3558                               ppConfig, pConfigSize);
3559     } catch (Exception *x) {
3560         delete x;
3561         GetTrackBytesProperty(trackId,
3562                               "mdia.minf.stbl.stsd.*[0].*.esds.decConfigDescr.decSpecificInfo[0].info",
3563                               ppConfig, pConfigSize);
3564     }
3565 }
3566 
GetTrackVideoMetadata(MP4TrackId trackId,uint8_t ** ppConfig,uint32_t * pConfigSize)3567 void MP4File::GetTrackVideoMetadata(MP4TrackId trackId,
3568                                     uint8_t** ppConfig, uint32_t* pConfigSize)
3569 {
3570     GetTrackBytesProperty(trackId,
3571                           "mdia.minf.stbl.stsd.*[0].*.metadata",
3572                           ppConfig, pConfigSize);
3573 }
3574 
SetTrackESConfiguration(MP4TrackId trackId,const uint8_t * pConfig,uint32_t configSize)3575 void MP4File::SetTrackESConfiguration(MP4TrackId trackId,
3576                                       const uint8_t* pConfig, uint32_t configSize)
3577 {
3578     // get a handle on the track decoder config descriptor
3579     MP4DescriptorProperty* pConfigDescrProperty = NULL;
3580     if (FindProperty(MakeTrackName(trackId,
3581                                    "mdia.minf.stbl.stsd.*[0].esds.decConfigDescr.decSpecificInfo"),
3582                      (MP4Property**)&pConfigDescrProperty) == false ||
3583             pConfigDescrProperty == NULL) {
3584         // probably trackId refers to a hint track
3585         throw new Exception("no such property", __FILE__, __LINE__, __FUNCTION__);
3586     }
3587 
3588     // lookup the property to store the configuration
3589     MP4BytesProperty* pInfoProperty = NULL;
3590     (void)pConfigDescrProperty->FindProperty("decSpecificInfo[0].info",
3591             (MP4Property**)&pInfoProperty);
3592 
3593     // configuration being set for the first time
3594     if (pInfoProperty == NULL) {
3595         // need to create a new descriptor to hold it
3596         MP4Descriptor* pConfigDescr =
3597             pConfigDescrProperty->AddDescriptor(MP4DecSpecificDescrTag);
3598         pConfigDescr->Generate();
3599 
3600         (void)pConfigDescrProperty->FindProperty(
3601             "decSpecificInfo[0].info",
3602             (MP4Property**)&pInfoProperty);
3603         ASSERT(pInfoProperty);
3604     }
3605 
3606     // set the value
3607     pInfoProperty->SetValue(pConfig, configSize);
3608 }
3609 
3610 
GetTrackH264SeqPictHeaders(MP4TrackId trackId,uint8_t *** pppSeqHeader,uint32_t ** ppSeqHeaderSize,uint8_t *** pppPictHeader,uint32_t ** ppPictHeaderSize)3611 void MP4File::GetTrackH264SeqPictHeaders (MP4TrackId trackId,
3612         uint8_t ***pppSeqHeader,
3613         uint32_t **ppSeqHeaderSize,
3614         uint8_t ***pppPictHeader,
3615         uint32_t **ppPictHeaderSize)
3616 {
3617     uint32_t count;
3618     const char *format;
3619     MP4Atom *avcCAtom;
3620 
3621     *pppSeqHeader = NULL;
3622     *pppPictHeader = NULL;
3623     *ppSeqHeaderSize = NULL;
3624     *ppPictHeaderSize = NULL;
3625 
3626     // get 4cc media format - can be avc1 or encv for ismacrypted track
3627     format = GetTrackMediaDataName (trackId);
3628 
3629     if (!strcasecmp(format, "avc1"))
3630         avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1.avcC"));
3631     else if (!strcasecmp(format, "encv"))
3632         avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.avcC"));
3633     else
3634         // huh?  unknown track format
3635         return;
3636 
3637     MP4BitfieldProperty *pSeqCount;
3638     MP4IntegerProperty *pSeqLen, *pPictCount, *pPictLen;
3639     MP4BytesProperty *pSeqVal, *pPictVal;
3640 
3641     if ((avcCAtom->FindProperty("avcC.numOfSequenceParameterSets",
3642                                 (MP4Property **)&pSeqCount) == false) ||
3643             (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetLength",
3644                                     (MP4Property **)&pSeqLen) == false) ||
3645             (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetNALUnit",
3646                                     (MP4Property **)&pSeqVal) == false)) {
3647         log.errorf("%s: \"%s\": Could not find avcC properties", __FUNCTION__, GetFilename().c_str());
3648         return ;
3649     }
3650     uint8_t **ppSeqHeader =
3651         (uint8_t **)malloc((pSeqCount->GetValue() + 1) * sizeof(uint8_t *));
3652     if (ppSeqHeader == NULL) return;
3653     *pppSeqHeader = ppSeqHeader;
3654 
3655     uint32_t *pSeqHeaderSize =
3656         (uint32_t *)malloc((pSeqCount->GetValue() + 1) * sizeof(uint32_t *));
3657 
3658     if (pSeqHeaderSize == NULL) return;
3659 
3660     *ppSeqHeaderSize = pSeqHeaderSize;
3661     for (count = 0; count < pSeqCount->GetValue(); count++) {
3662         pSeqVal->GetValue(&(ppSeqHeader[count]), &(pSeqHeaderSize[count]),
3663                           count);
3664     }
3665     ppSeqHeader[count] = NULL;
3666     pSeqHeaderSize[count] = 0;
3667 
3668     if ((avcCAtom->FindProperty("avcC.numOfPictureParameterSets",
3669                                 (MP4Property **)&pPictCount) == false) ||
3670             (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetLength",
3671                                     (MP4Property **)&pPictLen) == false) ||
3672             (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetNALUnit",
3673                                     (MP4Property **)&pPictVal) == false)) {
3674         log.errorf("%s: \"%s\": Could not find avcC picture table properties",
3675                    __FUNCTION__, GetFilename().c_str());
3676         return ;
3677     }
3678     uint8_t
3679     **ppPictHeader =
3680         (uint8_t **)malloc((pPictCount->GetValue() + 1) * sizeof(uint8_t *));
3681     if (ppPictHeader == NULL) return;
3682     uint32_t *pPictHeaderSize =
3683         (uint32_t *)malloc((pPictCount->GetValue() + 1)* sizeof(uint32_t *));
3684     if (pPictHeaderSize == NULL) {
3685         free(ppPictHeader);
3686         return;
3687     }
3688     *pppPictHeader = ppPictHeader;
3689     *ppPictHeaderSize = pPictHeaderSize;
3690 
3691     for (count = 0; count < pPictCount->GetValue(); count++) {
3692         pPictVal->GetValue(&(ppPictHeader[count]), &(pPictHeaderSize[count]),
3693                            count);
3694     }
3695     ppPictHeader[count] = NULL;
3696     pPictHeaderSize[count] = 0;
3697     return ;
3698 }
3699 
3700 
3701 
GetHintTrackSdp(MP4TrackId hintTrackId)3702 const char* MP4File::GetHintTrackSdp(MP4TrackId hintTrackId)
3703 {
3704     return GetTrackStringProperty(hintTrackId, "udta.hnti.sdp .sdpText");
3705 }
3706 
SetHintTrackSdp(MP4TrackId hintTrackId,const char * sdpString)3707 void MP4File::SetHintTrackSdp(MP4TrackId hintTrackId, const char* sdpString)
3708 {
3709     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3710 
3711     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3712         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3713     }
3714 
3715     (void)AddDescendantAtoms(
3716         MakeTrackName(hintTrackId, NULL), "udta.hnti.sdp ");
3717 
3718     SetTrackStringProperty(hintTrackId, "udta.hnti.sdp .sdpText", sdpString);
3719 }
3720 
AppendHintTrackSdp(MP4TrackId hintTrackId,const char * sdpFragment)3721 void MP4File::AppendHintTrackSdp(MP4TrackId hintTrackId,
3722                                  const char* sdpFragment)
3723 {
3724     const char* oldSdpString = NULL;
3725     try {
3726         oldSdpString = GetHintTrackSdp(hintTrackId);
3727     }
3728     catch (Exception* x) {
3729         delete x;
3730         SetHintTrackSdp(hintTrackId, sdpFragment);
3731         return;
3732     }
3733 
3734     char* newSdpString =
3735         (char*)MP4Malloc(strlen(oldSdpString) + strlen(sdpFragment) + 1);
3736     strcpy(newSdpString, oldSdpString);
3737     strcat(newSdpString, sdpFragment);
3738     SetHintTrackSdp(hintTrackId, newSdpString);
3739     MP4Free(newSdpString);
3740 }
3741 
GetHintTrackRtpPayload(MP4TrackId hintTrackId,char ** ppPayloadName,uint8_t * pPayloadNumber,uint16_t * pMaxPayloadSize,char ** ppEncodingParams)3742 void MP4File::GetHintTrackRtpPayload(
3743     MP4TrackId hintTrackId,
3744     char** ppPayloadName,
3745     uint8_t* pPayloadNumber,
3746     uint16_t* pMaxPayloadSize,
3747     char **ppEncodingParams)
3748 {
3749     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3750 
3751     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3752         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3753     }
3754 
3755     ((MP4RtpHintTrack*)pTrack)->GetPayload(
3756         ppPayloadName, pPayloadNumber, pMaxPayloadSize, ppEncodingParams);
3757 }
3758 
SetHintTrackRtpPayload(MP4TrackId hintTrackId,const char * payloadName,uint8_t * pPayloadNumber,uint16_t maxPayloadSize,const char * encoding_params,bool include_rtp_map,bool include_mpeg4_esid)3759 void MP4File::SetHintTrackRtpPayload(MP4TrackId hintTrackId,
3760                                      const char* payloadName, uint8_t* pPayloadNumber, uint16_t maxPayloadSize,
3761                                      const char *encoding_params,
3762                                      bool include_rtp_map,
3763                                      bool include_mpeg4_esid)
3764 {
3765     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3766 
3767     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3768         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3769     }
3770 
3771     uint8_t payloadNumber;
3772     if (pPayloadNumber && *pPayloadNumber != MP4_SET_DYNAMIC_PAYLOAD) {
3773         payloadNumber = *pPayloadNumber;
3774     } else {
3775         payloadNumber = AllocRtpPayloadNumber();
3776         if (pPayloadNumber) {
3777             *pPayloadNumber = payloadNumber;
3778         }
3779     }
3780 
3781     ((MP4RtpHintTrack*)pTrack)->SetPayload(
3782         payloadName, payloadNumber, maxPayloadSize, encoding_params,
3783         include_rtp_map, include_mpeg4_esid);
3784 }
3785 
AllocRtpPayloadNumber()3786 uint8_t MP4File::AllocRtpPayloadNumber()
3787 {
3788     MP4Integer32Array usedPayloads;
3789     uint32_t i;
3790 
3791     // collect rtp payload numbers in use by existing tracks
3792     for (i = 0; i < m_pTracks.Size(); i++) {
3793         MP4Atom& trakAtom = m_pTracks[i]->GetTrakAtom();
3794 
3795         MP4Integer32Property* pPayloadProperty = NULL;
3796         if (trakAtom.FindProperty("trak.udta.hinf.payt.payloadNumber",
3797                                     (MP4Property**)&pPayloadProperty) &&
3798                 pPayloadProperty) {
3799             usedPayloads.Add(pPayloadProperty->GetValue());
3800         }
3801     }
3802 
3803     // search dynamic payload range for an available slot
3804     uint8_t payload;
3805     for (payload = 96; payload < 128; payload++) {
3806         for (i = 0; i < usedPayloads.Size(); i++) {
3807             if (payload == usedPayloads[i]) {
3808                 break;
3809             }
3810         }
3811         if (i == usedPayloads.Size()) {
3812             break;
3813         }
3814     }
3815 
3816     if (payload >= 128) {
3817         throw new Exception("no more available rtp payload numbers",
3818                             __FILE__, __LINE__, __FUNCTION__);
3819     }
3820 
3821     return payload;
3822 }
3823 
GetHintTrackReferenceTrackId(MP4TrackId hintTrackId)3824 MP4TrackId MP4File::GetHintTrackReferenceTrackId(
3825     MP4TrackId hintTrackId)
3826 {
3827     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3828 
3829     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3830         throw new Exception("track is not a hint track",
3831                             __FILE__, __LINE__, __FUNCTION__);
3832     }
3833 
3834     MP4Track* pRefTrack = ((MP4RtpHintTrack*)pTrack)->GetRefTrack();
3835 
3836     if (pRefTrack == NULL) {
3837         return MP4_INVALID_TRACK_ID;
3838     }
3839     return pRefTrack->GetId();
3840 }
3841 
ReadRtpHint(MP4TrackId hintTrackId,MP4SampleId hintSampleId,uint16_t * pNumPackets)3842 void MP4File::ReadRtpHint(
3843     MP4TrackId hintTrackId,
3844     MP4SampleId hintSampleId,
3845     uint16_t* pNumPackets)
3846 {
3847     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3848 
3849     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3850         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3851     }
3852     ((MP4RtpHintTrack*)pTrack)->
3853     ReadHint(hintSampleId, pNumPackets);
3854 }
3855 
GetRtpHintNumberOfPackets(MP4TrackId hintTrackId)3856 uint16_t MP4File::GetRtpHintNumberOfPackets(
3857     MP4TrackId hintTrackId)
3858 {
3859     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3860 
3861     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3862         throw new Exception("track is not a hint track",
3863                             __FILE__, __LINE__, __FUNCTION__);
3864     }
3865     return ((MP4RtpHintTrack*)pTrack)->GetHintNumberOfPackets();
3866 }
3867 
GetRtpPacketBFrame(MP4TrackId hintTrackId,uint16_t packetIndex)3868 int8_t MP4File::GetRtpPacketBFrame(
3869     MP4TrackId hintTrackId,
3870     uint16_t packetIndex)
3871 {
3872     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3873 
3874     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3875         throw new Exception("track is not a hint track",
3876                             __FILE__, __LINE__, __FUNCTION__);
3877     }
3878     return ((MP4RtpHintTrack*)pTrack)->GetPacketBFrame(packetIndex);
3879 }
3880 
GetRtpPacketTransmitOffset(MP4TrackId hintTrackId,uint16_t packetIndex)3881 int32_t MP4File::GetRtpPacketTransmitOffset(
3882     MP4TrackId hintTrackId,
3883     uint16_t packetIndex)
3884 {
3885     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3886 
3887     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3888         throw new Exception("track is not a hint track",
3889                             __FILE__, __LINE__, __FUNCTION__);
3890     }
3891     return ((MP4RtpHintTrack*)pTrack)->GetPacketTransmitOffset(packetIndex);
3892 }
3893 
ReadRtpPacket(MP4TrackId hintTrackId,uint16_t packetIndex,uint8_t ** ppBytes,uint32_t * pNumBytes,uint32_t ssrc,bool includeHeader,bool includePayload)3894 void MP4File::ReadRtpPacket(
3895     MP4TrackId hintTrackId,
3896     uint16_t packetIndex,
3897     uint8_t** ppBytes,
3898     uint32_t* pNumBytes,
3899     uint32_t ssrc,
3900     bool includeHeader,
3901     bool includePayload)
3902 {
3903     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3904 
3905     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3906         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3907     }
3908     ((MP4RtpHintTrack*)pTrack)->ReadPacket(
3909         packetIndex, ppBytes, pNumBytes,
3910         ssrc, includeHeader, includePayload);
3911 }
3912 
GetRtpTimestampStart(MP4TrackId hintTrackId)3913 MP4Timestamp MP4File::GetRtpTimestampStart(
3914     MP4TrackId hintTrackId)
3915 {
3916     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3917 
3918     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3919         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3920     }
3921     return ((MP4RtpHintTrack*)pTrack)->GetRtpTimestampStart();
3922 }
3923 
SetRtpTimestampStart(MP4TrackId hintTrackId,MP4Timestamp rtpStart)3924 void MP4File::SetRtpTimestampStart(
3925     MP4TrackId hintTrackId,
3926     MP4Timestamp rtpStart)
3927 {
3928     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3929 
3930     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3931         throw new Exception("track is not a hint track",
3932                             __FILE__, __LINE__, __FUNCTION__);
3933     }
3934     ((MP4RtpHintTrack*)pTrack)->SetRtpTimestampStart(rtpStart);
3935 }
3936 
AddRtpHint(MP4TrackId hintTrackId,bool isBframe,uint32_t timestampOffset)3937 void MP4File::AddRtpHint(MP4TrackId hintTrackId,
3938                          bool isBframe, uint32_t timestampOffset)
3939 {
3940     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3941 
3942     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3943 
3944     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3945         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3946     }
3947     ((MP4RtpHintTrack*)pTrack)->AddHint(isBframe, timestampOffset);
3948 }
3949 
AddRtpPacket(MP4TrackId hintTrackId,bool setMbit,int32_t transmitOffset)3950 void MP4File::AddRtpPacket(
3951     MP4TrackId hintTrackId, bool setMbit, int32_t transmitOffset)
3952 {
3953     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3954 
3955     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3956 
3957     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3958         throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
3959     }
3960     ((MP4RtpHintTrack*)pTrack)->AddPacket(setMbit, transmitOffset);
3961 }
3962 
AddRtpImmediateData(MP4TrackId hintTrackId,const uint8_t * pBytes,uint32_t numBytes)3963 void MP4File::AddRtpImmediateData(MP4TrackId hintTrackId,
3964                                   const uint8_t* pBytes, uint32_t numBytes)
3965 {
3966     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3967 
3968     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3969 
3970     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3971         throw new Exception("track is not a hint track",
3972                             __FILE__, __LINE__, __FUNCTION__);
3973     }
3974     ((MP4RtpHintTrack*)pTrack)->AddImmediateData(pBytes, numBytes);
3975 }
3976 
AddRtpSampleData(MP4TrackId hintTrackId,MP4SampleId sampleId,uint32_t dataOffset,uint32_t dataLength)3977 void MP4File::AddRtpSampleData(MP4TrackId hintTrackId,
3978                                MP4SampleId sampleId, uint32_t dataOffset, uint32_t dataLength)
3979 {
3980     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3981 
3982     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3983 
3984     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3985         throw new Exception("track is not a hint track",
3986                             __FILE__, __LINE__, __FUNCTION__);
3987     }
3988     ((MP4RtpHintTrack*)pTrack)->AddSampleData(
3989         sampleId, dataOffset, dataLength);
3990 }
3991 
AddRtpESConfigurationPacket(MP4TrackId hintTrackId)3992 void MP4File::AddRtpESConfigurationPacket(MP4TrackId hintTrackId)
3993 {
3994     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
3995 
3996     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
3997 
3998     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
3999         throw new Exception("track is not a hint track",
4000                             __FILE__, __LINE__, __FUNCTION__);
4001     }
4002     ((MP4RtpHintTrack*)pTrack)->AddESConfigurationPacket();
4003 }
4004 
WriteRtpHint(MP4TrackId hintTrackId,MP4Duration duration,bool isSyncSample)4005 void MP4File::WriteRtpHint(MP4TrackId hintTrackId,
4006                            MP4Duration duration, bool isSyncSample)
4007 {
4008     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
4009 
4010     MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
4011 
4012     if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
4013         throw new Exception("track is not a hint track",
4014                             __FILE__, __LINE__, __FUNCTION__);
4015     }
4016     ((MP4RtpHintTrack*)pTrack)->WriteHint(duration, isSyncSample);
4017 }
4018 
ConvertFromMovieDuration(MP4Duration duration,uint32_t timeScale)4019 uint64_t MP4File::ConvertFromMovieDuration(
4020     MP4Duration duration,
4021     uint32_t timeScale)
4022 {
4023     return MP4ConvertTime((uint64_t)duration,
4024                           GetTimeScale(), timeScale);
4025 }
4026 
ConvertFromTrackTimestamp(MP4TrackId trackId,MP4Timestamp timeStamp,uint32_t timeScale)4027 uint64_t MP4File::ConvertFromTrackTimestamp(
4028     MP4TrackId trackId,
4029     MP4Timestamp timeStamp,
4030     uint32_t timeScale)
4031 {
4032     return MP4ConvertTime(timeStamp,
4033                           GetTrackTimeScale(trackId), timeScale);
4034 }
4035 
ConvertToTrackTimestamp(MP4TrackId trackId,uint64_t timeStamp,uint32_t timeScale)4036 MP4Timestamp MP4File::ConvertToTrackTimestamp(
4037     MP4TrackId trackId,
4038     uint64_t timeStamp,
4039     uint32_t timeScale)
4040 {
4041     return (MP4Timestamp)MP4ConvertTime(timeStamp,
4042                                         timeScale, GetTrackTimeScale(trackId));
4043 }
4044 
ConvertFromTrackDuration(MP4TrackId trackId,MP4Duration duration,uint32_t timeScale)4045 uint64_t MP4File::ConvertFromTrackDuration(
4046     MP4TrackId trackId,
4047     MP4Duration duration,
4048     uint32_t timeScale)
4049 {
4050     return MP4ConvertTime((uint64_t)duration,
4051                           GetTrackTimeScale(trackId), timeScale);
4052 }
4053 
ConvertToTrackDuration(MP4TrackId trackId,uint64_t duration,uint32_t timeScale)4054 MP4Duration MP4File::ConvertToTrackDuration(
4055     MP4TrackId trackId,
4056     uint64_t duration,
4057     uint32_t timeScale)
4058 {
4059     return (MP4Duration)MP4ConvertTime(duration,
4060                                        timeScale, GetTrackTimeScale(trackId));
4061 }
4062 
ConvertTrackTypeToStreamType(const char * trackType)4063 uint8_t MP4File::ConvertTrackTypeToStreamType(const char* trackType)
4064 {
4065     uint8_t streamType;
4066 
4067     if (!strcmp(trackType, MP4_OD_TRACK_TYPE)) {
4068         streamType = MP4ObjectDescriptionStreamType;
4069     } else if (!strcmp(trackType, MP4_SCENE_TRACK_TYPE)) {
4070         streamType = MP4SceneDescriptionStreamType;
4071     } else if (!strcmp(trackType, MP4_CLOCK_TRACK_TYPE)) {
4072         streamType = MP4ClockReferenceStreamType;
4073     } else if (!strcmp(trackType, MP4_MPEG7_TRACK_TYPE)) {
4074         streamType = MP4Mpeg7StreamType;
4075     } else if (!strcmp(trackType, MP4_OCI_TRACK_TYPE)) {
4076         streamType = MP4OCIStreamType;
4077     } else if (!strcmp(trackType, MP4_IPMP_TRACK_TYPE)) {
4078         streamType = MP4IPMPStreamType;
4079     } else if (!strcmp(trackType, MP4_MPEGJ_TRACK_TYPE)) {
4080         streamType = MP4MPEGJStreamType;
4081     } else {
4082         streamType = MP4UserPrivateStreamType;
4083     }
4084 
4085     return streamType;
4086 }
4087 
4088 // edit list
4089 
MakeTrackEditName(MP4TrackId trackId,MP4EditId editId,const char * name)4090 char* MP4File::MakeTrackEditName(
4091     MP4TrackId trackId,
4092     MP4EditId editId,
4093     const char* name)
4094 {
4095     char* trakName = MakeTrackName(trackId, NULL);
4096 
4097     if (m_editName == NULL) {
4098         m_editName = (char *)malloc(1024);
4099         if (m_editName == NULL) return NULL;
4100     }
4101     snprintf(m_editName, 1024,
4102              "%s.edts.elst.entries[%u].%s",
4103              trakName, editId - 1, name);
4104     return m_editName;
4105 }
4106 
AddTrackEdit(MP4TrackId trackId,MP4EditId editId)4107 MP4EditId MP4File::AddTrackEdit(
4108     MP4TrackId trackId,
4109     MP4EditId editId)
4110 {
4111     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
4112     return m_pTracks[FindTrackIndex(trackId)]->AddEdit(editId);
4113 }
4114 
DeleteTrackEdit(MP4TrackId trackId,MP4EditId editId)4115 void MP4File::DeleteTrackEdit(
4116     MP4TrackId trackId,
4117     MP4EditId editId)
4118 {
4119     ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
4120     m_pTracks[FindTrackIndex(trackId)]->DeleteEdit(editId);
4121 }
4122 
GetTrackNumberOfEdits(MP4TrackId trackId)4123 uint32_t MP4File::GetTrackNumberOfEdits(
4124     MP4TrackId trackId)
4125 {
4126     return GetTrackIntegerProperty(trackId, "edts.elst.entryCount");
4127 }
4128 
GetTrackEditTotalDuration(MP4TrackId trackId,MP4EditId editId)4129 MP4Duration MP4File::GetTrackEditTotalDuration(
4130     MP4TrackId trackId,
4131     MP4EditId editId)
4132 {
4133     return m_pTracks[FindTrackIndex(trackId)]->GetEditTotalDuration(editId);
4134 }
4135 
GetTrackEditStart(MP4TrackId trackId,MP4EditId editId)4136 MP4Timestamp MP4File::GetTrackEditStart(
4137     MP4TrackId trackId,
4138     MP4EditId editId)
4139 {
4140     return m_pTracks[FindTrackIndex(trackId)]->GetEditStart(editId);
4141 }
4142 
GetTrackEditMediaStart(MP4TrackId trackId,MP4EditId editId)4143 MP4Timestamp MP4File::GetTrackEditMediaStart(
4144     MP4TrackId trackId,
4145     MP4EditId editId)
4146 {
4147     return GetIntegerProperty(
4148                MakeTrackEditName(trackId, editId, "mediaTime"));
4149 }
4150 
SetTrackEditMediaStart(MP4TrackId trackId,MP4EditId editId,MP4Timestamp startTime)4151 void MP4File::SetTrackEditMediaStart(
4152     MP4TrackId trackId,
4153     MP4EditId editId,
4154     MP4Timestamp startTime)
4155 {
4156     SetIntegerProperty(
4157         MakeTrackEditName(trackId, editId, "mediaTime"),
4158         startTime);
4159 }
4160 
GetTrackEditDuration(MP4TrackId trackId,MP4EditId editId)4161 MP4Duration MP4File::GetTrackEditDuration(
4162     MP4TrackId trackId,
4163     MP4EditId editId)
4164 {
4165     return GetIntegerProperty(
4166                MakeTrackEditName(trackId, editId, "segmentDuration"));
4167 }
4168 
SetTrackEditDuration(MP4TrackId trackId,MP4EditId editId,MP4Duration duration)4169 void MP4File::SetTrackEditDuration(
4170     MP4TrackId trackId,
4171     MP4EditId editId,
4172     MP4Duration duration)
4173 {
4174     SetIntegerProperty(
4175         MakeTrackEditName(trackId, editId, "segmentDuration"),
4176         duration);
4177 }
4178 
GetTrackEditDwell(MP4TrackId trackId,MP4EditId editId)4179 bool MP4File::GetTrackEditDwell(
4180     MP4TrackId trackId,
4181     MP4EditId editId)
4182 {
4183     return (GetIntegerProperty(
4184                 MakeTrackEditName(trackId, editId, "mediaRate")) == 0);
4185 }
4186 
SetTrackEditDwell(MP4TrackId trackId,MP4EditId editId,bool dwell)4187 void MP4File::SetTrackEditDwell(
4188     MP4TrackId trackId,
4189     MP4EditId editId,
4190     bool dwell)
4191 {
4192     SetIntegerProperty(
4193         MakeTrackEditName(trackId, editId, "mediaRate"),
4194         (dwell ? 0 : 1));
4195 }
4196 
GetSampleIdFromEditTime(MP4TrackId trackId,MP4Timestamp when,MP4Timestamp * pStartTime,MP4Duration * pDuration)4197 MP4SampleId MP4File::GetSampleIdFromEditTime(
4198     MP4TrackId trackId,
4199     MP4Timestamp when,
4200     MP4Timestamp* pStartTime,
4201     MP4Duration* pDuration)
4202 {
4203     return m_pTracks[FindTrackIndex(trackId)]->GetSampleIdFromEditTime(
4204                when, pStartTime, pDuration);
4205 }
4206 
GetTrackDurationPerChunk(MP4TrackId trackId)4207 MP4Duration MP4File::GetTrackDurationPerChunk( MP4TrackId trackId )
4208 {
4209     return m_pTracks[FindTrackIndex(trackId)]->GetDurationPerChunk();
4210 }
4211 
SetTrackDurationPerChunk(MP4TrackId trackId,MP4Duration duration)4212 void MP4File::SetTrackDurationPerChunk( MP4TrackId trackId, MP4Duration duration )
4213 {
4214     m_pTracks[FindTrackIndex(trackId)]->SetDurationPerChunk( duration );
4215 }
4216 
CopySample(MP4File * srcFile,MP4TrackId srcTrackId,MP4SampleId srcSampleId,MP4File * dstFile,MP4TrackId dstTrackId,MP4Duration dstSampleDuration)4217 void MP4File::CopySample(
4218     MP4File*    srcFile,
4219     MP4TrackId  srcTrackId,
4220     MP4SampleId srcSampleId,
4221     MP4File*    dstFile,
4222     MP4TrackId  dstTrackId,
4223     MP4Duration dstSampleDuration )
4224 {
4225     // Note: we leave it up to the caller to ensure that the
4226     // source and destination tracks are compatible.
4227     // i.e. copying audio samples into a video track
4228     // is unlikely to do anything useful
4229 
4230     uint8_t* pBytes = NULL;
4231     uint32_t numBytes = 0;
4232     MP4Duration sampleDuration;
4233     MP4Duration renderingOffset;
4234     bool isSyncSample;
4235     bool hasDependencyFlags;
4236     uint32_t dependencyFlags;
4237 
4238     srcFile->ReadSample(
4239          srcTrackId,
4240          srcSampleId,
4241          &pBytes,
4242          &numBytes,
4243          NULL,
4244          &sampleDuration,
4245          &renderingOffset,
4246          &isSyncSample,
4247          &hasDependencyFlags,
4248          &dependencyFlags );
4249 
4250     if( !dstFile )
4251         dstFile = srcFile;
4252 
4253     if( dstTrackId == MP4_INVALID_TRACK_ID )
4254         dstTrackId = srcTrackId;
4255 
4256     if( dstSampleDuration != MP4_INVALID_DURATION )
4257         sampleDuration = dstSampleDuration;
4258 
4259     if( hasDependencyFlags ) {
4260         dstFile->WriteSampleDependency(
4261             dstTrackId,
4262             pBytes,
4263             numBytes,
4264             sampleDuration,
4265             renderingOffset,
4266             isSyncSample,
4267             dependencyFlags );
4268     }
4269     else {
4270         dstFile->WriteSample(
4271             dstTrackId,
4272             pBytes,
4273             numBytes,
4274             sampleDuration,
4275             renderingOffset,
4276             isSyncSample );
4277     }
4278 
4279     free( pBytes );
4280 }
4281 
EncAndCopySample(MP4File * srcFile,MP4TrackId srcTrackId,MP4SampleId srcSampleId,encryptFunc_t encfcnp,uint32_t encfcnparam1,MP4File * dstFile,MP4TrackId dstTrackId,MP4Duration dstSampleDuration)4282 void MP4File::EncAndCopySample(
4283     MP4File*      srcFile,
4284     MP4TrackId    srcTrackId,
4285     MP4SampleId   srcSampleId,
4286     encryptFunc_t encfcnp,
4287     uint32_t      encfcnparam1,
4288     MP4File*      dstFile,
4289     MP4TrackId    dstTrackId,
4290     MP4Duration   dstSampleDuration )
4291 {
4292     // Note: we leave it up to the caller to ensure that the
4293     // source and destination tracks are compatible.
4294     // i.e. copying audio samples into a video track
4295     // is unlikely to do anything useful
4296 
4297     uint8_t* pBytes = NULL;
4298     uint32_t numBytes = 0;
4299     uint8_t* encSampleData = NULL;
4300     uint32_t encSampleLength = 0;
4301     MP4Duration sampleDuration;
4302     MP4Duration renderingOffset;
4303     bool isSyncSample;
4304     bool hasDependencyFlags;
4305     uint32_t dependencyFlags;
4306 
4307     ASSERT(srcFile);
4308     srcFile->ReadSample(
4309          srcTrackId,
4310          srcSampleId,
4311          &pBytes,
4312          &numBytes,
4313          NULL,
4314          &sampleDuration,
4315          &renderingOffset,
4316          &isSyncSample,
4317          &hasDependencyFlags,
4318          &dependencyFlags );
4319 
4320     if( !dstFile )
4321         dstFile = srcFile;
4322 
4323     ASSERT(dstFile);
4324 
4325     if( dstTrackId == MP4_INVALID_TRACK_ID )
4326         dstTrackId = srcTrackId;
4327 
4328     if( dstSampleDuration != MP4_INVALID_DURATION )
4329         sampleDuration = dstSampleDuration;
4330 
4331     //if( ismacrypEncryptSampleAddHeader( ismaCryptSId, numBytes, pBytes, &encSampleLength, &encSampleData ) != 0)
4332     if( encfcnp( encfcnparam1, numBytes, pBytes, &encSampleLength, &encSampleData ) != 0 )
4333         log.errorf("%s(%s,%s) Can't encrypt the sample and add its header %u",
4334                    __FUNCTION__, srcFile->GetFilename().c_str(), dstFile->GetFilename().c_str(), srcSampleId );
4335 
4336     if( hasDependencyFlags ) {
4337         dstFile->WriteSampleDependency(
4338             dstTrackId,
4339             pBytes,
4340             numBytes,
4341             sampleDuration,
4342             renderingOffset,
4343             isSyncSample,
4344             dependencyFlags );
4345     }
4346     else {
4347         dstFile->WriteSample(
4348             dstTrackId,
4349             encSampleData,
4350             encSampleLength,
4351             sampleDuration,
4352             renderingOffset,
4353             isSyncSample );
4354     }
4355 
4356     free( pBytes );
4357 
4358     if( encSampleData != NULL )
4359         free( encSampleData );
4360 }
4361 
4362 ///////////////////////////////////////////////////////////////////////////////
4363 
4364 }} // namespace mp4v2::impl
4365