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, ×tamp, 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