Skip to content

Commit 1f53d0e

Browse files
Kona8lendKona8lend
authored andcommitted
- switched MP4ItmfItem to use MP4Atom* instead of indexes;
this lets it survivie multiple removes/adds while supporting set operations. - added general description docs for iTMF Generic and Tags APIs. - moved responsibility for ilst empty cleanup to MP4File::FinishWrite(). - added moov.udta.meta empty cleanup. - added moov.udta empty cleanup.
1 parent b339d3d commit 1f53d0e

File tree

4 files changed

+176
-34
lines changed

4 files changed

+176
-34
lines changed

include/mp4v2/itmf_generic.h

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,65 @@
66
* @defgroup mp4_itmf_generic MP4v2 iTMF (iTunes Metadata Format) Generic
77
* @{
88
*
9+
* This is a low-level API used to manage iTMF metadata.
10+
*
11+
* It provides support for virtually any kind of iTMF metadata item,
12+
* including meaning atoms, sometimes referred to as reverse-DNS meanings.
13+
* Structures are directly modified; ie: there are no fuctions which
14+
* modify values for you. There is little type-safety, logic checks, or
15+
* specifications compliance checks. For these reasons it is recommended
16+
* to use iTMF Tags API when possible.
17+
*
18+
* At the heart of this API is an #MP4ItmfItem which corresponds to an
19+
* iTMF metadata item atom. The item, and any recursive data structures
20+
* contained within require <b>manual</b> memory management. The general
21+
* rule to follow is that you must always check/free a ptr if you intend
22+
* to resize data. In cases where you know the existing data size is
23+
* exactly what is needed, you may overwrite the buffer contents.
24+
*
25+
* Each item always has at least 1 data elements which corresponds to
26+
* a data atom. Additionally, each item has optional <b>mean</b> and
27+
* <b>name</b> values which correspond to mean and name atoms.
28+
*
29+
* Each #MP4ItmfItem has a list of #MP4ItmfData. Similarily, care must
30+
* be taken to manage memory with one key difference; these structures
31+
* also have a valueSize field. If value is NULL then set valueSize=0.
32+
* Otherwise, set valueSize to the size (in bytes) of value buffer.
33+
*
34+
* In rare cases where the number of data elements in a single item
35+
* is > 1, the user must manually free/alloc/copy the <b>elements</b>
36+
* buffer and update <b>size</b> accordingly.
37+
*
38+
* The mp4 file structure is modified only when MP4AddItem(),
39+
* MP4SetItem() and MP4RemoveItem() are used. Simply free'ing
40+
* the item list does not modify the mp4 file.
41+
*
42+
* <b>iTMF Generic read workflow:</b>
43+
*
44+
* @li MP4ItmfGetItems()
45+
* @li inspect each item...
46+
* @li MP4ItmfItemListFree()
47+
*
48+
* <b>iTMF Generic read/modify/remove workflow:</b>
49+
*
50+
* @li MP4ItmfGetItems()
51+
* @li inspect/modify item...
52+
* @li MP4ItmfSetItem() each modified item...
53+
* @li MP4ItmfRemoveItem()...
54+
* @li MP4ItmfItemListFree()
55+
*
56+
* <b>iTMF Generic add workflow:</b>
57+
*
58+
* @li MP4ItmfItemAlloc()
59+
* @li MP4ItmfAddItem()
60+
* @li MP4ItmfItemFree()
61+
*
62+
* @par Warning:
63+
* Care must be taken when using multiple mechanisms to modify an open mp4
64+
* file as it is not thread-safe, nor does it permit overlapping different
65+
* API workflows which have a begin/end to their workflow. That is to say
66+
* do not interleave an iTMF Generic workflow with an iTMF Tags workflow.
67+
*
968
*****************************************************************************/
1069

1170
/** Basic types of value data as enumerated in spec. */
@@ -59,7 +118,8 @@ typedef struct MP4ItmfDataList_s
59118
*/
60119
typedef struct MP4ItmfItem_s
61120
{
62-
int32_t index; /**< 0-based index of item in ilst container. -1 if undefined. */
121+
void* __handle; /**< internal use only. */
122+
63123
char* code; /**< four-char code identifing atom type. NULL-terminated. */
64124
char* mean; /**< may be NULL. UTF-8 meaning. NULL-terminated. */
65125
char* name; /**< may be NULL. UTF-8 name. NULL-terminated. */

include/mp4v2/itmf_tags.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,51 @@
66
* @defgroup mp4_itmf_tags MP4v2 iTMF (iTunes Metadata Format) Tags
77
* @{
88
*
9+
* This is a high-level API used to manage iTMF metadata.
10+
*
11+
* It provides more type-safety and simplified memory management as compared
12+
* to iTMF Generic API.
13+
*
14+
* At the heart of this API is a read-only structure that holds all known
15+
* items and their current values. The value is always a pointer which if
16+
* NULL indicates its corresponding atom does not exist. Thus, one must
17+
* always check if the pointer is non-NULL before attempting to extract
18+
* its value.
19+
*
20+
* The structure may not be directly modified. Instead, <b>set</b> functions
21+
* corresponding to each item are used to modify the backing-store of
22+
* the read-only structure. Setting the value ptr to NULL will effectively
23+
* remove it. Setting the value ptr to real data will immediately make a
24+
* copy of the value in the backing-store and the read-only structure
25+
* will correctly reflect the change.
26+
*
27+
* The hidden data cache memory is automatically managed. Thus the user need
28+
* only guarantee the data is available during the lifetime of the set-function
29+
* call.
30+
*
31+
* <b>iTMF Tags read workflow:</b>
32+
*
33+
* @li MP4TagsAlloc()
34+
* @li MP4TagsFetch()
35+
* @li inspect each tag of interest...
36+
* @li MP4TagsStore() (if modified)
37+
* @li MP4TagsFree()
38+
*
39+
* <b>iTMF Tags read/modify/add/remove workflow:</b>
40+
*
41+
* @li MP4TagsAlloc()
42+
* @li MP4TagsFetch()
43+
* @li inspect each tag of interest...
44+
* @li MP4TagsSetName(), MP4TagsSetArtist()...
45+
* @li MP4TagsStore()
46+
* @li MP4TagsFree()
47+
*
48+
* @par Warning:
49+
* Care must be taken when using multiple mechanisms to modify an open mp4
50+
* file as it is not thread-safe, nor does it permit overlapping different
51+
* API workflows which have a begin/end to their workflow. That is to say
52+
* do not interleave an iTMF Generic workflow with an iTMF Tags workflow.
53+
*
954
*****************************************************************************/
1055

1156
/** Enumeration of possible MP4TagArtwork::type values. */

src/itmf/generic.cpp

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,10 @@ __dataListResize( MP4ItmfDataList& list, uint32_t size )
8383
void
8484
__itemInit( MP4ItmfItem& item )
8585
{
86-
item.index = -1;
87-
item.code = NULL;
88-
item.mean = NULL;
89-
item.name = NULL;
86+
item.__handle = NULL;
87+
item.code = NULL;
88+
item.mean = NULL;
89+
item.name = NULL;
9090

9191
__dataListInit( item.dataList );
9292
}
@@ -151,11 +151,11 @@ __itemListAlloc()
151151
///////////////////////////////////////////////////////////////////////////////
152152

153153
static bool
154-
__itemAtomToModel( uint32_t index, MP4ItemAtom& item_atom, MP4ItmfItem& model )
154+
__itemAtomToModel( MP4ItemAtom& item_atom, MP4ItmfItem& model )
155155
{
156156
__itemClear( model );
157-
model.index = index;
158-
model.code = strdup( item_atom.GetType() );
157+
model.__handle = &item_atom;
158+
model.code = strdup( item_atom.GetType() );
159159

160160
// handle special meaning atom
161161
if( ATOMID( item_atom.GetType() ) == ATOMID( "----" )) {
@@ -302,7 +302,7 @@ genericGetItems( MP4File& file )
302302
__itemListResize( list, itemCount );
303303

304304
for( uint32_t i = 0; i < list.size; i++ )
305-
__itemAtomToModel( i, *(MP4ItemAtom*)ilst->GetChildAtom( i ), list.elements[i] );
305+
__itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( i ), list.elements[i] );
306306

307307
return &list;
308308
}
@@ -335,7 +335,7 @@ genericGetItemsByCode( MP4File& file, const string& code )
335335
const vector<uint32_t>::size_type max = indexList.size();
336336
for( vector<uint32_t>::size_type i = 0; i < max; i++ ) {
337337
uint32_t& aidx = indexList[i];
338-
__itemAtomToModel( aidx, *(MP4ItemAtom*)ilst->GetChildAtom( aidx ), list.elements[i] );
338+
__itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( aidx ), list.elements[i] );
339339
}
340340

341341
return &list;
@@ -387,7 +387,7 @@ genericGetItemsByMeaning( MP4File& file, const string& meaning, const string& na
387387
const vector<uint32_t>::size_type max = indexList.size();
388388
for( vector<uint32_t>::size_type i = 0; i < max; i++ ) {
389389
uint32_t& aidx = indexList[i];
390-
__itemAtomToModel( aidx, *(MP4ItemAtom*)ilst->GetChildAtom( aidx ), list.elements[i] );
390+
__itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( aidx ), list.elements[i] );
391391
}
392392

393393
return &list;
@@ -416,24 +416,32 @@ genericAddItem( MP4File& file, const MP4ItmfItem* item )
416416
bool
417417
genericSetItem( MP4File& file, const MP4ItmfItem* item )
418418
{
419-
if( item->index == -1 )
419+
if( !item->__handle )
420420
return false;
421421

422422
MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" );
423423
if( !ilst )
424424
return false;
425425

426-
if( (uint32_t)item->index >= ilst->GetNumberOfChildAtoms() )
427-
return genericAddItem( file, item );
428-
429-
MP4ItemAtom* old = (MP4ItemAtom*)ilst->GetChildAtom( item->index );
430-
if( old ) {
431-
ilst->DeleteChildAtom( ilst->GetChildAtom( item->index ));
432-
delete old;
426+
MP4ItemAtom* const old = static_cast<MP4ItemAtom*>(item->__handle);
427+
const uint32_t childCount = ilst->GetNumberOfChildAtoms();
428+
uint32_t fidx = numeric_limits<uint32_t>::max();
429+
for( uint32_t i = 0; i < childCount; i++ ) {
430+
MP4Atom* atom = ilst->GetChildAtom( i );
431+
if( atom == old ) {
432+
fidx = i;
433+
break;
434+
}
433435
}
434436

437+
if( fidx == numeric_limits<uint32_t>::max() )
438+
return false;
439+
440+
ilst->DeleteChildAtom( old );
441+
delete old;
442+
435443
MP4ItemAtom& itemAtom = *(MP4ItemAtom*)MP4Atom::CreateAtom( ilst, item->code );
436-
ilst->InsertChildAtom( &itemAtom, item->index );
444+
ilst->InsertChildAtom( &itemAtom, fidx );
437445

438446
return __itemModelToAtom( *item, itemAtom );
439447
}
@@ -443,26 +451,16 @@ genericSetItem( MP4File& file, const MP4ItmfItem* item )
443451
bool
444452
genericRemoveItem( MP4File& file, const MP4ItmfItem* item )
445453
{
446-
if( item->index == -1 )
454+
if( !item->__handle )
447455
return false;
448456

449457
MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" );
450458
if( !ilst )
451459
return false;
452460

453-
if( (uint32_t)item->index >= ilst->GetNumberOfChildAtoms() )
454-
return false;
455-
456-
MP4ItemAtom* itemAtom = (MP4ItemAtom*)ilst->GetChildAtom( item->index );
457-
if( itemAtom ) {
458-
ilst->DeleteChildAtom( ilst->GetChildAtom( item->index ));
459-
delete itemAtom;
460-
}
461-
462-
if( ilst->GetNumberOfChildAtoms() == 0 ) {
463-
ilst->GetParentAtom()->DeleteChildAtom( ilst );
464-
delete ilst;
465-
}
461+
MP4ItemAtom* const old = static_cast<MP4ItemAtom*>(item->__handle);
462+
ilst->DeleteChildAtom( old );
463+
delete old;
466464

467465
return true;
468466
}

src/mp4file.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,45 @@ void MP4File::BeginWrite()
463463

464464
void MP4File::FinishWrite()
465465
{
466+
// remove empty moov.udta.meta.ilst
467+
{
468+
MP4Atom* ilst = FindAtom( "moov.udta.meta.ilst" );
469+
if( ilst ) {
470+
if( ilst->GetNumberOfChildAtoms() == 0 ) {
471+
ilst->GetParentAtom()->DeleteChildAtom( ilst );
472+
delete ilst;
473+
}
474+
}
475+
}
476+
477+
// remove empty moov.udta.meta
478+
{
479+
MP4Atom* meta = FindAtom( "moov.udta.meta" );
480+
if( meta ) {
481+
if( meta->GetNumberOfChildAtoms() == 0 ) {
482+
meta->GetParentAtom()->DeleteChildAtom( meta );
483+
delete meta;
484+
}
485+
else if( meta->GetNumberOfChildAtoms() == 1 ) {
486+
if( ATOMID( meta->GetChildAtom( 0 )->GetType() ) == ATOMID( "hdlr" )) {
487+
meta->GetParentAtom()->DeleteChildAtom( meta );
488+
delete meta;
489+
}
490+
}
491+
}
492+
}
493+
494+
// remove empty moov.udta
495+
{
496+
MP4Atom* udta = FindAtom( "moov.udta" );
497+
if( udta ) {
498+
if( udta->GetNumberOfChildAtoms() == 0 ) {
499+
udta->GetParentAtom()->DeleteChildAtom( udta );
500+
delete udta;
501+
}
502+
}
503+
}
504+
466505
// for all tracks, flush chunking buffers
467506
for( uint32_t i = 0; i < m_pTracks.Size(); i++ ) {
468507
ASSERT( m_pTracks[i] );

0 commit comments

Comments
 (0)