/* * 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 MPEG4IP. * * The Initial Developer of the Original Code is Cisco Systems Inc. * Portions created by Cisco Systems Inc. are * Copyright (C) Cisco Systems Inc. 2001 - 2004. All Rights Reserved. * * 3GPP features implementation is based on 3GPP's TS26.234-v5.60, * and was contributed by Ximpo Group Ltd. * * Portions created by Ximpo Group Ltd. are * Copyright (C) Ximpo Group Ltd. 2003, 2004. All Rights Reserved. * * Portions created by Adnecto d.o.o. are * Copyright (C) Adnecto d.o.o. 2005. All Rights Reserved * * Contributor(s): * Dave Mackie dmackie@cisco.com * Alix Marchandise-Franquet alix@cisco.com * Ximpo Group Ltd. mp4v2@ximpo.com * Danijel Kopcinovic danijel.kopcinovic@adnecto.net */ #include "src/impl.h" namespace mp4v2 { namespace impl { /////////////////////////////////////////////////////////////////////////////// MP4AtomInfo::MP4AtomInfo(const char* name, bool mandatory, bool onlyOne) { m_name = name; m_mandatory = mandatory; m_onlyOne = onlyOne; m_count = 0; } MP4Atom::MP4Atom(MP4File& file, const char* type) : m_File(file) { SetType(type); m_unknownType = false; m_start = 0; m_end = 0; m_largesizeMode = false; m_size = 0; m_pParentAtom = NULL; m_depth = 0xFF; } MP4Atom::~MP4Atom() { uint32_t i; for (i = 0; i < m_pProperties.Size(); i++) { delete m_pProperties[i]; } for (i = 0; i < m_pChildAtomInfos.Size(); i++) { delete m_pChildAtomInfos[i]; } for (i = 0; i < m_pChildAtoms.Size(); i++) { delete m_pChildAtoms[i]; } } MP4Atom* MP4Atom::CreateAtom( MP4File &file, MP4Atom* parent, const char* type ) { MP4Atom* atom = factory( file, parent, type ); ASSERT( atom ); return atom; } // generate a skeletal self void MP4Atom::Generate() { uint32_t i; // for all properties for (i = 0; i < m_pProperties.Size(); i++) { // ask it to self generate m_pProperties[i]->Generate(); } // for all mandatory, single child atom types for (i = 0; i < m_pChildAtomInfos.Size(); i++) { if (m_pChildAtomInfos[i]->m_mandatory && m_pChildAtomInfos[i]->m_onlyOne) { // create the mandatory, single child atom MP4Atom* pChildAtom = CreateAtom(m_File, this, m_pChildAtomInfos[i]->m_name); AddChildAtom(pChildAtom); // and ask it to self generate pChildAtom->Generate(); } } } MP4Atom* MP4Atom::ReadAtom(MP4File& file, MP4Atom* pParentAtom) { uint8_t hdrSize = 8; uint8_t extendedType[16]; uint64_t pos = file.GetPosition(); log.verbose1f("\"%s\": pos = 0x%" PRIx64, file.GetFilename().c_str(), pos); uint64_t dataSize = file.ReadUInt32(); char type[5]; file.ReadBytes((uint8_t*)&type[0], 4); type[4] = '\0'; // extended size const bool largesizeMode = (dataSize == 1); if (dataSize == 1) { dataSize = file.ReadUInt64(); hdrSize += 8; file.Check64BitStatus(type); } // extended type if (ATOMID(type) == ATOMID("uuid")) { file.ReadBytes(extendedType, sizeof(extendedType)); hdrSize += sizeof(extendedType); } if (dataSize == 0) { // extends to EOF dataSize = file.GetSize() - pos; } dataSize -= hdrSize; log.verbose1f("\"%s\": type = \"%s\" data-size = %" PRIu64 " (0x%" PRIx64 ") hdr %u", file.GetFilename().c_str(), type, dataSize, dataSize, hdrSize); if (pos + hdrSize + dataSize > pParentAtom->GetEnd()) { log.errorf("%s: \"%s\": invalid atom size, extends outside parent atom - skipping to end of \"%s\" \"%s\" %" PRIu64 " vs %" PRIu64, __FUNCTION__, file.GetFilename().c_str(), pParentAtom->GetType(), type, pos + hdrSize + dataSize, pParentAtom->GetEnd()); log.verbose1f("\"%s\": parent %s (%" PRIu64 ") pos %" PRIu64 " hdr %d data %" PRIu64 " sum %" PRIu64, file.GetFilename().c_str(), pParentAtom->GetType(), pParentAtom->GetEnd(), pos, hdrSize, dataSize, pos + hdrSize + dataSize); // skip to end of atom dataSize = pParentAtom->GetEnd() - pos - hdrSize; } MP4Atom* pAtom = CreateAtom(file, pParentAtom, type); // pAtom->SetFile(pFile); pAtom->SetStart(pos); pAtom->SetEnd(pos + hdrSize + dataSize); pAtom->SetLargesizeMode(largesizeMode); pAtom->SetSize(dataSize); if (ATOMID(type) == ATOMID("uuid")) { pAtom->SetExtendedType(extendedType); } if (pAtom->IsUnknownType()) { if (!IsReasonableType(pAtom->GetType())) { log.warningf("%s: \"%s\": atom type %s is suspect", __FUNCTION__, file.GetFilename().c_str(), pAtom->GetType()); } else { log.verbose1f("\"%s\": Info: atom type %s is unknown", file.GetFilename().c_str(), pAtom->GetType()); } if (dataSize > 0) { pAtom->AddProperty( new MP4BytesProperty(*pAtom, "data", dataSize)); } } pAtom->SetParentAtom(pParentAtom); try { pAtom->Read(); } catch (Exception* x) { // delete atom and rethrow so we don't leak memory. delete pAtom; throw x; } return pAtom; } bool MP4Atom::IsReasonableType(const char* type) { // Unwound this. Pricy when called a lot. if( isalnum((unsigned char) type[0])) { if( isalnum((unsigned char) type[1])) { if( isalnum((unsigned char) type[2])) { if( isalnum((unsigned char) type[3]) || type[3] == ' ' ) { return true; } } } } return false; } // generic read void MP4Atom::Read() { if (ATOMID(m_type) != 0 && m_size > 1000000) { log.verbose1f("%s: \"%s\": %s atom size %" PRIu64 " is suspect", __FUNCTION__, m_File.GetFilename().c_str(), m_type, m_size); } ReadProperties(); // read child atoms, if we expect there to be some if (m_pChildAtomInfos.Size() > 0) { ReadChildAtoms(); } Skip(); // to end of atom } void MP4Atom::Skip() { if (m_File.GetPosition() != m_end) { log.verbose1f("\"%s\": Skip: %" PRIu64 " bytes", m_File.GetFilename().c_str(), m_end - m_File.GetPosition()); } m_File.SetPosition(m_end); } MP4Atom* MP4Atom::FindAtom(const char* name) { if (!IsMe(name)) { return NULL; } if (!IsRootAtom()) { log.verbose1f("\"%s\": FindAtom: matched %s", GetFile().GetFilename().c_str(), name); name = MP4NameAfterFirst(name); // I'm the sought after atom if (name == NULL) { return this; } } // else it's one of my children return FindChildAtom(name); } bool MP4Atom::FindProperty(const char *name, MP4Property** ppProperty, uint32_t* pIndex) { if (!IsMe(name)) { return false; } if (!IsRootAtom()) { log.verbose1f("\"%s\": FindProperty: matched %s", GetFile().GetFilename().c_str(), name); name = MP4NameAfterFirst(name); // no property name given if (name == NULL) { return false; } } return FindContainedProperty(name, ppProperty, pIndex); } bool MP4Atom::IsMe(const char* name) { if (name == NULL) { return false; } // root atom always matches if (!strcmp(m_type, "")) { return true; } // check if our atom name is specified as the first component if (!MP4NameFirstMatches(m_type, name)) { return false; } return true; } MP4Atom* MP4Atom::FindChildAtom(const char* name) { uint32_t atomIndex = 0; // get the index if we have one, e.g. moov.trak[2].mdia... (void)MP4NameFirstIndex(name, &atomIndex); // need to get to the index'th child atom of the right type for (uint32_t i = 0; i < m_pChildAtoms.Size(); i++) { if (MP4NameFirstMatches(m_pChildAtoms[i]->GetType(), name)) { if (atomIndex == 0) { // this is the one, ask it to match return m_pChildAtoms[i]->FindAtom(name); } atomIndex--; } } return NULL; } bool MP4Atom::FindContainedProperty(const char *name, MP4Property** ppProperty, uint32_t* pIndex) { uint32_t numProperties = m_pProperties.Size(); uint32_t i; // check all of our properties for (i = 0; i < numProperties; i++) { if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) { return true; } } // not one of our properties, // presumably one of our children's properties // check child atoms... // check if we have an index, e.g. trak[2].mdia... uint32_t atomIndex = 0; (void)MP4NameFirstIndex(name, &atomIndex); // need to get to the index'th child atom of the right type for (i = 0; i < m_pChildAtoms.Size(); i++) { if (MP4NameFirstMatches(m_pChildAtoms[i]->GetType(), name)) { if (atomIndex == 0) { // this is the one, ask it to match return m_pChildAtoms[i]->FindProperty(name, ppProperty, pIndex); } atomIndex--; } } log.verbose1f("\"%s\": FindProperty: no match for %s", GetFile().GetFilename().c_str(), name); return false; } void MP4Atom::ReadProperties(uint32_t startIndex, uint32_t count) { uint32_t numProperties = min(count, m_pProperties.Size() - startIndex); // read any properties of the atom for (uint32_t i = startIndex; i < startIndex + numProperties; i++) { m_pProperties[i]->Read(m_File); if (m_File.GetPosition() > m_end) { log.verbose1f("ReadProperties: insufficient data for property: %s pos 0x%" PRIx64 " atom end 0x%" PRIx64, m_pProperties[i]->GetName(), m_File.GetPosition(), m_end); ostringstream oss; oss << "atom '" << GetType() << "' is too small; overrun at property: " << m_pProperties[i]->GetName(); throw new Exception( oss.str().c_str(), __FILE__, __LINE__, __FUNCTION__ ); } MP4LogLevel thisVerbosity = (m_pProperties[i]->GetType() == TableProperty) ? MP4_LOG_VERBOSE2 : MP4_LOG_VERBOSE1; if (log.verbosity >= thisVerbosity) { // log.printf(thisVerbosity,"Read: "); m_pProperties[i]->Dump(0, true); } } } void MP4Atom::ReadChildAtoms() { bool this_is_udta = ATOMID(m_type) == ATOMID("udta"); log.verbose1f("\"%s\": of %s", m_File.GetFilename().c_str(), m_type[0] ? m_type : "root"); for (uint64_t position = m_File.GetPosition(); position < m_end; position = m_File.GetPosition()) { // make sure that we have enough to read at least 8 bytes // size and type. if (m_end - position < 2 * sizeof(uint32_t)) { // if we're reading udta, it's okay to have 4 bytes of 0 if (this_is_udta && m_end - position == sizeof(uint32_t)) { uint32_t mbz = m_File.ReadUInt32(); if (mbz != 0) { log.warningf("%s: \"%s\": In udta atom, end value is not zero %x", __FUNCTION__, m_File.GetFilename().c_str(), mbz); } continue; } // otherwise, output a warning, but don't care log.warningf("%s: \"%s\": In %s atom, extra %" PRId64 " bytes at end of atom", __FUNCTION__, m_File.GetFilename().c_str(), m_type, (m_end - position)); for (uint64_t ix = 0; ix < m_end - position; ix++) { (void)m_File.ReadUInt8(); } continue; } MP4Atom* pChildAtom = MP4Atom::ReadAtom(m_File, this); AddChildAtom(pChildAtom); MP4AtomInfo* pChildAtomInfo = FindAtomInfo(pChildAtom->GetType()); // if child atom is of known type // but not expected here print warning if (pChildAtomInfo == NULL && !pChildAtom->IsUnknownType()) { log.verbose1f("%s: \"%s\": In atom %s unexpected child atom %s", __FUNCTION__, m_File.GetFilename().c_str(), GetType(), pChildAtom->GetType()); } // if child atoms should have just one instance // and this is more than one, print warning if (pChildAtomInfo) { pChildAtomInfo->m_count++; if (pChildAtomInfo->m_onlyOne && pChildAtomInfo->m_count > 1) { log.warningf("%s: \"%s\": In atom %s multiple child atoms %s", __FUNCTION__, m_File.GetFilename().c_str(), GetType(), pChildAtom->GetType()); } } } // if mandatory child atom doesn't exist, print warning uint32_t numAtomInfo = m_pChildAtomInfos.Size(); for (uint32_t i = 0; i < numAtomInfo; i++) { if (m_pChildAtomInfos[i]->m_mandatory && m_pChildAtomInfos[i]->m_count == 0) { log.warningf("%s: \"%s\": In atom %s missing child atom %s", __FUNCTION__, m_File.GetFilename().c_str(), GetType(), m_pChildAtomInfos[i]->m_name); } } log.verbose1f("\"%s\": finished %s", m_File.GetFilename().c_str(), m_type); } MP4AtomInfo* MP4Atom::FindAtomInfo(const char* name) { uint32_t numAtomInfo = m_pChildAtomInfos.Size(); for (uint32_t i = 0; i < numAtomInfo; i++) { if (ATOMID(m_pChildAtomInfos[i]->m_name) == ATOMID(name)) { return m_pChildAtomInfos[i]; } } return NULL; } // generic write void MP4Atom::Write() { BeginWrite(); WriteProperties(); WriteChildAtoms(); FinishWrite(); } void MP4Atom::Rewrite() { if (!m_end) { // This atom hasn't been written yet... return; } uint64_t fPos = m_File.GetPosition(); m_File.SetPosition(GetStart()); Write(); m_File.SetPosition(fPos); } void MP4Atom::BeginWrite(bool use64) { m_start = m_File.GetPosition(); //use64 = m_File.Use64Bits(); if (use64) { m_File.WriteUInt32(1); } else { m_File.WriteUInt32(0); } m_File.WriteBytes((uint8_t*)&m_type[0], 4); if (use64) { m_File.WriteUInt64(0); } if (ATOMID(m_type) == ATOMID("uuid")) { m_File.WriteBytes(m_extendedType, sizeof(m_extendedType)); } } void MP4Atom::FinishWrite(bool use64) { m_end = m_File.GetPosition(); m_size = (m_end - m_start); log.verbose1f("end: type %s %" PRIu64 " %" PRIu64 " size %" PRIu64, m_type,m_start, m_end, m_size); //use64 = m_File.Use64Bits(); if (use64) { m_File.SetPosition(m_start + 8); m_File.WriteUInt64(m_size); } else { ASSERT(m_size <= (uint64_t)0xFFFFFFFF); m_File.SetPosition(m_start); m_File.WriteUInt32(m_size); } m_File.SetPosition(m_end); // adjust size to just reflect data portion of atom m_size -= (use64 ? 16 : 8); if (ATOMID(m_type) == ATOMID("uuid")) { m_size -= sizeof(m_extendedType); } } void MP4Atom::WriteProperties(uint32_t startIndex, uint32_t count) { uint32_t numProperties = min(count, m_pProperties.Size() - startIndex); log.verbose1f("Write: \"%s\": type %s", m_File.GetFilename().c_str(), m_type); for (uint32_t i = startIndex; i < startIndex + numProperties; i++) { m_pProperties[i]->Write(m_File); MP4LogLevel thisVerbosity = (m_pProperties[i]->GetType() == TableProperty) ? MP4_LOG_VERBOSE2 : MP4_LOG_VERBOSE1; if (log.verbosity >= thisVerbosity) { log.printf(thisVerbosity,"Write: "); m_pProperties[i]->Dump(0, false); } } } void MP4Atom::WriteChildAtoms() { uint32_t size = m_pChildAtoms.Size(); for (uint32_t i = 0; i < size; i++) { m_pChildAtoms[i]->Write(); } log.verbose1f("Write: \"%s\": finished %s", m_File.GetFilename().c_str(), m_type); } void MP4Atom::AddProperty(MP4Property* pProperty) { ASSERT(pProperty); m_pProperties.Add(pProperty); } void MP4Atom::AddVersionAndFlags() { AddProperty(new MP4Integer8Property(*this, "version")); AddProperty(new MP4Integer24Property(*this, "flags")); } void MP4Atom::AddReserved(MP4Atom& parentAtom, const char* name, uint32_t size) { MP4BytesProperty* pReserved = new MP4BytesProperty(parentAtom, name, size); pReserved->SetReadOnly(); AddProperty(pReserved); } void MP4Atom::ExpectChildAtom(const char* name, bool mandatory, bool onlyOne) { m_pChildAtomInfos.Add(new MP4AtomInfo(name, mandatory, onlyOne)); } uint8_t MP4Atom::GetVersion() { if (strcmp("version", m_pProperties[0]->GetName())) { return 0; } return ((MP4Integer8Property*)m_pProperties[0])->GetValue(); } void MP4Atom::SetVersion(uint8_t version) { if (strcmp("version", m_pProperties[0]->GetName())) { return; } ((MP4Integer8Property*)m_pProperties[0])->SetValue(version); } uint32_t MP4Atom::GetFlags() { if (strcmp("flags", m_pProperties[1]->GetName())) { return 0; } return ((MP4Integer24Property*)m_pProperties[1])->GetValue(); } void MP4Atom::SetFlags(uint32_t flags) { if (strcmp("flags", m_pProperties[1]->GetName())) { return; } ((MP4Integer24Property*)m_pProperties[1])->SetValue(flags); } void MP4Atom::Dump(uint8_t indent, bool dumpImplicits) { if ( m_type[0] != '\0' ) { // create list of ancestors list tlist; for( MP4Atom* atom = this; atom; atom = atom->GetParentAtom() ) { const char* const type = atom->GetType(); if( type && type[0] != '\0' ) tlist.push_front( type ); } // create contextual atom-name string can; const list::iterator ie = tlist.end(); for( list::iterator it = tlist.begin(); it != ie; it++ ) can += *it + '.'; if( can.length() ) can.resize( can.length() - 1 ); log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": type %s (%s)", GetFile().GetFilename().c_str(), m_type, can.c_str() ); } uint32_t i; uint32_t size; // dump our properties size = m_pProperties.Size(); for (i = 0; i < size; i++) { /* skip details of tables unless we're told to be verbose */ if (m_pProperties[i]->GetType() == TableProperty && (log.verbosity < MP4_LOG_VERBOSE2)) { log.dump(indent + 1, MP4_LOG_VERBOSE1, "\"%s\": ", GetFile().GetFilename().c_str() ); continue; } m_pProperties[i]->Dump(indent + 1, dumpImplicits); } // dump our children size = m_pChildAtoms.Size(); for (i = 0; i < size; i++) { m_pChildAtoms[i]->Dump(indent + 1, dumpImplicits); } } uint8_t MP4Atom::GetDepth() { if (m_depth < 0xFF) { return m_depth; } MP4Atom *pAtom = this; m_depth = 0; while ((pAtom = pAtom->GetParentAtom()) != NULL) { m_depth++; ASSERT(m_depth < 255); } return m_depth; } bool MP4Atom::GetLargesizeMode() { return m_largesizeMode; } void MP4Atom::SetLargesizeMode( bool mode ) { m_largesizeMode = mode; } bool MP4Atom::descendsFrom( MP4Atom* parent, const char* type ) { const uint32_t id = ATOMID( type ); for( MP4Atom* atom = parent; atom; atom = atom->GetParentAtom() ) { if( id == ATOMID(atom->GetType()) ) return true; } return false; } // UDTA child atom types to be constructed as MP4UdtaElementAtom. // List gleaned from QTFF 2007-09-04. static const char* const UDTA_ELEMENTS[] = { "\xA9" "arg", "\xA9" "ark", "\xA9" "cok", "\xA9" "com", "\xA9" "cpy", "\xA9" "day", "\xA9" "dir", "\xA9" "ed1", "\xA9" "ed2", "\xA9" "ed3", "\xA9" "ed4", "\xA9" "ed5", "\xA9" "ed6", "\xA9" "ed7", "\xA9" "ed8", "\xA9" "ed9", "\xA9" "fmt", "\xA9" "inf", "\xA9" "isr", "\xA9" "lab", "\xA9" "lal", "\xA9" "mak", "\xA9" "nak", "\xA9" "nam", "\xA9" "pdk", "\xA9" "phg", "\xA9" "prd", "\xA9" "prf", "\xA9" "prk", "\xA9" "prl", "\xA9" "req", "\xA9" "snk", "\xA9" "snm", "\xA9" "src", "\xA9" "swf", "\xA9" "swk", "\xA9" "swr", "\xA9" "wrt", "Allf", "name", "LOOP", "ptv ", "SelO", "WLOC", NULL // must be last }; MP4Atom* MP4Atom::factory( MP4File &file, MP4Atom* parent, const char* type ) { // type may be NULL only in case of root-atom if( !type ) return new MP4RootAtom(file); // construct atoms which are context-savvy if( parent ) { const char* const ptype = parent->GetType(); if( descendsFrom( parent, "ilst" )) { if( ATOMID( ptype ) == ATOMID( "ilst" )) return new MP4ItemAtom( file, type ); if( ATOMID( type ) == ATOMID( "data" )) return new MP4DataAtom(file); if( ATOMID( ptype ) == ATOMID( "----" )) { if( ATOMID( type ) == ATOMID( "mean" )) return new MP4MeanAtom(file); if( ATOMID( type ) == ATOMID( "name" )) return new MP4NameAtom(file); } } else if( ATOMID( ptype ) == ATOMID( "meta" )) { if( ATOMID( type ) == ATOMID( "hdlr" )) return new MP4ItmfHdlrAtom(file); } else if( ATOMID( ptype ) == ATOMID( "udta" )) { if( ATOMID( type ) == ATOMID( "hnti" )) return new MP4HntiAtom(file); if( ATOMID( type ) == ATOMID( "hinf" )) return new MP4HinfAtom(file); for( const char* const* p = UDTA_ELEMENTS; *p; p++ ) if( !strcmp( type, *p )) return new MP4UdtaElementAtom( file, type ); } } // no-context construction (old-style) switch( (uint8_t)type[0] ) { case 'S': if( ATOMID(type) == ATOMID("SVQ3") ) return new MP4VideoAtom( file, type ); if( ATOMID(type) == ATOMID("SMI ") ) return new MP4SmiAtom(file); break; case 'a': if( ATOMID(type) == ATOMID("avc1") ) return new MP4Avc1Atom(file); if( ATOMID(type) == ATOMID("ac-3") ) return new MP4Ac3Atom(file); if( ATOMID(type) == ATOMID("avcC") ) return new MP4AvcCAtom(file); if( ATOMID(type) == ATOMID("alis") ) return new MP4UrlAtom( file, type ); if( ATOMID(type) == ATOMID("alaw") ) return new MP4SoundAtom( file, type ); if( ATOMID(type) == ATOMID("alac") ) return new MP4SoundAtom( file, type ); break; case 'c': if( ATOMID(type) == ATOMID("chap") ) return new MP4TrefTypeAtom( file, type ); if( ATOMID(type) == ATOMID("chpl") ) return new MP4ChplAtom(file); if( ATOMID(type) == ATOMID("colr") ) return new MP4ColrAtom(file); break; case 'd': if( ATOMID(type) == ATOMID("d263") ) return new MP4D263Atom(file); if( ATOMID(type) == ATOMID("damr") ) return new MP4DamrAtom(file); if( ATOMID(type) == ATOMID("dref") ) return new MP4DrefAtom(file); if( ATOMID(type) == ATOMID("dpnd") ) return new MP4TrefTypeAtom( file, type ); if( ATOMID(type) == ATOMID("dac3") ) return new MP4DAc3Atom(file); break; case 'e': if( ATOMID(type) == ATOMID("elst") ) return new MP4ElstAtom(file); if( ATOMID(type) == ATOMID("enca") ) return new MP4EncaAtom(file); if( ATOMID(type) == ATOMID("encv") ) return new MP4EncvAtom(file); break; case 'f': if( ATOMID(type) == ATOMID("free") ) return new MP4FreeAtom(file); if( ATOMID(type) == ATOMID("ftyp") ) return new MP4FtypAtom(file); if( ATOMID(type) == ATOMID("ftab") ) return new MP4FtabAtom(file); break; case 'g': if( ATOMID(type) == ATOMID("gmin") ) return new MP4GminAtom(file); break; case 'h': if( ATOMID(type) == ATOMID("hdlr") ) return new MP4HdlrAtom(file); if( ATOMID(type) == ATOMID("hint") ) return new MP4TrefTypeAtom( file, type ); if( ATOMID(type) == ATOMID("h263") ) return new MP4VideoAtom( file, type ); if( ATOMID(type) == ATOMID("href") ) return new MP4HrefAtom(file); break; case 'i': if( ATOMID(type) == ATOMID("ipir") ) return new MP4TrefTypeAtom( file, type ); if( ATOMID(type) == ATOMID("ima4") ) return new MP4SoundAtom( file, type ); break; case 'j': if( ATOMID(type) == ATOMID("jpeg") ) return new MP4VideoAtom(file, "jpeg"); break; case 'm': if( ATOMID(type) == ATOMID("mdhd") ) return new MP4MdhdAtom(file); if( ATOMID(type) == ATOMID("mvhd") ) return new MP4MvhdAtom(file); if( ATOMID(type) == ATOMID("mdat") ) return new MP4MdatAtom(file); if( ATOMID(type) == ATOMID("mpod") ) return new MP4TrefTypeAtom( file, type ); if( ATOMID(type) == ATOMID("mp4a") ) return new MP4SoundAtom( file, type ); if( ATOMID(type) == ATOMID("mp4s") ) return new MP4Mp4sAtom(file); if( ATOMID(type) == ATOMID("mp4v") ) return new MP4Mp4vAtom(file); break; case 'n': if( ATOMID(type) == ATOMID("nmhd") ) return new MP4NmhdAtom(file); break; case 'o': if( ATOMID(type) == ATOMID("ohdr") ) return new MP4OhdrAtom(file); break; case 'p': if( ATOMID(type) == ATOMID("pasp") ) return new MP4PaspAtom(file); break; case 'r': if( ATOMID(type) == ATOMID("rtp ") ) return new MP4RtpAtom(file); if( ATOMID(type) == ATOMID("raw ") ) return new MP4VideoAtom( file, type ); break; case 's': if( ATOMID(type) == ATOMID("s263") ) return new MP4S263Atom(file); if( ATOMID(type) == ATOMID("samr") ) return new MP4AmrAtom( file, type ); if( ATOMID(type) == ATOMID("sawb") ) return new MP4AmrAtom( file, type ); if( ATOMID(type) == ATOMID("sdtp") ) return new MP4SdtpAtom(file); if( ATOMID(type) == ATOMID("stbl") ) return new MP4StblAtom(file); if( ATOMID(type) == ATOMID("stsd") ) return new MP4StsdAtom(file); if( ATOMID(type) == ATOMID("stsz") ) return new MP4StszAtom(file); if( ATOMID(type) == ATOMID("stsc") ) return new MP4StscAtom(file); if( ATOMID(type) == ATOMID("stz2") ) return new MP4Stz2Atom(file); if( ATOMID(type) == ATOMID("stdp") ) return new MP4StdpAtom(file); if( ATOMID(type) == ATOMID("sdp ") ) return new MP4SdpAtom(file); if( ATOMID(type) == ATOMID("sync") ) return new MP4TrefTypeAtom( file, type ); if( ATOMID(type) == ATOMID("skip") ) return new MP4FreeAtom( file, type ); if (ATOMID(type) == ATOMID("sowt") ) return new MP4SoundAtom( file, type ); break; case 't': if( ATOMID(type) == ATOMID("text") ) return new MP4TextAtom(file); if( ATOMID(type) == ATOMID("tx3g") ) return new MP4Tx3gAtom(file); if( ATOMID(type) == ATOMID("tkhd") ) return new MP4TkhdAtom(file); if( ATOMID(type) == ATOMID("tfhd") ) return new MP4TfhdAtom(file); if( ATOMID(type) == ATOMID("trun") ) return new MP4TrunAtom(file); if( ATOMID(type) == ATOMID("twos") ) return new MP4SoundAtom( file, type ); break; case 'u': if( ATOMID(type) == ATOMID("udta") ) return new MP4UdtaAtom(file); if( ATOMID(type) == ATOMID("url ") ) return new MP4UrlAtom(file); if( ATOMID(type) == ATOMID("urn ") ) return new MP4UrnAtom(file); if( ATOMID(type) == ATOMID("ulaw") ) return new MP4SoundAtom( file, type ); break; case 'v': if( ATOMID(type) == ATOMID("vmhd") ) return new MP4VmhdAtom(file); break; case 'y': if( ATOMID(type) == ATOMID("yuv2") ) return new MP4VideoAtom( file, type ); break; default: break; } // default to MP4StandardAtom implementation return new MP4StandardAtom( file, type ); } /////////////////////////////////////////////////////////////////////////////// }} // namespace mp4v2::impl