| /////////////////////////////////////////////////////////////////////////////// |
| // |
| // The contents of this file are subject to the Mozilla Public License |
| // Version 1.1 (the "License"); you may not use this file except in |
| // compliance with the License. You may obtain a copy of the License at |
| // http://www.mozilla.org/MPL/ |
| // |
| // Software distributed under the License is distributed on an "AS IS" |
| // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the |
| // License for the specific language governing rights and limitations |
| // under the License. |
| // |
| // The Original Code is MP4v2. |
| // |
| // The Initial Developer of the Original Code is Kona Blend. |
| // Portions created by Kona Blend are Copyright (C) 2008. |
| // All Rights Reserved. |
| // |
| // Contributors: |
| // Kona Blend, kona8lend@@gmail.com |
| // |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| #include "impl.h" |
| |
| namespace mp4v2 { namespace impl { namespace itmf { namespace { |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void |
| __dataInit( MP4ItmfData& data ) |
| { |
| data.typeSetIdentifier = 0; |
| data.typeCode = MP4_ITMF_BT_IMPLICIT; |
| data.locale = 0; |
| data.value = NULL; |
| data.valueSize = 0; |
| } |
| |
| void |
| __dataClear( MP4ItmfData& data ) |
| { |
| if( data.value ) |
| free( data.value ); |
| __dataInit( data ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void |
| __dataListInit( MP4ItmfDataList& list ) |
| { |
| list.elements = NULL; |
| list.size = 0; |
| } |
| |
| void |
| __dataListClear( MP4ItmfDataList& list ) |
| { |
| if( list.elements ) { |
| for( uint32_t i = 0; i < list.size; i++ ) |
| __dataClear( list.elements[i] ); |
| free( list.elements ); |
| } |
| |
| __dataListInit( list ); |
| } |
| |
| void |
| __dataListResize( MP4ItmfDataList& list, uint32_t size ) |
| { |
| __dataListClear( list ); |
| |
| list.elements = (MP4ItmfData*)malloc( sizeof( MP4ItmfData ) * size ); |
| list.size = size; |
| |
| for( uint32_t i = 0; i < size; i++ ) |
| __dataInit( list.elements[i] ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void |
| __itemInit( MP4ItmfItem& item ) |
| { |
| item.__handle = NULL; |
| item.code = NULL; |
| item.mean = NULL; |
| item.name = NULL; |
| |
| __dataListInit( item.dataList ); |
| } |
| |
| void |
| __itemClear( MP4ItmfItem& item ) |
| { |
| if( item.code ) |
| free( item.code ); |
| if( item.mean ) |
| free( item.mean ); |
| if( item.name ) |
| free( item.name ); |
| |
| __dataListClear( item.dataList ); |
| __itemInit( item ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void |
| __itemListInit( MP4ItmfItemList& list ) |
| { |
| list.elements = NULL; |
| list.size = 0; |
| } |
| |
| void |
| __itemListClear( MP4ItmfItemList& list ) |
| { |
| if( list.elements ) { |
| for( uint32_t i = 0; i < list.size; i++ ) |
| __itemClear( list.elements[i] ); |
| free( list.elements ); |
| } |
| |
| __itemListInit( list ); |
| } |
| |
| void |
| __itemListResize( MP4ItmfItemList& list, uint32_t size ) |
| { |
| __itemListClear( list ); |
| if( !size ) |
| return; |
| |
| list.elements = (MP4ItmfItem*)malloc( sizeof( MP4ItmfItem ) * size ); |
| list.size = size; |
| |
| for( uint32_t i = 0; i < size; i++ ) |
| __itemInit( list.elements[i] ); |
| } |
| |
| MP4ItmfItemList* |
| __itemListAlloc() |
| { |
| MP4ItmfItemList& list = *(MP4ItmfItemList*)malloc( sizeof( MP4ItmfItemList )); |
| __itemListInit( list ); |
| return &list; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static bool |
| __itemAtomToModel( MP4ItemAtom& item_atom, MP4ItmfItem& model ) |
| { |
| __itemClear( model ); |
| model.__handle = &item_atom; |
| model.code = strdup( item_atom.GetType() ); |
| |
| // handle special meaning atom |
| if( ATOMID( item_atom.GetType() ) == ATOMID( "----" )) { |
| // meaning is mandatory |
| MP4MeanAtom* mean = (MP4MeanAtom*)item_atom.FindAtom( "----.mean" ); |
| if( !mean ) |
| return true; |
| |
| // copy atom UTF-8 value (not NULL-terminated) to model (NULL-terminated) |
| model.mean = mean->value.GetValueStringAlloc(); |
| |
| // name is optional |
| MP4NameAtom* name = (MP4NameAtom*)item_atom.FindAtom( "----.name" ); |
| if( name ) { |
| // copy atom UTF-8 value (not NULL-terminated) to model (NULL-terminated) |
| model.name = name->value.GetValueStringAlloc(); |
| } |
| } |
| |
| // pass 1: count data atoms |
| const uint32_t childCount = item_atom.GetNumberOfChildAtoms(); |
| uint32_t dataCount = 0; |
| for( uint32_t i = 0; i < childCount; i++ ) { |
| if( ATOMID( item_atom.GetChildAtom( i )->GetType() ) != ATOMID( "data" )) |
| continue; |
| dataCount++; |
| } |
| |
| // one or more data atoms is mandatory |
| if( dataCount < 1 ) |
| return true; |
| |
| __dataListResize( model.dataList, dataCount ); |
| |
| // pass 2: populate data model |
| for( uint32_t i = 0, idata = 0; i < childCount; i++ ) { |
| MP4Atom* atom = item_atom.GetChildAtom( i ); |
| if( ATOMID( atom->GetType() ) != ATOMID( "data" )) |
| continue; |
| |
| MP4DataAtom& data_atom = *(MP4DataAtom*)atom; |
| MP4ItmfData& data_model = model.dataList.elements[idata]; |
| |
| data_model.typeSetIdentifier = data_atom.typeSetIdentifier.GetValue(); |
| data_model.typeCode = (MP4ItmfBasicType)data_atom.typeCode.GetValue(); |
| data_model.locale = data_atom.locale.GetValue(); |
| |
| data_atom.metadata.GetValue( &data_model.value, &data_model.valueSize ); |
| idata++; |
| } |
| |
| return false; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| static bool |
| __itemModelToAtom( const MP4ItmfItem& model, MP4ItemAtom& atom ) |
| { |
| if( ATOMID( atom.GetType() ) == ATOMID( "----" )) { |
| ASSERT( model.mean ); // mandatory |
| MP4MeanAtom& meanAtom = *(MP4MeanAtom*)MP4Atom::CreateAtom( atom.GetFile(), &atom, "mean" ); |
| atom.AddChildAtom( &meanAtom ); |
| meanAtom.value.SetValue( (const uint8_t*)model.mean, (uint32_t)strlen( model.mean )); |
| |
| if( model.name ) { |
| MP4NameAtom& nameAtom = *(MP4NameAtom*)MP4Atom::CreateAtom( atom.GetFile(), &atom, "name" ); |
| atom.AddChildAtom( &nameAtom ); |
| nameAtom.value.SetValue( (const uint8_t*)model.name, (uint32_t)strlen( model.name )); |
| } |
| } |
| |
| for( uint32_t i = 0; i < model.dataList.size; i++ ) { |
| MP4ItmfData& dataModel = model.dataList.elements[i]; |
| MP4DataAtom& dataAtom = *(MP4DataAtom*)MP4Atom::CreateAtom( atom.GetFile(), &atom, "data" ); |
| atom.AddChildAtom( &dataAtom ); |
| |
| dataAtom.typeSetIdentifier.SetValue( dataModel.typeSetIdentifier ); |
| dataAtom.typeCode.SetValue( (itmf::BasicType)dataModel.typeCode ); |
| dataAtom.locale.SetValue( dataModel.locale ); |
| dataAtom.metadata.SetValue( dataModel.value, dataModel.valueSize ); |
| } |
| |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| } // namespace anonymous |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| MP4ItmfItem* |
| genericItemAlloc( const string& code, uint32_t numData ) |
| { |
| MP4ItmfItem* item = (MP4ItmfItem*)malloc( sizeof( MP4ItmfItem )); |
| if( !item ) |
| return NULL; |
| |
| __itemInit( *item ); |
| item->code = strdup( code.c_str() ); |
| |
| // always create array size of 1 |
| __dataListResize( item->dataList, numData ); |
| |
| return item; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void |
| genericItemFree( MP4ItmfItem* item ) |
| { |
| if( !item ) |
| return; |
| |
| __itemClear( *item ); |
| free( item ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| void |
| genericItemListFree( MP4ItmfItemList* list ) |
| { |
| if( !list ) |
| return; |
| |
| __itemListClear( *list ); |
| free( list ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| MP4ItmfItemList* |
| genericGetItems( MP4File& file ) |
| { |
| MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| if( !ilst ) |
| return __itemListAlloc(); |
| |
| const uint32_t itemCount = ilst->GetNumberOfChildAtoms(); |
| if( itemCount < 1 ) |
| return __itemListAlloc(); |
| |
| MP4ItmfItemList& list = *__itemListAlloc(); |
| __itemListResize( list, itemCount ); |
| |
| for( uint32_t i = 0; i < list.size; i++ ) |
| __itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( i ), list.elements[i] ); |
| |
| return &list; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| MP4ItmfItemList* |
| genericGetItemsByCode( MP4File& file, const string& code ) |
| { |
| MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| if( !ilst ) |
| return __itemListAlloc(); |
| |
| // pass 1: filter by code and populate indexList |
| const uint32_t childCount = ilst->GetNumberOfChildAtoms(); |
| std::vector<uint32_t> indexList; |
| for( uint32_t i = 0; i < childCount; i++ ) { |
| if( ATOMID( ilst->GetChildAtom( i )->GetType() ) != ATOMID( code.c_str() )) |
| continue; |
| indexList.push_back( i ); |
| } |
| |
| if( indexList.size() < 1 ) |
| return __itemListAlloc(); |
| |
| MP4ItmfItemList& list = *__itemListAlloc(); |
| __itemListResize( list, (uint32_t)indexList.size() ); |
| |
| // pass 2: process each atom |
| const std::vector<uint32_t>::size_type max = indexList.size(); |
| for( std::vector<uint32_t>::size_type i = 0; i < max; i++ ) { |
| uint32_t& aidx = indexList[i]; |
| __itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( aidx ), list.elements[i] ); |
| } |
| |
| return &list; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| MP4ItmfItemList* |
| genericGetItemsByMeaning( MP4File& file, const string& meaning, const string& name ) |
| { |
| MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| if( !ilst ) |
| return __itemListAlloc(); |
| |
| // pass 1: filter by code and populate indexList |
| const uint32_t childCount = ilst->GetNumberOfChildAtoms(); |
| std::vector<uint32_t> indexList; |
| for( uint32_t i = 0; i < childCount; i++ ) { |
| MP4Atom& atom = *ilst->GetChildAtom( i ); |
| if( ATOMID( atom.GetType() ) != ATOMID( "----" )) |
| continue; |
| |
| // filter-out meaning mismatch |
| MP4MeanAtom* meanAtom = (MP4MeanAtom*)atom.FindAtom( "----.mean" ); |
| if( !meanAtom ) |
| continue; |
| if( meanAtom->value.CompareToString( meaning )) |
| continue; |
| |
| if( !name.empty() ) { |
| // filter-out name mismatch |
| MP4MeanAtom* nameAtom = (MP4MeanAtom*)atom.FindAtom( "----.name" ); |
| if( !nameAtom ) |
| continue; |
| if( nameAtom->value.CompareToString( name )) |
| continue; |
| } |
| |
| indexList.push_back( i ); |
| } |
| |
| if( indexList.size() < 1 ) |
| return __itemListAlloc(); |
| |
| MP4ItmfItemList& list = *__itemListAlloc(); |
| __itemListResize( list, (uint32_t)indexList.size() ); |
| |
| // pass 2: process each atom |
| const std::vector<uint32_t>::size_type max = indexList.size(); |
| for( std::vector<uint32_t>::size_type i = 0; i < max; i++ ) { |
| uint32_t& aidx = indexList[i]; |
| __itemAtomToModel( *(MP4ItemAtom*)ilst->GetChildAtom( aidx ), list.elements[i] ); |
| } |
| |
| return &list; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| bool |
| genericAddItem( MP4File& file, const MP4ItmfItem* item ) |
| { |
| if( !item ) |
| return false; |
| |
| MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| if( !ilst ) { |
| file.AddDescendantAtoms( "moov", "udta.meta.ilst" ); |
| ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| ASSERT( ilst ); |
| } |
| |
| MP4ItemAtom& itemAtom = *(MP4ItemAtom*)MP4Atom::CreateAtom( file, ilst, item->code ); |
| ilst->AddChildAtom( &itemAtom ); |
| |
| return __itemModelToAtom( *item, itemAtom ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| bool |
| genericSetItem( MP4File& file, const MP4ItmfItem* item ) |
| { |
| if( !item || !item->__handle ) |
| return false; |
| |
| MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| if( !ilst ) |
| return false; |
| |
| MP4ItemAtom* const old = static_cast<MP4ItemAtom*>(item->__handle); |
| const uint32_t childCount = ilst->GetNumberOfChildAtoms(); |
| uint32_t fidx = std::numeric_limits<uint32_t>::max(); |
| for( uint32_t i = 0; i < childCount; i++ ) { |
| MP4Atom* atom = ilst->GetChildAtom( i ); |
| if( atom == old ) { |
| fidx = i; |
| break; |
| } |
| } |
| |
| if( fidx == std::numeric_limits<uint32_t>::max() ) |
| return false; |
| |
| ilst->DeleteChildAtom( old ); |
| delete old; |
| |
| MP4ItemAtom& itemAtom = *(MP4ItemAtom*)MP4Atom::CreateAtom( file, ilst, item->code ); |
| ilst->InsertChildAtom( &itemAtom, fidx ); |
| |
| return __itemModelToAtom( *item, itemAtom ); |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| bool |
| genericRemoveItem( MP4File& file, const MP4ItmfItem* item ) |
| { |
| if( !item || !item->__handle ) |
| return false; |
| |
| MP4Atom* ilst = file.FindAtom( "moov.udta.meta.ilst" ); |
| if( !ilst ) |
| return false; |
| |
| MP4ItemAtom* const old = static_cast<MP4ItemAtom*>(item->__handle); |
| ilst->DeleteChildAtom( old ); |
| delete old; |
| |
| return true; |
| } |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| |
| }}} // namespace mp4v2::impl::itmf |