Internal change

PiperOrigin-RevId: 139268906
Change-Id: I7c35776cd8d76fab690cf6368d219cbe778ed87a
diff --git a/CHANGES.logging b/CHANGES.logging
new file mode 100644
index 0000000..e1db8f2
--- /dev/null
+++ b/CHANGES.logging
@@ -0,0 +1,339 @@
+change log for logging branch
+
+$Revision$
+$Date$
+
+-------------------
+
+(31-jan-11, dbyron)
+
+- fix ASSERTs in win32 implementation of
+StandardFileProvider to check against _INVALID_FILE_HANDLE
+instead of NULL since that's what the constructor and close
+method use
+
+- merge r440, r441, r442, r443, r444 from trunk
+
+- fix return value of win32 implementation of
+StandardFileProvider::close
+
+- merge r439 from trunk
+
+(18-jan-11, dbyron)
+
+- add __FUNCTION__, filename to warning messages
+
+- add warning to MP4File::Modify when failing because the
+file has no moov atom
+
+- add __FUNCTION__, filename to error messages
+
+- add filename to verbose log messages in mp4descriptor.cpp
+
+- remove __FUNCTION__ from verbose log messages in
+mp4atom.cpp since it makes the dump output hard to read
+
+- add filename to descriptions passed to Log::dump
+
+- change log messages with Warning: to use Log::warningf
+
+- fix Log::hexdump to make sure output begins with format
+string/varargs instead of overwriting with ascii-hex
+
+(17-jan-11, dbyron)
+
+- change the MP4RtpData, MP4RtpNullData,
+MP4RtpImmediateData, MP4RtpSampleData,
+MP4RtpSampleDescriptionData, MP4RtpPacket, MP4RtpHint and
+MP4Track to take references instead of pointers, with
+accessors changed appropriately.
+
+(14-jan-11, dbyron)
+
+- change the MP4Descriptor constructor (and the constructors
+of all its child classes) to take a (MP4Atom &) and remove
+MP4Descriptor::SetParentAtom to remove the chance to
+dereference a NULL pointer.  Similarly for MP4Property and
+all its children.  Declare default and copy constructors and
+assignment operator private to prevent using
+compiler-generated routines.
+
+- change all Read and Write methods that took (MP4File *) to
+take a (MP4File &).
+
+- change the MP4Track, MP4RtpHintTrack constructors to take a
+(MP4File &).  Corresponding changes to MP4Track::GetFile.
+
+(13-jan-11, dbyron)
+
+- change the MP4Atom constructor to take an (MP4File &), remove
+MP4Atom::SetFile and change MP4Atom::GetFile to return an
+(MP4File &).  Change the internal member variable to an
+(MP4File &) as well.  Corresponding changes to
+MP4Atom::ReadAtom, CreateAtom, factory and child class
+constructors.  This makes it impossible to dereference a
+NULL pointer by accident.
+
+- convert messages with warning in the text to use
+Log::warningf
+
+- include __FUNCTION__ and filename in log messages in
+mp4atom.cpp
+
+(11-jan-11, dbyron)
+
+- declare Log copy constructor and assignment operator
+private but don't implement them to prohibit copies
+
+(6-jan-11, dbyron)
+
+- add mp4subtitle.vcproj to build on windows
+
+(3-jan-11, dbyron)
+
+- declare MP4File copy constructor and assignment operator
+private but don't implement them to prohibit copies
+
+- remove MP4HexDump and Indent from mp4util since they're
+not used anymore
+
+- change Log::hexDump to dump printable bytes as well as the
+hex values
+
+- remove error_msg_func_t, lib_message_func_t and
+MP4SetLibFunc from general.h since they're not used and
+don't really make sense now that MP4SetLogCallback exists
+
+- add MP4TagsHasMetadata to indicate whether a file contains
+any metadata at all
+
+(31-dec-10, dbyron)
+
+- add MP4GetFilename so the filename associated with a
+MP4FileHandle is available.  Add MP4File::GetFilename to
+support it.
+
+(27-dec-10, dbyron)
+
+- remove m_tempFileName from MP4File since it's not used
+
+- throw an exception from the win32 implementation of
+FileSystem::exists, FileSystem::isDirectory and
+FileSystem::isFile if there's an error
+
+- add logging to win32 FileSystem
+
+- add Utf8ToFilename::GetUTF8 to get the UTF-8
+representation of the filename actually used
+
+- add Utf8ToFilename::IsUTF16Valid so users of the class can
+detect errors
+
+- rename Utf8ToWideChar to Utf8ToFilename to more accurately
+reflect what the class does
+
+- replace MultiByteToWideChar with custom code to remove
+differences across versions of windows and for improved
+error detection and illegal character handling
+
+- add long filename support to Utf8ToWideChar
+
+(21-dec-10, dbyron)
+
+- add logging to win32 StandardFileProvider
+
+(20-dec-10, dbyron)
+
+- make try/catch blocks more granular in MP4Read,
+MP4ReadProvider, MP4Create, MP4CreateEx, MP4Modify,
+MP4Optimize, MP4Make3GPCompliant, MP4MakeIsmaCompliant,
+MP4MakeIsmaSdpIod so it's easier to figure out the cause of
+failure based on the log message.
+
+- log more specific messages, clean up leak if there's an
+error adding the IpodUUID atom in MP4AddIPodUUID
+
+- remove MP4_DETAILS_* as it was only used in a few internal
+places where an MP4_LOG_* replacement is fine until the
+verbosity-altering code disappears.  For some reason
+MP4File::MakeIsmaCompliant alters the verbosity before
+calling MP4GetVideoProfileLevel, MP4IsIsmaCrypMediaTrack
+does the same before calling MP4File::IsIsmaCrypMediaTrack,
+as does MP4CloneTrack before calling
+MP4GetTrackESConfiguration.
+
+- change command line programs to map debug levels as
+follows:
+  0: MP4_LOG_NONE
+  1: MP4_LOG_ERROR
+  2, 3: MP4_LOG_VERBOSE2
+  4: MP4_LOG_VERBOSE4
+
+- change MP4NormalizeTrackType (an internal function) to
+always use MP4_LOG_VERBOSE1.
+
+- another fix to Log::hexDump so that it now actually dumps
+all the bytes it was asked to dump
+
+- MP4File is no longer a child of Log.  Use the global log
+object everywhere.
+
+- remove the verbosity argument to MP4Read, MP4ReadProvider,
+MP4Create and MP4CreateEx, MP4Modify, MP4Optimize,
+MP4MakeIsmaCompliant, MP4MakeIsmaSdpIod, MP4Make3GPCompliant
+and MP4NormalizeTrackType.  Remove MP4GetVerbosity,
+MP4SetVerbosity and MP4Atom::GetVerbosity.  Use
+MP4LogSetLevel to change the global log level instead.
+There is no more file-specific verbosity since it is more
+complicated than one global log level and it's not clear
+anyone needs the added complexity.
+
+- remove the MP4FileHandle argument to MP4LogSetLevel and
+MP4LogGetLevel and move them to general.h/log.cpp since
+they're no longer file-specific.  The default log level is
+MP4_LOG_WARNING without any call to MP4LogSetLevel.
+
+- change MP4BytesProperty::Dump to use Log::hexDump for
+large (> 16) byte hex output
+
+- remove the FILE* argument from MP4Dump, the internal Dump
+methods (e.g. MP4File::Dump, MP4Atom::Dump,
+MP4Property::Dump, etc.).  Use Log::dump instead.
+
+- remove Indent from mp4util since it may pollute
+stdout/stderr
+
+- add Log::dump, Log::vdump to support dumping with a
+particular indent
+
+- fix Log::hexDump to print the correct ascii-hex info
+
+(17-dec-10, dbyron)
+
+- remove C_ASSERT from mp4util.h since it's not used
+
+- remove MP4Printf to keep from writing to stdout when
+logging
+
+(7-jun-09, dbyron)
+
+- add MP4LogGetLevel, MP4LogSetLevel
+
+(5-jun-09, dbyron)
+
+- change return value of MP4DeleteTrack,
+MP4SetTrackTimeScale, MP4GetTrackH264SeqPictHeaders from
+void to bool
+
+- change MP4AddIPodUUID to return bool, check for valid
+hFile and catch exceptions
+
+- check for NULL existingFileName, newFileName in
+MP4Optimize
+
+- check for NULL fileName in MP4ReadProvider, MP4CreateEx,
+MP4Modify, MP4Make3GPCompliant, MP4MakeIsmaCompliant
+
+- properly handle NULL pFile when catching Exception
+
+- check for NULL parameters in genericAddItem,
+genericSetItem, genericRemoveItem
+
+- change signature of C wrappers to return bool and check
+for NULL parameters:
+  - MP4TagsAddArtwork
+  - MP4TagsFetch
+  - MP4TagsRemoveArtwork
+  - MP4TagsSetArtwork
+  - MP4TagsStore
+  - MP4TagsSetName
+  - MP4TagsSetArtist
+  - MP4TagsSetAlbumArtist
+  - MP4TagsSetAlbum
+  - MP4TagsSetGrouping
+  - MP4TagsSetComposer(
+  - MP4TagsSetComments
+  - MP4TagsSetGenre
+  - MP4TagsSetGenreType
+  - MP4TagsSetReleaseDate
+  - MP4TagsSetTrack
+  - MP4TagsSetDisk
+  - MP4TagsSetTempo
+  - MP4TagsSetCompilation
+  - MP4TagsSetTVShow
+  - MP4TagsSetTVNetwork
+  - MP4TagsSetTVEpisodeID
+  - MP4TagsSetTVSeason
+  - MP4TagsSetTVEpisode
+  - MP4TagsSetSortName
+  - MP4TagsSetSortArtist
+  - MP4TagsSetSortAlbumArtist
+  - MP4TagsSetSortAlbum
+  - MP4TagsSetSortComposer
+  - MP4TagsSetSortTVShow
+  - MP4TagsSetDescription
+  - MP4TagsSetLongDescription
+  - MP4TagsSetLyrics
+  - MP4TagsSetCopyright
+  - MP4TagsSetEncodingTool
+  - MP4TagsSetEncodedBy
+  - MP4TagsSetPurchaseDate
+  - MP4TagsSetPodcast
+  - MP4TagsSetKeywords
+  - MP4TagsSetCategory
+  - MP4TagsSetHDVideo
+  - MP4TagsSetMediaType
+  - MP4TagsSetContentRating
+  - MP4TagsSetGapless
+  - MP4TagsSetITunesAccount
+  - MP4TagsSetITunesAccountType
+  - MP4TagsSetITunesCountry
+  - MP4TagsSetCNID
+  - MP4TagsSetATID
+  - MP4TagsSetPLID
+  - MP4TagsSetGEID
+
+- add catch( ... ) blocks to C wrapper functions
+
+- add static void errorf ( Log            *olog,
+                           const char     *format,
+                           ... ) MP4V2_WFORMAT_PRINTF(3,4);
+
+- add static void errorf ( Log                    *olog,
+                           const Exception        &x );
+- remove MP4Exception, replace with Exception
+
+- change MP4File::ProtectWriteOperation to take file, line,
+function instead of one "where" string.
+
+- change exception catching code to call Log::errorf();
+
+- remove VERBOSE, VERBOSE_ERROR from mp4util.h
+
+- remove MP4Error, replace with Exception, PlatformException
+as appropriate
+
+- add Exception class and Log::errorf( const Exception &x );
+
+(16-feb-09, dbyron)
+
+- First pass of actually using the Log class:
+
+  - MP4LogNone --> MP4_LOG_NONE
+  - add global log object (log, declared in log.h)
+  - change MP4File to inherit from Log
+    - remove m_verbosity member variable
+    - remove MP4File::GetVerbosity, SetVerbosity methods
+  - change MP4NormalizeTrackType to take MP4LogLevel
+  - replace VERBOSE_* with corresponding Log method calls
+  (VERBOSE_ERROR remains until exception handling gets
+  revisited)
+  - change WARNING macro to take a Log object as the first
+  argument
+  - add Log::hexDump to mostly replace MP4HexDump (though
+  MP4HexDump is still called if there's no logging callback)
+  - fix MP4_DETAILS_ALL handling in Log::detailsToLevel
+  - add uint32_t Log::getVerbosity() const
+  - add Log::hexDump
+  - add uint32_t Log::getVerbosity
+  - add Log::verbose3f, verbose4f
diff --git a/GNUmakefile.am b/GNUmakefile.am
new file mode 100644
index 0000000..947e6e3
--- /dev/null
+++ b/GNUmakefile.am
@@ -0,0 +1,322 @@
+lib_LTLIBRARIES = libmp4v2.la
+
+bin_PROGRAMS =
+
+check_PROGRAMS =
+
+###############################################################################
+
+libmp4v2_la_LDFLAGS = \
+    $(AM_LDFLAGS) \
+    -version-number $(PROJECT_version_major):$(PROJECT_version_minor):$(PROJECT_version_point) $(X_libmp4v2_la_LDFLAGS)
+
+libmp4v2_la_SOURCES = \
+    src/3gp.cpp                          \
+    src/atom_ac3.cpp                     \
+    src/atom_amr.cpp                     \
+    src/atom_avc1.cpp                    \
+    src/atom_avcC.cpp                    \
+    src/atom_chpl.cpp                    \
+    src/atom_colr.cpp                    \
+    src/atom_d263.cpp                    \
+    src/atom_dac3.cpp                    \
+    src/atom_damr.cpp                    \
+    src/atom_dref.cpp                    \
+    src/atom_elst.cpp                    \
+    src/atom_enca.cpp                    \
+    src/atom_encv.cpp                    \
+    src/atom_free.cpp                    \
+    src/atom_ftyp.cpp                    \
+    src/atom_ftab.cpp                    \
+    src/atom_gmin.cpp                    \
+    src/atom_hdlr.cpp                    \
+    src/atom_hinf.cpp                    \
+    src/atom_hnti.cpp                    \
+    src/atom_href.cpp                    \
+    src/atom_mdat.cpp                    \
+    src/atom_mdhd.cpp                    \
+    src/atom_meta.cpp                    \
+    src/atom_mp4s.cpp                    \
+    src/atom_mp4v.cpp                    \
+    src/atom_mvhd.cpp                    \
+    src/atom_nmhd.cpp                    \
+    src/atom_ohdr.cpp                    \
+    src/atom_pasp.cpp                    \
+    src/atom_root.cpp                    \
+    src/atom_rtp.cpp                     \
+    src/atom_s263.cpp                    \
+    src/atom_sdp.cpp                     \
+    src/atom_sdtp.cpp                    \
+    src/atom_smi.cpp                     \
+    src/atom_sound.cpp                   \
+    src/atom_standard.cpp                \
+    src/atom_stbl.cpp                    \
+    src/atom_stdp.cpp                    \
+    src/atom_stsc.cpp                    \
+    src/atom_stsd.cpp                    \
+    src/atom_stsz.cpp                    \
+    src/atom_stz2.cpp                    \
+    src/atom_text.cpp                    \
+    src/atom_tfhd.cpp                    \
+    src/atom_tkhd.cpp                    \
+    src/atom_treftype.cpp                \
+    src/atom_trun.cpp                    \
+    src/atom_tx3g.cpp                    \
+    src/atom_udta.cpp                    \
+    src/atom_url.cpp                     \
+    src/atom_urn.cpp                     \
+    src/atom_uuid.cpp                    \
+    src/atom_video.cpp                   \
+    src/atom_vmhd.cpp                    \
+    src/atoms.h                          \
+    src/cmeta.cpp                        \
+    src/descriptors.cpp                  \
+    src/descriptors.h                    \
+    src/exception.cpp                    \
+    src/exception.h                      \
+    src/enum.h                           \
+    src/enum.tcc                         \
+    src/impl.h                           \
+    src/isma.cpp                         \
+    src/log.h                            \
+    src/log.cpp                          \
+    src/mp4.cpp                          \
+    src/mp4array.h                       \
+    src/mp4atom.cpp                      \
+    src/mp4atom.h                        \
+    src/mp4container.cpp                 \
+    src/mp4container.h                   \
+    src/mp4descriptor.cpp                \
+    src/mp4descriptor.h                  \
+    src/mp4file.cpp                      \
+    src/mp4file.h                        \
+    src/mp4file_io.cpp                   \
+    src/mp4info.cpp                      \
+    src/mp4property.cpp                  \
+    src/mp4property.h                    \
+    src/mp4track.cpp                     \
+    src/mp4track.h                       \
+    src/mp4util.cpp                      \
+    src/mp4util.h                        \
+    src/ocidescriptors.cpp               \
+    src/ocidescriptors.h                 \
+    src/odcommands.cpp                   \
+    src/odcommands.h                     \
+    src/qosqualifiers.cpp                \
+    src/qosqualifiers.h                  \
+    src/rtphint.cpp                      \
+    src/rtphint.h                        \
+    src/src.h                            \
+    src/text.cpp                         \
+    src/text.h                           \
+    src/util.h
+
+libmp4v2_la_SOURCES += \
+    src/bmff/bmff.h    \
+    src/bmff/impl.h    \
+    src/bmff/typebmff.cpp  \
+    src/bmff/typebmff.h
+
+libmp4v2_la_SOURCES += \
+    src/itmf/CoverArtBox.cpp  \
+    src/itmf/CoverArtBox.h    \
+    src/itmf/Tags.cpp         \
+    src/itmf/Tags.h           \
+    src/itmf/generic.cpp      \
+    src/itmf/generic.h        \
+    src/itmf/impl.h           \
+    src/itmf/itmf.h           \
+    src/itmf/type.cpp         \
+    src/itmf/type.h
+
+libmp4v2_la_SOURCES += \
+    src/qtff/ColorParameterBox.cpp      \
+    src/qtff/ColorParameterBox.h        \
+    src/qtff/PictureAspectRatioBox.cpp  \
+    src/qtff/PictureAspectRatioBox.h    \
+    src/qtff/coding.cpp                 \
+    src/qtff/coding.h                   \
+    src/qtff/impl.h                     \
+    src/qtff/qtff.h
+
+libmp4v2_la_SOURCES += \
+    libplatform/endian.h                 \
+    libplatform/impl.h                   \
+    libplatform/io/File.cpp              \
+    libplatform/io/File.h                \
+    libplatform/io/FileSystem.cpp        \
+    libplatform/io/FileSystem.h          \
+    libplatform/number/random.h          \
+    libplatform/platform.h               \
+    libplatform/platform_base.h          \
+    libplatform/platform_posix.h         \
+    libplatform/platform_win32.h         \
+    libplatform/process/process.h        \
+    libplatform/prog/option.cpp          \
+    libplatform/prog/option.h            \
+    libplatform/sys/error.cpp            \
+    libplatform/sys/error.h              \
+    libplatform/time/time.cpp            \
+    libplatform/time/time.h              \
+    libplatform/warning.h
+
+if ADD_PLATFORM_POSIX
+    libmp4v2_la_SOURCES += \
+        libplatform/io/File_posix.cpp          \
+        libplatform/io/FileSystem_posix.cpp    \
+        libplatform/number/random_posix.cpp    \
+        libplatform/process/process_posix.cpp  \
+        libplatform/time/time_posix.cpp
+endif
+if ADD_PLATFORM_WIN32
+    libmp4v2_la_SOURCES += \
+        libplatform/io/File_win32.cpp          \
+        libplatform/io/FileSystem_win32.cpp    \
+        libplatform/number/random_win32.cpp    \
+        libplatform/process/process_win32.cpp  \
+        libplatform/time/time_win32.cpp
+endif
+
+if ADD_UTIL
+    libmp4v2_la_SOURCES += \
+        libutil/Database.cpp       \
+        libutil/Database.h         \
+        libutil/Timecode.cpp       \
+        libutil/Timecode.h         \
+        libutil/TrackModifier.cpp  \
+        libutil/TrackModifier.h    \
+        libutil/Utility.cpp        \
+        libutil/Utility.h          \
+        libutil/crc.cpp            \
+        libutil/crc.h              \
+        libutil/impl.h             \
+        libutil/other.cpp          \
+        libutil/other.h            \
+        libutil/util.h
+
+    bin_PROGRAMS += mp4art
+    bin_PROGRAMS += mp4chaps
+    bin_PROGRAMS += mp4extract
+    bin_PROGRAMS += mp4file
+    bin_PROGRAMS += mp4info
+    bin_PROGRAMS += mp4subtitle
+    bin_PROGRAMS += mp4tags
+    bin_PROGRAMS += mp4track
+    bin_PROGRAMS += mp4trackdump
+endif
+
+mp4art_SOURCES       = util/impl.h util/mp4art.cpp
+mp4chaps_SOURCES     = util/impl.h util/mp4chaps.cpp
+mp4extract_SOURCES   = util/impl.h util/mp4extract.cpp
+mp4file_SOURCES      = util/impl.h util/mp4file.cpp
+mp4info_SOURCES      = util/impl.h util/mp4info.cpp
+mp4subtitle_SOURCES  = util/impl.h util/mp4subtitle.cpp
+mp4tags_SOURCES      = util/impl.h util/mp4tags.cpp
+mp4track_SOURCES     = util/impl.h util/mp4track.cpp
+mp4trackdump_SOURCES = util/impl.h util/mp4trackdump.cpp
+
+mp4art_LDADD       = libmp4v2.la $(X_LDFLAGS)
+mp4chaps_LDADD     = libmp4v2.la $(X_LDFLAGS)
+mp4extract_LDADD   = libmp4v2.la $(X_LDFLAGS)
+mp4file_LDADD      = libmp4v2.la $(X_LDFLAGS)
+mp4info_LDADD      = libmp4v2.la $(X_LDFLAGS)
+mp4subtitle_LDADD  = libmp4v2.la $(X_LDFLAGS)
+mp4tags_LDADD      = libmp4v2.la $(X_LDFLAGS)
+mp4track_LDADD     = libmp4v2.la $(X_LDFLAGS)
+mp4trackdump_LDADD = libmp4v2.la $(X_LDFLAGS)
+
+###############################################################################
+
+DEJATOOL = main
+
+TESTLOGDIR = $(top_builddir)/testlog
+
+override RUNTESTDEFAULTFLAGS = --tool $$tool --srcdir $$srcdir/testsuite --outdir $(TESTLOGDIR)
+
+###############################################################################
+
+mp4v2incdir = $(pkgincludedir)
+
+mp4v2inc_HEADERS = \
+    include/mp4v2/project.h \
+    \
+    include/mp4v2/chapter.h                     \
+    include/mp4v2/file.h                        \
+    include/mp4v2/file_prop.h                   \
+    include/mp4v2/general.h                     \
+    include/mp4v2/isma.h                        \
+    include/mp4v2/itmf_generic.h                \
+    include/mp4v2/itmf_tags.h                   \
+    include/mp4v2/mp4v2.h                       \
+    include/mp4v2/platform.h                    \
+    include/mp4v2/sample.h                      \
+    include/mp4v2/streaming.h                   \
+    include/mp4v2/track.h                       \
+    include/mp4v2/track_prop.h
+
+###############################################################################
+
+EXTRA_DIST = project/project.m4
+
+###############################################################################
+
+clean-local:
+
+distclean-local:
+	rm -f $(top_builddir)/Makefile
+	rm -f $(top_builddir)/testlog/*.log
+	rm -f $(top_builddir)/testlog/*.sum
+
+###############################################################################
+
+AM_CPPFLAGS = $(strip $(MK_CXX_ARCH) $(X_CXX_ARCH) $(MK_CXX_I) $(X_CXX_I))
+
+AM_LDFLAGS = $(strip $(MK_CXX_ARCH) $(X_CXX_ARCH))
+
+AM_CXXFLAGS = $(strip $(MK_CXX_W) $(X_CXX_W))
+
+LIBS := $(LIBS) $(X_MINGW_LIBS)
+
+###############################################################################
+
+MK_CXX_ARCH =
+MK_CXX_W    = -Wall -Wformat
+MK_CXX_D    =
+
+MK_CXX_I = \
+    -I$(top_builddir)/include -I$(top_srcdir)/include \
+    -I$(top_builddir) -I$(top_srcdir)
+
+###############################################################################
+
+if ADD_UTIL
+if ADD_MANS
+    man1_MANS = \
+        doc/man/man1/mp4art.1       \
+        doc/man/man1/mp4file.1      \
+        doc/man/man1/mp4subtitle.1  \
+        doc/man/man1/mp4track.1
+endif
+endif
+
+###############################################################################
+
+##
+## workaround: DejaGNU adds a hard-coded dependency on Makefile
+## and need to create logdir
+##
+Makefile: ${Makefile}
+	touch $@
+	$(mkdir_p) $(TESTLOGDIR)
+
+dist-hook:
+
+###############################################################################
+
+##
+## include GNUmakefiles which are not full-fledged automake citizens but
+## they are aware of automake variables and targets. Note that we purposely
+## use a GNUmakefile extension (-include) to prevent automake from parsing
+## the file.
+##
+-include $(top_srcdir)/doc/GNUmakefile.mk
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..d3c5b40
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,237 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007 Free Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package.  The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package.
+
+   The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation.  It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions.  Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+   It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring.  Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+   If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release.  If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+   The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'.  You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+The simplest way to compile this package is:
+
+  1. `cd' to the directory containing the package's source code and type
+     `./configure' to configure the package for your system.
+
+     Running `configure' might take a while.  While running, it prints
+     some messages telling which features it is checking for.
+
+  2. Type `make' to compile the package.
+
+  3. Optionally, type `make check' to run any self-tests that come with
+     the package.
+
+  4. Type `make install' to install the programs and any data files and
+     documentation.
+
+  5. You can remove the program binaries and object files from the
+     source code directory by typing `make clean'.  To also remove the
+     files that `configure' created (so you can compile the package for
+     a different kind of computer), type `make distclean'.  There is
+     also a `make maintainer-clean' target, but that is intended mainly
+     for the package's developers.  If you use it, you may have to get
+     all sorts of other programs in order to regenerate files that came
+     with the distribution.
+
+  6. Often, you can also type `make uninstall' to remove the installed
+     files again.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about.  Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+   You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment.  Here
+is an example:
+
+     ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+   *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory.  To do this, you can use GNU `make'.  `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script.  `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+   With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory.  After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc.  You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+   You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files.  If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+   In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files.  Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+   If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System).  The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+   For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option.  TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+     CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+     OS KERNEL-OS
+
+   See the file `config.sub' for the possible values of each field.  If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+   If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+   If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists.  Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'.  However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost.  In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'.  For example:
+
+     ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug.  Until the bug is fixed you can use this workaround:
+
+     CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+     Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+     Print the version of Autoconf used to generate the `configure'
+     script, and exit.
+
+`--cache-file=FILE'
+     Enable the cache: use and save the results of the tests in FILE,
+     traditionally `config.cache'.  FILE defaults to `/dev/null' to
+     disable caching.
+
+`--config-cache'
+`-C'
+     Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+     Do not print messages saying which checks are being made.  To
+     suppress all normal output, redirect it to `/dev/null' (any error
+     messages will still be shown).
+
+`--srcdir=DIR'
+     Look for the package's source code in directory DIR.  Usually
+     `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options.  Run
+`configure --help' for more details.
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0c3dd64
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,458 @@
+                          MOZILLA PUBLIC LICENSE
+                                Version 1.1
+
+                              ---------------
+
+1. Definitions.
+
+     1.0.1. "Commercial Use" means distribution or otherwise making the
+     Covered Code available to a third party.
+
+     1.1. "Contributor" means each entity that creates or contributes to
+     the creation of Modifications.
+
+     1.2. "Contributor Version" means the combination of the Original
+     Code, prior Modifications used by a Contributor, and the Modifications
+     made by that particular Contributor.
+
+     1.3. "Covered Code" means the Original Code or Modifications or the
+     combination of the Original Code and Modifications, in each case
+     including portions thereof.
+
+     1.4. "Electronic Distribution Mechanism" means a mechanism generally
+     accepted in the software development community for the electronic
+     transfer of data.
+
+     1.5. "Executable" means Covered Code in any form other than Source
+     Code.
+
+     1.6. "Initial Developer" means the individual or entity identified
+     as the Initial Developer in the Source Code notice required by Exhibit
+     A.
+
+     1.7. "Larger Work" means a work which combines Covered Code or
+     portions thereof with code not governed by the terms of this License.
+
+     1.8. "License" means this document.
+
+     1.8.1. "Licensable" means having the right to grant, to the maximum
+     extent possible, whether at the time of the initial grant or
+     subsequently acquired, any and all of the rights conveyed herein.
+
+     1.9. "Modifications" means any addition to or deletion from the
+     substance or structure of either the Original Code or any previous
+     Modifications. When Covered Code is released as a series of files, a
+     Modification is:
+          A. Any addition to or deletion from the contents of a file
+          containing Original Code or previous Modifications.
+
+          B. Any new file that contains any part of the Original Code or
+          previous Modifications.
+
+     1.10. "Original Code" means Source Code of computer software code
+     which is described in the Source Code notice required by Exhibit A as
+     Original Code, and which, at the time of its release under this
+     License is not already Covered Code governed by this License.
+
+     1.10.1. "Patent Claims" means any patent claim(s), now owned or
+     hereafter acquired, including without limitation,  method, process,
+     and apparatus claims, in any patent Licensable by grantor.
+
+     1.11. "Source Code" means the preferred form of the Covered Code for
+     making modifications to it, including all modules it contains, plus
+     any associated interface definition files, scripts used to control
+     compilation and installation of an Executable, or source code
+     differential comparisons against either the Original Code or another
+     well known, available Covered Code of the Contributor's choice. The
+     Source Code can be in a compressed or archival form, provided the
+     appropriate decompression or de-archiving software is widely available
+     for no charge.
+
+     1.12. "You" (or "Your")  means an individual or a legal entity
+     exercising rights under, and complying with all of the terms of, this
+     License or a future version of this License issued under Section 6.1.
+     For legal entities, "You" includes any entity which controls, is
+     controlled by, or is under common control with You. For purposes of
+     this definition, "control" means (a) the power, direct or indirect,
+     to cause the direction or management of such entity, whether by
+     contract or otherwise, or (b) ownership of more than fifty percent
+     (50%) of the outstanding shares or beneficial ownership of such
+     entity.
+
+2. Source Code License.
+
+     2.1. The Initial Developer Grant.
+     The Initial Developer hereby grants You a world-wide, royalty-free,
+     non-exclusive license, subject to third party intellectual property
+     claims:
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Initial Developer to use, reproduce,
+          modify, display, perform, sublicense and distribute the Original
+          Code (or portions thereof) with or without Modifications, and/or
+          as part of a Larger Work; and
+
+          (b) under Patents Claims infringed by the making, using or
+          selling of Original Code, to make, have made, use, practice,
+          sell, and offer for sale, and/or otherwise dispose of the
+          Original Code (or portions thereof).
+
+          (c) the licenses granted in this Section 2.1(a) and (b) are
+          effective on the date Initial Developer first distributes
+          Original Code under the terms of this License.
+
+          (d) Notwithstanding Section 2.1(b) above, no patent license is
+          granted: 1) for code that You delete from the Original Code; 2)
+          separate from the Original Code;  or 3) for infringements caused
+          by: i) the modification of the Original Code or ii) the
+          combination of the Original Code with other software or devices.
+
+     2.2. Contributor Grant.
+     Subject to third party intellectual property claims, each Contributor
+     hereby grants You a world-wide, royalty-free, non-exclusive license
+
+          (a)  under intellectual property rights (other than patent or
+          trademark) Licensable by Contributor, to use, reproduce, modify,
+          display, perform, sublicense and distribute the Modifications
+          created by such Contributor (or portions thereof) either on an
+          unmodified basis, with other Modifications, as Covered Code
+          and/or as part of a Larger Work; and
+
+          (b) under Patent Claims infringed by the making, using, or
+          selling of  Modifications made by that Contributor either alone
+          and/or in combination with its Contributor Version (or portions
+          of such combination), to make, use, sell, offer for sale, have
+          made, and/or otherwise dispose of: 1) Modifications made by that
+          Contributor (or portions thereof); and 2) the combination of
+          Modifications made by that Contributor with its Contributor
+          Version (or portions of such combination).
+
+          (c) the licenses granted in Sections 2.2(a) and 2.2(b) are
+          effective on the date Contributor first makes Commercial Use of
+          the Covered Code.
+
+          (d)    Notwithstanding Section 2.2(b) above, no patent license is
+          granted: 1) for any code that Contributor has deleted from the
+          Contributor Version; 2)  separate from the Contributor Version;
+          3)  for infringements caused by: i) third party modifications of
+          Contributor Version or ii)  the combination of Modifications made
+          by that Contributor with other software  (except as part of the
+          Contributor Version) or other devices; or 4) under Patent Claims
+          infringed by Covered Code in the absence of Modifications made by
+          that Contributor.
+
+3. Distribution Obligations.
+
+     3.1. Application of License.
+     The Modifications which You create or to which You contribute are
+     governed by the terms of this License, including without limitation
+     Section 2.2. The Source Code version of Covered Code may be
+     distributed only under the terms of this License or a future version
+     of this License released under Section 6.1, and You must include a
+     copy of this License with every copy of the Source Code You
+     distribute. You may not offer or impose any terms on any Source Code
+     version that alters or restricts the applicable version of this
+     License or the recipients' rights hereunder. However, You may include
+     an additional document offering the additional rights described in
+     Section 3.5.
+
+     3.2. Availability of Source Code.
+     Any Modification which You create or to which You contribute must be
+     made available in Source Code form under the terms of this License
+     either on the same media as an Executable version or via an accepted
+     Electronic Distribution Mechanism to anyone to whom you made an
+     Executable version available; and if made available via Electronic
+     Distribution Mechanism, must remain available for at least twelve (12)
+     months after the date it initially became available, or at least six
+     (6) months after a subsequent version of that particular Modification
+     has been made available to such recipients. You are responsible for
+     ensuring that the Source Code version remains available even if the
+     Electronic Distribution Mechanism is maintained by a third party.
+
+     3.3. Description of Modifications.
+     You must cause all Covered Code to which You contribute to contain a
+     file documenting the changes You made to create that Covered Code and
+     the date of any change. You must include a prominent statement that
+     the Modification is derived, directly or indirectly, from Original
+     Code provided by the Initial Developer and including the name of the
+     Initial Developer in (a) the Source Code, and (b) in any notice in an
+     Executable version or related documentation in which You describe the
+     origin or ownership of the Covered Code.
+
+     3.4. Intellectual Property Matters
+          (a) Third Party Claims.
+          If Contributor has knowledge that a license under a third party's
+          intellectual property rights is required to exercise the rights
+          granted by such Contributor under Sections 2.1 or 2.2,
+          Contributor must include a text file with the Source Code
+          distribution titled "LEGAL" which describes the claim and the
+          party making the claim in sufficient detail that a recipient will
+          know whom to contact. If Contributor obtains such knowledge after
+          the Modification is made available as described in Section 3.2,
+          Contributor shall promptly modify the LEGAL file in all copies
+          Contributor makes available thereafter and shall take other steps
+          (such as notifying appropriate mailing lists or newsgroups)
+          reasonably calculated to inform those who received the Covered
+          Code that new knowledge has been obtained.
+
+          (b) Contributor APIs.
+          If Contributor's Modifications include an application programming
+          interface and Contributor has knowledge of patent licenses which
+          are reasonably necessary to implement that API, Contributor must
+          also include this information in the LEGAL file.
+
+               (c)    Representations.
+          Contributor represents that, except as disclosed pursuant to
+          Section 3.4(a) above, Contributor believes that Contributor's
+          Modifications are Contributor's original creation(s) and/or
+          Contributor has sufficient rights to grant the rights conveyed by
+          this License.
+
+     3.5. Required Notices.
+     You must duplicate the notice in Exhibit A in each file of the Source
+     Code.  If it is not possible to put such notice in a particular Source
+     Code file due to its structure, then You must include such notice in a
+     location (such as a relevant directory) where a user would be likely
+     to look for such a notice.  If You created one or more Modification(s)
+     You may add your name as a Contributor to the notice described in
+     Exhibit A.  You must also duplicate this License in any documentation
+     for the Source Code where You describe recipients' rights or ownership
+     rights relating to Covered Code.  You may choose to offer, and to
+     charge a fee for, warranty, support, indemnity or liability
+     obligations to one or more recipients of Covered Code. However, You
+     may do so only on Your own behalf, and not on behalf of the Initial
+     Developer or any Contributor. You must make it absolutely clear than
+     any such warranty, support, indemnity or liability obligation is
+     offered by You alone, and You hereby agree to indemnify the Initial
+     Developer and every Contributor for any liability incurred by the
+     Initial Developer or such Contributor as a result of warranty,
+     support, indemnity or liability terms You offer.
+
+     3.6. Distribution of Executable Versions.
+     You may distribute Covered Code in Executable form only if the
+     requirements of Section 3.1-3.5 have been met for that Covered Code,
+     and if You include a notice stating that the Source Code version of
+     the Covered Code is available under the terms of this License,
+     including a description of how and where You have fulfilled the
+     obligations of Section 3.2. The notice must be conspicuously included
+     in any notice in an Executable version, related documentation or
+     collateral in which You describe recipients' rights relating to the
+     Covered Code. You may distribute the Executable version of Covered
+     Code or ownership rights under a license of Your choice, which may
+     contain terms different from this License, provided that You are in
+     compliance with the terms of this License and that the license for the
+     Executable version does not attempt to limit or alter the recipient's
+     rights in the Source Code version from the rights set forth in this
+     License. If You distribute the Executable version under a different
+     license You must make it absolutely clear that any terms which differ
+     from this License are offered by You alone, not by the Initial
+     Developer or any Contributor. You hereby agree to indemnify the
+     Initial Developer and every Contributor for any liability incurred by
+     the Initial Developer or such Contributor as a result of any such
+     terms You offer.
+
+     3.7. Larger Works.
+     You may create a Larger Work by combining Covered Code with other code
+     not governed by the terms of this License and distribute the Larger
+     Work as a single product. In such a case, You must make sure the
+     requirements of this License are fulfilled for the Covered Code.
+
+4. Inability to Comply Due to Statute or Regulation.
+
+     If it is impossible for You to comply with any of the terms of this
+     License with respect to some or all of the Covered Code due to
+     statute, judicial order, or regulation then You must: (a) comply with
+     the terms of this License to the maximum extent possible; and (b)
+     describe the limitations and the code they affect. Such description
+     must be included in the LEGAL file described in Section 3.4 and must
+     be included with all distributions of the Source Code. Except to the
+     extent prohibited by statute or regulation, such description must be
+     sufficiently detailed for a recipient of ordinary skill to be able to
+     understand it.
+
+5. Application of this License.
+
+     This License applies to code to which the Initial Developer has
+     attached the notice in Exhibit A and to related Covered Code.
+
+6. Versions of the License.
+
+     6.1. New Versions.
+     Netscape Communications Corporation ("Netscape") may publish revised
+     and/or new versions of the License from time to time. Each version
+     will be given a distinguishing version number.
+
+     6.2. Effect of New Versions.
+     Once Covered Code has been published under a particular version of the
+     License, You may always continue to use it under the terms of that
+     version. You may also choose to use such Covered Code under the terms
+     of any subsequent version of the License published by Netscape. No one
+     other than Netscape has the right to modify the terms applicable to
+     Covered Code created under this License.
+
+     6.3. Derivative Works.
+     If You create or use a modified version of this License (which you may
+     only do in order to apply it to code which is not already Covered Code
+     governed by this License), You must (a) rename Your license so that
+     the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape",
+     "MPL", "NPL" or any confusingly similar phrase do not appear in your
+     license (except to note that your license differs from this License)
+     and (b) otherwise make it clear that Your version of the license
+     contains terms which differ from the Mozilla Public License and
+     Netscape Public License. (Filling in the name of the Initial
+     Developer, Original Code or Contributor in the notice described in
+     Exhibit A shall not of themselves be deemed to be modifications of
+     this License.)
+
+7. DISCLAIMER OF WARRANTY.
+
+     COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS,
+     WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+     WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF
+     DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING.
+     THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE
+     IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT,
+     YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE
+     COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER
+     OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+     ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
+
+8. TERMINATION.
+
+     8.1.  This License and the rights granted hereunder will terminate
+     automatically if You fail to comply with terms herein and fail to cure
+     such breach within 30 days of becoming aware of the breach. All
+     sublicenses to the Covered Code which are properly granted shall
+     survive any termination of this License. Provisions which, by their
+     nature, must remain in effect beyond the termination of this License
+     shall survive.
+
+     8.2.  If You initiate litigation by asserting a patent infringement
+     claim (excluding declatory judgment actions) against Initial Developer
+     or a Contributor (the Initial Developer or Contributor against whom
+     You file such action is referred to as "Participant")  alleging that:
+
+     (a)  such Participant's Contributor Version directly or indirectly
+     infringes any patent, then any and all rights granted by such
+     Participant to You under Sections 2.1 and/or 2.2 of this License
+     shall, upon 60 days notice from Participant terminate prospectively,
+     unless if within 60 days after receipt of notice You either: (i)
+     agree in writing to pay Participant a mutually agreeable reasonable
+     royalty for Your past and future use of Modifications made by such
+     Participant, or (ii) withdraw Your litigation claim with respect to
+     the Contributor Version against such Participant.  If within 60 days
+     of notice, a reasonable royalty and payment arrangement are not
+     mutually agreed upon in writing by the parties or the litigation claim
+     is not withdrawn, the rights granted by Participant to You under
+     Sections 2.1 and/or 2.2 automatically terminate at the expiration of
+     the 60 day notice period specified above.
+
+     (b)  any software, hardware, or device, other than such Participant's
+     Contributor Version, directly or indirectly infringes any patent, then
+     any rights granted to You by such Participant under Sections 2.1(b)
+     and 2.2(b) are revoked effective as of the date You first made, used,
+     sold, distributed, or had made, Modifications made by that
+     Participant.
+
+     8.3.  If You assert a patent infringement claim against Participant
+     alleging that such Participant's Contributor Version directly or
+     indirectly infringes any patent where such claim is resolved (such as
+     by license or settlement) prior to the initiation of patent
+     infringement litigation, then the reasonable value of the licenses
+     granted by such Participant under Sections 2.1 or 2.2 shall be taken
+     into account in determining the amount or value of any payment or
+     license.
+
+     8.4.  In the event of termination under Sections 8.1 or 8.2 above,
+     all end user license agreements (excluding distributors and resellers)
+     which have been validly granted by You or any distributor hereunder
+     prior to termination shall survive termination.
+
+9. LIMITATION OF LIABILITY.
+
+     UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+     (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL
+     DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE,
+     OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR
+     ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY
+     CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL,
+     WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+     COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+     INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+     LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY
+     RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW
+     PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE
+     EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO
+     THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
+
+10. U.S. GOVERNMENT END USERS.
+
+     The Covered Code is a "commercial item," as that term is defined in
+     48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer
+     software" and "commercial computer software documentation," as such
+     terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48
+     C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995),
+     all U.S. Government End Users acquire Covered Code with only those
+     rights set forth herein.
+
+11. MISCELLANEOUS.
+
+     This License represents the complete agreement concerning subject
+     matter hereof. If any provision of this License is held to be
+     unenforceable, such provision shall be reformed only to the extent
+     necessary to make it enforceable. This License shall be governed by
+     California law provisions (except to the extent applicable law, if
+     any, provides otherwise), excluding its conflict-of-law provisions.
+     With respect to disputes in which at least one party is a citizen of,
+     or an entity chartered or registered to do business in the United
+     States of America, any litigation relating to this License shall be
+     subject to the jurisdiction of the Federal Courts of the Northern
+     District of California, with venue lying in Santa Clara County,
+     California, with the losing party responsible for costs, including
+     without limitation, court costs and reasonable attorneys' fees and
+     expenses. The application of the United Nations Convention on
+     Contracts for the International Sale of Goods is expressly excluded.
+     Any law or regulation which provides that the language of a contract
+     shall be construed against the drafter shall not apply to this
+     License.
+
+12. RESPONSIBILITY FOR CLAIMS.
+
+     As between Initial Developer and the Contributors, each party is
+     responsible for claims and damages arising, directly or indirectly,
+     out of its utilization of rights under this License and You agree to
+     work with Initial Developer and Contributors to distribute such
+     responsibility on an equitable basis. Nothing herein is intended or
+     shall be deemed to constitute any admission of liability.
+
+13. MULTIPLE-LICENSED CODE.
+
+     Initial Developer may designate portions of the Covered Code as
+     "Multiple-Licensed".  "Multiple-Licensed" means that the Initial
+     Developer permits you to utilize portions of the Covered Code under
+     Your choice of the NPL or the alternative licenses, if any, specified
+     by the Initial Developer in the file described in Exhibit A.
+
+EXHIBIT A -Mozilla Public License.
+
+     ``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. All Rights Reserved.
+
+     Contributor(s): ...
+
+     [NOTE: The text of this Exhibit A may differ slightly from the text of
+     the notices in the Source Code files of the Original Code. You should
+     use the text of this Exhibit A rather than the text found in the
+     Original Code Source Code for Your Modifications.]
diff --git a/README b/README
new file mode 100644
index 0000000..bce10af
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+All docs are located in doc/ subdirectory. Useful starting points:
+
+Release Notes           -- doc/ReleaseNotes.txt
+Building the Source     -- doc/BuildSource.txt
+Building the Repository -- doc/BuildRepository.txt
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..83e9dce
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,419 @@
+###############################################################################
+# prelude
+###############################################################################
+
+m4_include([project/project.m4sugar])
+
+m4_define([SVNINFO],m4_esyscmd([LANG=en svn info 2>/dev/null]))
+
+changequote(<<,>>)dnl
+    m4_define(<<PRJ_repo_url>>,m4_bregexp(SVNINFO,<<^URL: *\(.+\)>>,<<\1>>))
+    m4_define(<<PRJ_repo_branch>>,m4_bregexp(PRJ_repo_url,<<\([^/]+\)$>>,<<\1>>))
+    m4_define(<<PRJ_repo_root>>,m4_bregexp(SVNINFO,<<^Repository Root: *\(.+\)>>,<<\1>>))
+    m4_define(<<PRJ_repo_uuid>>,m4_bregexp(SVNINFO,<<^Repository UUID: *\(.+\)>>,<<\1>>))
+    m4_define(<<PRJ_repo_rev>>,m4_bregexp(SVNINFO,<<^Last Changed Rev: *\(.+\)>>,<<\1>>))
+    m4_define(<<PRJ_repo_date>>,m4_bregexp(SVNINFO,<<^Last Changed Date: *\(.+\)>>,<<\1>>))
+    m4_define(<<PRJ_repo_type>>,ifelse(m4_bregexp(PRJ_repo_url,<</releases/>>),<<-1>>,<<developer>>,<<stable>>))
+changequote([,])dnl
+
+m4_define([PRJ_version_hex],m4_format([0x%04x%02x%02x],PRJ_version_major,PRJ_version_minor,PRJ_version_point))
+
+m4_define([PRJ_version],ifelse(
+    PRJ_repo_type,[stable],m4_format([%s],PRJ_repo_branch),
+    m4_format([%s-r%s],PRJ_repo_branch,PRJ_repo_rev)))
+
+###############################################################################
+# initialization
+###############################################################################
+
+AC_PREREQ([2.61])
+AC_INIT(PRJ_name,PRJ_version,PRJ_bugreport)
+
+AC_MSG_NOTICE([
+  -->
+  --> Configuring ]AC_PACKAGE_STRING[
+  -->])
+
+AC_CONFIG_AUX_DIR([autoaux])
+AM_INIT_AUTOMAKE([1.9.6 foreign -Wall -Wno-portability -Werror subdir-objects nostdinc no-dist-gzip dist-bzip2 dist-zip dejagnu])
+
+AC_CONFIG_SRCDIR([src/mp4.cpp])
+
+# disable unused langs (improves config speed, reduces configure file size)
+AC_DEFUN([_LT_AC_LANG_F77_CONFIG], [:])
+AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG], [:])
+AC_DEFUN([_LT_AC_LANG_RC_CONFIG], [:])
+
+AC_CANONICAL_HOST
+
+###############################################################################
+# add configure options
+###############################################################################
+
+if test -z "$enable_dependency_tracking"; then
+    enable_dependency_tracking=no
+fi
+
+AC_ARG_ENABLE([debug],
+    [AS_HELP_STRING([--disable-debug],[disable debugging compilation])])
+AC_ARG_ENABLE([optimize],
+    [AS_HELP_STRING([--disable-optimize],[disable optimizing compilation])])
+AC_ARG_ENABLE([fvisibility],
+    [AS_HELP_STRING([--disable-fvisibility],[disable use of -fvisibility])])
+AC_ARG_ENABLE([gch],
+    [AS_HELP_STRING([--disable-gch],[disable GCC precompiled-headers])])
+AC_ARG_ENABLE([largefile],
+    [AS_HELP_STRING([--disable-largefile],[disable LFS (large file support)])])
+AC_ARG_ENABLE([util],
+    [AS_HELP_STRING([--disable-util],[disable build of command-line utilities])])
+AC_ARG_ENABLE([bi],
+    [AS_HELP_STRING([--enable-bi=ARCH],[enable -mARCH for bi-arch compilation])])
+AC_ARG_ENABLE([ub],
+    [AS_HELP_STRING([--enable-ub@<:@=ARCHS@:>@],[enable -arch ARCH for universal-binaries (OSX only)])])
+AC_ARG_ENABLE([cygwin_win32],
+    [AS_HELP_STRING([--enable-cygwin-win32],[when building with Cygwin use -mwin32])])
+AC_ARG_ENABLE([mingw_mt],
+    [AS_HELP_STRING([--enable-mingw-threads],[when building with MinGW use -mthreads])])
+
+###############################################################################
+# checks for programs
+###############################################################################
+
+AC_PROG_CXX
+AC_PROG_LIBTOOL
+
+AC_CHECK_PROG([FOUND_HELP2MAN],[help2man],[yes],[no])
+
+###############################################################################
+# top-level platform check
+###############################################################################
+
+AC_MSG_CHECKING([$PACKAGE_NAME platform portability])
+X_PLATFORM=posix
+case ${host} in
+*-*-cygwin)
+    X_CXX_W="$X_CXX_W -Wno-format"
+    if test "$enable_cygwin_win32" = "yes"; then
+        X_PLATFORM=win32
+        X_CXX_ARCH="$X_CXX_ARCH -mwin32"
+    fi
+    ;;
+*-*-mingw*)
+    # do not support shared
+    enable_shared=no
+    X_PLATFORM=win32
+    X_MINGW_LIBS="$X_MINGW_LIBS"
+    X_CXX_W="$X_CXX_W -Wno-format"
+    if test "$enable_mingw_threads" = "yes"; then
+        X_CXX_ARCH="$X_CXX_ARCH -mthreads"
+    fi
+    ;;
+esac
+AC_MSG_RESULT([$X_PLATFORM])
+
+###############################################################################
+# prepare project metadata
+###############################################################################
+
+PROJECT_name="PRJ_name"
+PROJECT_name_lower="PRJ_name_lower"
+PROJECT_name_upper="PRJ_name_upper"
+PROJECT_name_formal="PRJ_name PRJ_version"
+PROJECT_url_website="PRJ_url_website"
+PROJECT_url_downloads="PRJ_url_downloads"
+PROJECT_url_discussion="PRJ_url_discussion"
+PROJECT_irc="PRJ_irc"
+PROJECT_bugreport="<PRJ_bugreport>"
+
+PROJECT_version="PRJ_version"
+PROJECT_version_hex="PRJ_version_hex"
+PROJECT_version_major="PRJ_version_major"
+PROJECT_version_minor="PRJ_version_minor"
+PROJECT_version_point="PRJ_version_point"
+PROJECT_repo_url="PRJ_repo_url"
+PROJECT_repo_branch="PRJ_repo_branch"
+PROJECT_repo_root="PRJ_repo_root"
+PROJECT_repo_uuid="PRJ_repo_uuid"
+PROJECT_repo_rev="PRJ_repo_rev"
+PROJECT_repo_date="PRJ_repo_date"
+PROJECT_repo_type="PRJ_repo_type"
+PROJECT_build="`date`"
+
+test -z "$PROJECT_version_hex"   && PROJECT_version_hex="0x0000000000000000LL"
+test -z "$PROJECT_version_major" && PROJECT_version_major="0"
+test -z "$PROJECT_version_minor" && PROJECT_version_minor="0"
+test -z "$PROJECT_version_point" && PROJECT_version_point="0"
+test -z "$PROJECT_repo_url"      && PROJECT_repo_url="svn://nowhere.com/project/unknown"
+test -z "$PROJECT_repo_branch"   && PROJECT_repo_branch="unknown"
+test -z "$PROJECT_repo_root"     && PROJECT_repo_root="svn://nowhere.com/project"
+test -z "$PROJECT_repo_uuid"     && PROJECT_repo_uuid="00000000-0000-0000-0000-000000000000"
+test -z "$PROJECT_repo_rev"      && PROJECT_repo_rev="0"
+test -z "$PROJECT_repo_date"     && PROJECT_repo_date="unknown"
+test -z "$PROJECT_repo_type"     && PROJECT_repo_type="unknown"
+test -z "$PROJECT_build"         && PROJECT_build="unknown"
+
+AC_SUBST([PROJECT_name])
+AC_SUBST([PROJECT_name_lower])
+AC_SUBST([PROJECT_name_upper])
+AC_SUBST([PROJECT_name_formal])
+AC_SUBST([PROJECT_url_website])
+AC_SUBST([PROJECT_url_downloads])
+AC_SUBST([PROJECT_url_discussion])
+AC_SUBST([PROJECT_irc])
+AC_SUBST([PROJECT_bugreport])
+AC_SUBST([PROJECT_version])
+AC_SUBST([PROJECT_version_hex])
+AC_SUBST([PROJECT_version_major])
+AC_SUBST([PROJECT_version_minor])
+AC_SUBST([PROJECT_version_point])
+AC_SUBST([PROJECT_repo_url])
+AC_SUBST([PROJECT_repo_branch])
+AC_SUBST([PROJECT_repo_root])
+AC_SUBST([PROJECT_repo_uuid])
+AC_SUBST([PROJECT_repo_rev])
+AC_SUBST([PROJECT_repo_date])
+AC_SUBST([PROJECT_repo_type])
+AC_SUBST([PROJECT_build])
+
+###############################################################################
+# checks for libraries
+###############################################################################
+
+###############################################################################
+# checks for header files
+###############################################################################
+
+###############################################################################
+# checks for typedefs, structures, and compiler characteristics
+###############################################################################
+
+###############################################################################
+# replace -ggdb3 with -ggdb2 to avoid GNU 'as' from failing
+# See http://code.google.com/p/mp4v2/issues/detail?id=27
+###############################################################################
+if test "$GXX" = "yes"; then
+	CXXFLAGS_GGDB3=`echo "$CXXFLAGS" | sed -e 's/-ggdb3/-ggdb2/'`
+	CXXFLAGS="$CXXFLAGS_GGDB3"
+fi
+
+###############################################################################
+# additional ldflags
+###############################################################################
+
+case ${host} in
+    *-*-darwin*)
+        X_libmp4v2_la_LDFLAGS='-Wl,-current_version,$(PROJECT_version_major).$(PROJECT_version_minor).$(PROJECT_version_point) -Wl,-compatibility_version,$(PROJECT_version_major).0.0'
+        ;;
+    *-*-cygwin*)
+        X_libmp4v2_la_LDFLAGS='-no-undefined'
+        ;;
+    *)
+        X_libmp4v2_la_LDFLAGS=
+        ;;
+esac
+
+AC_SUBST([X_libmp4v2_la_LDFLAGS])
+
+###############################################################################
+# check for --disable-fvisibility
+###############################################################################
+
+if test "$enable_fvisibility" != "no" -a "$GXX" = "yes"; then
+    case ${host} in
+        *-*-cygwin)
+            ;;
+        *-*-mingw*)
+            ;;
+        *)
+            AC_LANG(C++)
+            AC_CACHE_CHECK([if $CXX supports -fvisibility],[x_cv_fvisibility],[
+                x_save="$CXXFLAGS"
+                CXXFLAGS="$CXXFLAGS -fvisibility=hidden"
+                AC_TRY_COMPILE([],[],[x_cv_fvisibility=yes],[x_cv_fvisibility=no])
+                if test "$x_cv_fvisibility" != "yes"; then
+                    CXXFLAGS="$x_save"
+                fi
+                x_save=
+            ])
+            ;;
+    esac
+fi
+
+###############################################################################
+# check for --disable-gch
+###############################################################################
+
+AC_MSG_CHECKING([if GCC precompiled-headers should be created])
+
+X_GCH=0
+X_GCH_FLAGS=
+X_GCH_STATIC=0
+X_GCH_STATIC_FLAGS="$lt_prog_compiler_static_CXX"
+X_GCH_SHARED=0
+X_GCH_SHARED_FLAGS="$lt_prog_compiler_pic_CXX"
+
+if test "$enable_gch" = "yes"; then
+    X_GCH=1
+elif test "$enable_gch" != "no"; then
+    if test "$GXX" = "yes"; then
+        case ${host_os} in
+            cygwin*|darwin*|freebsd*|linux*|mingw*|solaris*)
+                X_GCH=1
+                ;;
+        esac
+    fi
+fi
+
+if test "$X_GCH" -eq 1; then
+    x_gch_result="yes"
+    if test "$enable_static" = "yes"; then
+        X_GCH_STATIC=1
+    fi
+    if test "$enable_shared" = "yes"; then
+        X_GCH_SHARED=1
+    fi
+else
+    x_gch_result="no"
+fi
+
+AC_MSG_RESULT([$x_gch_result])
+
+AC_SUBST([X_GCH])
+AC_SUBST([X_GCH_FLAGS])
+AC_SUBST([X_GCH_STATIC])
+AC_SUBST([X_GCH_STATIC_FLAGS])
+AC_SUBST([X_GCH_SHARED])
+AC_SUBST([X_GCH_SHARED_FLAGS])
+
+###############################################################################
+# check for --disable-largefile
+###############################################################################
+
+if test "$enable_largefile" != "no" -a "$ac_cv_header_unistd_h" = "yes"; then
+    case ${host_cpu} in
+        i?86|ppc)
+            AC_LANG(C++)
+            AC_CACHE_CHECK([if LFS (large file support) is required],[x_cv_largefile],[
+                AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <unistd.h>
+#ifndef _LFS_LARGEFILE
+    error: not LFS conformant
+#endif
+                ]])],[x_cv_largefile=yes],[x_cv_largefile=no])
+            ])
+            if test "$x_cv_largefile" = "yes"; then
+                AC_DEFINE([NEED_LFS_ACTIVATION],[1],[Define to 1 if LFS should be activated])
+            fi
+            ;;
+    esac
+fi
+
+###############################################################################
+# set arch flags
+###############################################################################
+
+if test "$GXX" = "yes"; then
+    AC_MSG_CHECKING([arch flags])
+    x_flags=
+    x_save_IFS=$IFS; IFS=,
+    for arch in ${enable_ub}; do
+        case "$arch" in
+            ""|no)
+                ;;
+            yes)
+                case ${host} in
+                    *-*-darwin*)
+                        x_flags="$xflags -arch i386 -arch x86_64 -arch ppc -arch ppc64"
+                        ;;
+                esac
+                ;;
+            *)
+                x_flags="$x_flags -arch $arch"
+                ;;
+        esac
+    done
+    IFS=$x_save_IFS
+
+    case "$enable_bi" in
+        ""|no|yes)
+            ;;
+        *)
+            x_flags="$x_flags -m${enable_bi}"
+            ;;
+    esac
+
+    if test -n "$x_flags"; then
+        AC_MSG_RESULT([$x_flags])
+        X_CXX_ARCH="$X_CXX_ARCH $x_flags"
+    else
+        AC_MSG_RESULT([none])
+    fi
+
+    x_flags=
+fi
+
+###############################################################################
+# disable debugging
+###############################################################################
+
+if test "$enable_debug" = "no"; then
+    changequote(<<,>>)dnl
+    if test "$GCC" = "yes"; then
+        CFLAGS=`echo "$CFLAGS" | sed -e 's/-g[^ ]*[ ]*//' -e 's/^ //' -e 's/ $//'`
+    fi
+    if test "$GXX" = "yes"; then
+        CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's/-g[^ ]*[ ]*//' -e 's/^ //' -e 's/ $//'`
+    fi
+    changequote([,])dnl
+fi
+
+###############################################################################
+# disable optimizing
+###############################################################################
+
+if test "$enable_optimize" = "no"; then
+    changequote(<<,>>)dnl
+    if test "$GCC" = "yes"; then
+        CFLAGS=`echo "$CFLAGS" | sed -e 's/-O[^ ]*[ ]*//' -e 's/^ //' -e 's/ $//'`
+    fi
+    if test "$GXX" = "yes"; then
+        CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's/-O[^ ]*[ ]*//' -e 's/^ //' -e 's/ $//'`
+    fi
+    changequote([,])dnl
+fi
+
+###############################################################################
+# checks for library functions
+###############################################################################
+
+###############################################################################
+# conditional compilation
+###############################################################################
+
+AM_CONDITIONAL([ADD_PLATFORM_POSIX],[test "$X_PLATFORM" = "posix"])
+AM_CONDITIONAL([ADD_PLATFORM_WIN32],[test "$X_PLATFORM" = "win32"])
+
+AM_CONDITIONAL([ADD_UTIL],[test "$enable_util" != "no"])
+AM_CONDITIONAL([ADD_MANS],[test "$X_PLATFORM" != "win32"])
+
+###############################################################################
+# declare common substitutions
+###############################################################################
+
+AC_SUBST([X_CXX_ARCH])
+AC_SUBST([X_CXX_W])
+AC_SUBST([X_CXX_D])
+AC_SUBST([X_CXX_I])
+AC_SUBST([X_MINGW_LIBS])
+
+###############################################################################
+# output files
+###############################################################################
+
+AC_CONFIG_HEADERS([libplatform/config.h])
+
+AC_CONFIG_FILES([GNUmakefile])
+AC_CONFIG_FILES([include/mp4v2/project.h])
+AC_CONFIG_FILES([project/project.m4])
+
+AC_OUTPUT
diff --git a/doc/GNUmakefile.mk b/doc/GNUmakefile.mk
new file mode 100644
index 0000000..131b03f
--- /dev/null
+++ b/doc/GNUmakefile.mk
@@ -0,0 +1,387 @@
+###############################################################################
+##
+##  makefile for documentation creation
+##
+##  INTPUT FILES (SOURCE/doc)
+##      doc/
+##          dox/            <-- generated API docs support files
+##          doxygen/        <-- generated API docs support files
+##          texi/           <-- hand-written project articles
+##
+##  OUTPUT FILES (BUILD/doc)
+##      doc/
+##      doc/
+##          api/
+##              html/   <-- API in html format
+##              xml/    <-- API in xml format
+##          articles/
+##              html/   <-- articles in html format
+##              txt/    <-- articles in txt format
+##              wiki/   <-- articles in Google Code Wiki format
+##              xml/    <-- articles in xml-texinfo format
+##          man/
+##              man1/   <-- man-pages for utilities
+##
+###############################################################################
+
+# Conventional VPATH directive breaks too many things so we use vpath and its
+# pattern-spec feature. If new files or extensions are introduced they may need
+# to be added here.
+
+VPATH =
+
+vpath COPYING       $(top_srcdir)
+vpath INSTALL       $(top_srcdir)
+vpath README        $(top_srcdir)
+vpath autoaux/%     $(top_srcdir)
+vpath %.1           $(top_srcdir)
+vpath %.cpp         $(top_srcdir)
+vpath %.h           $(top_srcdir)
+vpath %.m4          $(top_srcdir)
+vpath %.tcc         $(top_srcdir)
+
+###############################################################################
+
+SOURCE/ = $(top_srcdir)/
+BUILD/  = $(top_builddir)/
+
+DOC.in/  = $(SOURCE/)doc/
+DOC.out/ = $(BUILD/)doc/
+
+DOC.out.api/      = $(DOC.out/)api/
+DOC.out.articles/ = $(DOC.out/)articles/
+DOC.out.site/     = $(DOC.out/)site/
+
+###############################################################################
+
+M4       = m4
+M4.flags = -I$(BUILD/)project
+M4.cmd   = $(M4) $(M4.flags) $(1) > $(2)
+M4.deps  = $(BUILD/)project/project.m4
+
+ifeq ($(FOUND_HELP2MAN),yes)
+HELP2MAN       = help2man
+HELP2MAN.flags = -m "$(PROJECT_name) Utilities" -N
+HELP2MAN.cmd   = $(HELP2MAN) $(HELP2MAN.flags) ./$(1) -o $(2)
+else
+HELP2MAN.cmd = touch $(2)
+endif
+
+MAKEINFO.flags      = -I$(DOC.in/)texi -I$(DOC.out/)texi
+MAKEINFO.flags.html = --html --no-headers --no-split
+MAKEINFO.flags.txt  = --plaintext --no-headers
+MAKEINFO.flags.xml  = --xml --output-indent=4
+MAKEINFO.cmd        = makeinfo $(MAKEINFO.flags) $(MAKEINFO.flags.$(1)) $(2) -o $(3)
+
+HTMLCOMBINE = $(SOURCE/)project/htmlcombine.py
+HTMLCOMBINE.cmd   = $(HTMLCOMBINE) --header $(1) --footer $(2) --body $(3) > $(4)
+HTMLCOMBINE.deps  = $(HTMLCOMBINE)
+
+XML2WIKI       = $(SOURCE/)project/xml2wiki.py
+XML2WIKI.flags = --date --toc
+XML2WIKI.cmd   = $(XML2WIKI) $(XML2WIKI.flags) $(1) > $(2)
+XML2WIKI.deps  = $(XML2WIKI)
+
+DOXYGEN     = doxygen
+DOXYGEN.cmd = $(DOXYGEN) $(1)
+
+###############################################################################
+
+DOC.m4.out = \
+    doc/texi/base/project.texi \
+    doc/doxygen/Doxyfile       \
+    doc/doxygen/header.html    \
+    doc/doxygen/footer.html    \
+    doc/html/header.html       \
+    doc/html/footer.html
+
+DOC.man.utils = mp4art mp4file mp4subtitle mp4track
+DOC.man.out   = $(DOC.man.utils:%=$(DOC.out/)man/man1/%.1)
+
+DOC.texi.articles = $(wildcard $(DOC.in/)texi/*.texi $(DOC.out/)texi/*.texi)
+
+DOC.texi.includes = \
+    doc/texi/base/project.texi \
+    $(wildcard $(DOC.in/)texi/*/*.texi $(DOC.out/)texi/*/*.texi)
+
+DOC.texi2html.out = $(DOC.texi.articles:$(DOC.in/)texi/%.texi=$(DOC.out.articles/)html/%.html)
+DOC.texi2txt.out  = $(DOC.texi.articles:$(DOC.in/)texi/%.texi=$(DOC.out.articles/)txt/%.txt)
+DOC.texi2xml.out  = $(DOC.texi.articles:$(DOC.in/)texi/%.texi=$(DOC.out.articles/)xml/%.xml)
+
+DOC.xml2wiki.out  = $(DOC.texi2xml.out:$(DOC.out.articles/)xml/%.xml=$(DOC.out.articles/)wiki/%.wiki)
+
+DOC.api.out = $(DOC.out.api/).stamp
+
+DOC.site.out       = $(DOC.out.site/).stamp
+DOC.site.out.copy  = $(patsubst $(DOC.in/)html/%,$(DOC.out.site/)%, \
+    $(filter-out %.in,$(wildcard $(DOC.in/)html/*)))
+DOC.site.out.index = $(DOC.out.site/)index.html
+DOC.site.out.html  = $(patsubst $(DOC.out.articles/)html/%,$(DOC.out.site/)%, \
+    $(filter-out %/Documentation.html,$(DOC.texi2html.out)))
+
+###############################################################################
+
+MKDIRS += $(dir $(DOC.m4.out))
+MKDIRS += $(DOC.out/)man/man1/
+MKDIRS += $(foreach n,html man texi txt wiki xml,$(DOC.out.articles/)$n)
+MKDIRS += $(DOC.out.api/)
+MKDIRS += $(DOC.out.site/)
+
+###############################################################################
+
+EXTRA_DIST += \
+    $(SOURCE/)vstudio9.0/include/mp4v2/project.h \
+    $(SOURCE/)vstudio9.0/mp4v2.sln \
+    $(wildcard $(SOURCE/)vstudio9.0/*/*.vcproj)
+
+###############################################################################
+
+dist-hook: $(DOC.texi2txt.out) $(DOC.man.out)
+	rm -fr $(distdir)/doc
+	$(mkdir_p) $(distdir)/doc
+	$(INSTALL_DATA) $(DOC.in/)GNUmakefile.mk $(distdir)/doc/.
+ifneq (,$(DOC.texi2txt.out))
+	$(INSTALL_DATA) $(DOC.texi2txt.out) $(distdir)/doc/.
+endif
+ifneq (,$(DOC.man.out))
+	$(mkdir_p) $(distdir)/doc/man/man1
+	$(INSTALL_DATA) $(DOC.man.out) $(distdir)/doc/man/man1
+endif
+
+distclean-local: docclean
+
+###############################################################################
+
+$(DOC.m4.out): %: %.m4 $(M4.deps) | $(dir $(DOC.m4.out))
+	$(call M4.cmd,$<,$@)
+
+###############################################################################
+
+.PHONY: articles doc
+articles: html txt xml wiki
+doc: man articles api site
+
+.PHONY: articlesclean apiclean docclean
+articlesclean: htmlclean txtclean xmlclean wikiclean
+docclean: manclean articlesclean apiclean siteclean
+
+.PHONY: man html txt xml wiki api
+man: $(DOC.man.out)
+html: $(DOC.texi2html.out)
+txt: $(DOC.texi2txt.out)
+xml: $(DOC.texi2xml.out)
+wiki: $(DOC.xml2wiki.out)
+api: $(DOC.api.out)
+site: $(DOC.site.out)
+
+.PHONY: manclean htmlclean txtclean xmlclean wikiclean apiclean
+manclean:
+	rm -f $(DOC.man.out)
+
+htmlclean:
+	rm -f $(DOC.texi2html.out)
+
+txtclean:
+	rm -f $(DOC.texi2txt.out)
+
+xmlclean:
+	rm -f $(DOC.texi2xml.out)
+
+wikiclean:
+	rm -f $(DOC.xml2wiki.out)
+
+apiclean:
+	rm -f $(DOC.api.out)
+	rm -fr $(DOC.out.api/)html/ $(DOC.out.api/)xml/
+
+siteclean:
+	rm -fr $(DOC.out.site/)
+
+###############################################################################
+
+$(DOC.man.out): | $(dir $(DOC.man.out))
+$(DOC.man.out): $(DOC.out/)man/man1/%.1: $(BUILD/)%$(EXEEXT)
+	$(call HELP2MAN.cmd,$<,$@)
+
+$(DOC.texi2html.out): $(DOC.texi.includes) | $(dir $(DOC.texi2html.out))
+$(DOC.texi2html.out): $(DOC.out.articles/)html/%.html: $(DOC.in/)texi/%.texi
+	$(call MAKEINFO.cmd,html,$<,$@)
+
+$(DOC.texi2txt.out): $(DOC.texi.includes) | $(dir $(DOC.texi2txt.out))
+$(DOC.texi2txt.out): $(DOC.out.articles/)txt/%.txt: $(DOC.in/)texi/%.texi
+	$(call MAKEINFO.cmd,txt,$<,$@)
+
+$(DOC.texi2xml.out): $(DOC.texi.includes) | $(dir $(DOC.texi2xml.out))
+$(DOC.texi2xml.out): $(DOC.out.articles/)xml/%.xml: $(DOC.in/)texi/%.texi
+	$(call MAKEINFO.cmd,xml,$<,$@)
+
+$(DOC.xml2wiki.out): $(XML2WIKI.deps) | $(dir $(DOC.xml2wiki.out))
+$(DOC.xml2wiki.out): $(DOC.out.articles/)wiki/%.wiki: $(DOC.out.articles/)xml/%.xml
+	$(call XML2WIKI.cmd,$<,$@)
+
+$(DOC.api.out): | $(dir $(DOC.api.out))
+$(DOC.api.out): $(DOC.in/)doxygen/banner.png
+$(DOC.api.out): $(DOC.in/)doxygen/project.css
+$(DOC.api.out): $(DOC.out/)doxygen/header.html
+$(DOC.api.out): $(DOC.out/)doxygen/footer.html
+$(DOC.api.out): $(DOC.out/)doxygen/Doxyfile
+	$(call DOXYGEN.cmd,$<)
+	$(INSTALL_DATA) $(DOC.in/)doxygen/banner.png $(DOC.out.api/)html/
+	touch $@
+
+###############################################################################
+
+$(DOC.site.out): | $(DOC.out.site/)articles
+$(DOC.site.out): | $(DOC.out.site/)api
+$(DOC.site.out): $(DOC.site.out.copy)
+$(DOC.site.out): $(DOC.site.out.index)
+$(DOC.site.out): $(DOC.site.out.html)
+
+$(DOC.out.site/)articles: | html $(DOC.out.site/)
+	rm -f $@
+	ln -s ../articles/html $@
+
+$(DOC.out.site/)api: | api $(DOC.out.site/)
+	rm -f $@
+	ln -s ../api/html $@
+
+$(DOC.site.out.copy): $(DOC.out.site/)%: $(DOC.in/)html/%
+	$(INSTALL_DATA) $^ $(@D)/.
+
+$(DOC.site.out.index): $(DOC.out/)html/header.html $(DOC.out/)html/footer.html
+$(DOC.site.out.index): $(HTMLCOMBINE.deps)
+$(DOC.site.out.index): $(DOC.out.articles/)html/Documentation.html
+	$(call HTMLCOMBINE.cmd,$(word 2,$^),$(word 3,$^),$<,$@)
+
+$(DOC.site.out.html): $(DOC.out/)html/header.html $(DOC.out/)html/footer.html
+$(DOC.site.out.html): $(HTMLCOMBINE.deps)
+$(DOC.site.out.html): $(DOC.out.site/)%: $(DOC.out.articles/)html/%
+	$(call HTMLCOMBINE.cmd,$(word 2,$^),$(word 3,$^),$<,$@)
+
+###############################################################################
+
+GOOGLE.out/      = $(top_builddir)/google/
+GOOGLE.repo.doc  = $(PROJECT_repo_branch)
+
+google.clean:
+	rm -fr $(GOOGLE.out/)
+
+google.post: site wiki
+google.post: | $(GOOGLE.out/)
+google.post: google.rsync google.rm google.add google.propset
+
+$(GOOGLE.out/):
+	svn co --depth immediates $(PROJECT_repo_root) $(GOOGLE.out/)
+	svn update --set-depth immediates google/wiki
+	svn update --set-depth infinity google/doc/$(GOOGLE.repo.doc)
+
+google.rsync:
+	rsync -vrptPL --delete \
+	    --exclude=".svn/*" --exclude=".svn" \
+	    --exclude="*/.svn/*" --exclude="*/.svn" \
+	    --exclude=".stamp" --exclude="*/.stamp" \
+	    --exclude="*.m4" --exclude="*/*.m4" \
+	    $(DOC.out.site/). $(GOOGLE.out/)doc/$(GOOGLE.repo.doc)/.
+	$(INSTALL_DATA) $(DOC.out.articles/)wiki/BuildRepository.wiki $(GOOGLE.out/)wiki/.
+	$(INSTALL_DATA) $(DOC.out.articles/)wiki/BuildSource.wiki $(GOOGLE.out/)wiki/.
+
+google.rm:
+	@files=`svn status $(GOOGLE.out/) | grep '^!' | awk '{ print $$2 }'`; \
+	if [ -n "$$files" ]; then \
+	    svn rm $$files; \
+	fi
+
+google.add:
+	@files=`svn status $(GOOGLE.out/) | grep '^?' | awk '{ print $$2 }'`; \
+	if [ -n "$$files" ]; then \
+	    svn add $$files; \
+	fi
+
+google.propset:
+	find $(GOOGLE.out/)doc/$(GOOGLE.repo.doc) -type f -a \( -name "*.html" -o -name "*.css" \) -print0 \
+	    | xargs -0 svn propset svn:eol-style native
+	find $(GOOGLE.out/)doc/$(GOOGLE.repo.doc) -type f -a -name "*.html" -print0 \
+	    | xargs -0 svn propset svn:mime-type "text/html"
+
+###############################################################################
+
+dist-hb: distdir=lib$(PACKAGE)
+dist-hb: distdir
+	tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >lib$(PACKAGE)-$(VERSION).tar.gz
+	$(am__remove_distdir)
+
+###############################################################################
+
+## Precompiled header support is tricky given libtool's complexity and its
+## historical aversion to supporting it.
+##
+## What we do is add rules to precompile. Unfortunately, we cannot
+## automatically determine what flags libtool adds for compilation variants;
+## ie: PIC or non-PIC compilation. So instead we will hardcode the flags
+## to match. The reason for this is the precompiled-header will not be used if
+## (relevent) compilation flags differ.
+
+CXX.gch.static.flags = $(X_GCH_STATIC_FLAGS)
+CXX.gch.static.in    = libplatform/impl.h src/impl.h libutil/impl.h
+CXX.gch.static.out   = $(CXX.gch.static.in:%=$(BUILD/)%.gch/static)
+
+CXX.gch.shared.flags = $(X_GCH_SHARED_FLAGS)
+CXX.gch.shared.in    = libplatform/impl.h src/impl.h libutil/impl.h
+CXX.gch.shared.out   = $(CXX.gch.shared.in:%=$(BUILD/)%.gch/shared)
+
+CXX.gch.exe.flags = $(X_GCH_FLAGS)
+CXX.gch.exe.in    = util/impl.h
+CXX.gch.exe.out   = $(CXX.gch.exe.in:%=$(BUILD/)%.gch/exe)
+
+CXX.gch.static.dependents = $(libmp4v2_la_OBJECTS)
+CXX.gch.shared.dependents = $(libmp4v2_la_OBJECTS)
+
+CXX.gch.exe.dependents = \
+    $(mp4art_OBJECTS)        \
+    $(mp4chaps_OBJECTS)      \
+    $(mp4extract_OBJECTS)    \
+    $(mp4file_OBJECTS)       \
+    $(mp4info_OBJECTS)       \
+    $(mp4subtitle_OBJECTS)   \
+    $(mp4syncfiles_OBJECTS)  \
+    $(mp4tags_OBJECTS)       \
+    $(mp4track_OBJECTS)      \
+    $(mp4trackdump_OBJECTS)
+
+ifeq ($(X_GCH_STATIC),1)
+$(CXX.gch.static.dependents): $(CXX.gch.static.out)
+endif
+
+ifeq ($(X_GCH_SHARED),1)
+$(CXX.gch.shared.dependents): $(CXX.gch.shared.out)
+endif
+
+ifeq ($(X_GCH),1)
+$(CXX.gch.exe.dependents): $(CXX.gch.exe.out)
+endif
+
+$(CXX.gch.static.out): | $(sort $(dir $(CXX.gch.static.out)))
+$(CXX.gch.static.out): $(BUILD/)%.gch/static: %
+	$(CXXCOMPILE) $(CXX.gch.static.flags) -c $< -o $@
+
+$(CXX.gch.shared.out): | $(sort $(dir $(CXX.gch.shared.out)))
+$(CXX.gch.shared.out): $(BUILD/)%.gch/shared: %
+	$(CXXCOMPILE) $(CXX.gch.shared.flags) -c $< -o $@
+
+$(CXX.gch.exe.out): | $(sort $(dir $(CXX.gch.exe.out)))
+$(CXX.gch.exe.out): $(BUILD/)%.gch/exe: %
+	$(CXXCOMPILE) $(CXX.gch.exe.flags) -c $< -o $@
+
+MKDIRS += $(dir $(CXX.gch.static.out))
+MKDIRS += $(dir $(CXX.gch.shared.out))
+MKDIRS += $(dir $(CXX.gch.exe.out))
+
+clean-local:
+	rm -f $(CXX.gch.static.out)
+	rm -f $(CXX.gch.shared.out)
+	rm -f $(CXX.gch.exe.out)
+
+###############################################################################
+
+$(sort $(MKDIRS)):
+	$(mkdir_p) $@
diff --git a/doc/MP4.3 b/doc/MP4.3
new file mode 100644
index 0000000..9b3378e
--- /dev/null
+++ b/doc/MP4.3
@@ -0,0 +1,47 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "DESCRIPTION"
+.LP 
+The MP4 library provides an API to create and modify mp4 files as defined by ISO\-IEC:14496\-1:2001 MPEG\-4 Systems. This file format is derived from Apple's QuickTime file format that has been used as a multimedia file format in a variety of platforms and applications. It is a very powerful and extensible format that can accomodate practically any type of media.
+.LP 
+The basic structure of an mp4 file is that the file is a container for one or more tracks. These tracks contain one type of media, such as audio or video. Each track has its own timeline, samples, and properties. An example of a sample is a frame of video. The file describes how to synchronize the timelines of the tracks and the aggregate properties of the tracks.
+.LP 
+The MP4 library is focussed on providing an easy to use API for the mp4 file format. It has been used with an encoder, a server, a player, and a number of mp4 utilities. However, it may not be adequate for multimedia editors that wish to work directly with mp4 files. It can be used by these type of tools to export an mp4 file. (The library is open source so contributions of extensions to the library are welcome.)
+.LP 
+In providing a easy to use API not all the information in the mp4 file is directly exposed via the API. To accomodate applications that need access to information not otherwise available via the API there are file and track level generic get and set property routines that use arbitary string property names. To use these routines you will need to be familar with the mp4 file specification or be willing to wade thru the output of MP4Dump() to determine what you want. See MP4GetIntegerProperty() for more details.
+.SH "INVOCATION"
+.LP 
+The libary API is defined in <mp4.h> which includes all the necessary dependent include files.
+.LP 
+The MP4 library can be used by either C or C++ programs. The calling convention is C, but if C++ is used then the default argument feature of that language can be used. 
+.LP 
+For example:
+.br 
+	MP4Create("foo.mp4", 0, 0, 0); /* OK in C++ and C */
+.br 
+	MP4Create("foo.mp4"); /* OK in C++, ERROR in C */
+.SH "EXAMPLES"
+See mpeg4ip/lib/mp4v2/util and mpeg4ip/lib/mp4v2/test for simple example programs that use the MP4 library.
+.LP 
+In particular:
+.br 
+	mp4nullcreate.cpp 
+.br 
+	Program that creates an empty mp4 file.
+.LP 
+	mp4dump.cpp	
+.br 
+	Simple program to print a text version of an mp4 file
+.LP 
+	mp4extract.cpp 
+.br 
+	Program to read each track and sample and place them in a separate file to ease inspection or recombination.
+.LP 
+	mp4broadcaster.cpp
+.br 
+	Program to broadcast the hinted tracks of an mp4 file using RTP.
+.LP 
+	mp4nullvplayer.cpp
+.br 
+	Program that reads the video track but doesn't actually render the video.
+.LP 
+For more complete applications that use the MP4 library see mpeg4ip/server/mp4creator, mpeg4ip/server/mp4live, and mpeg4ip/player/src.
diff --git a/doc/MP4AddRtpESConfigurationPacket.3 b/doc/MP4AddRtpESConfigurationPacket.3
new file mode 100644
index 0000000..989febc
--- /dev/null
+++ b/doc/MP4AddRtpESConfigurationPacket.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddRtpESConfigurationPacket\fR \- Add ES configuration information to an RTP hint
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AddRtpESConfigurationPacket\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddRtpESConfigurationPacket\fR adds a packet to the current RTP hint that contains a copy of the elementary stream configuration information of the reference media track. Some RTP payloads require this information to be transmitted at the start of streaming or periodically during streaming.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetTrackESConfiguration(3)
diff --git a/doc/MP4AddRtpHint.3 b/doc/MP4AddRtpHint.3
new file mode 100644
index 0000000..8b505af
--- /dev/null
+++ b/doc/MP4AddRtpHint.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddRtpHint\fR \- Add an RTP hint
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AddRtpHint\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddRtpHint\fR creates a new hint sample for the specified hint track and enables subsequent calls to MP4AddRtpPacket() to create the RTP packets associated with this hint. After all the RTP packets for the hint have been created, MP4WriteRtpHint() should be called to write the hint to the track.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpPacket(3) MP4WriteRtpHint (3)
diff --git a/doc/MP4AddRtpImmediateData.3 b/doc/MP4AddRtpImmediateData.3
new file mode 100644
index 0000000..81cfc79
--- /dev/null
+++ b/doc/MP4AddRtpImmediateData.3
@@ -0,0 +1,42 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddRtpImmediateData\fR \- Add immediate data to an RTP packet
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AddRtpImmediateData\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	u_int8_t* \fIpBytes\fP,
+.br 
+	u_int32_t \fInumBytes\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIpBytes\fP
+Specifies a pointer to the immediate data that should be included in the current RTP packet.
+.TP 
+\fInumBytes\fP
+Specifies the length in bytes of the immediate data that should be included in the current RTP packet.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddRtpImmediateData\fR adds immediate data to the current pending RTP packet. Typically, this is used to add RTP payload specific headers to RTP packets. Note that the size of a block of immediate data is limited to 14 bytes. But multiple immediate data blocks can be added if more space is needed.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpPacket (3) MP4AddRtpImmediateData (3)
diff --git a/doc/MP4AddRtpPacket.3 b/doc/MP4AddRtpPacket.3
new file mode 100644
index 0000000..14f2600
--- /dev/null
+++ b/doc/MP4AddRtpPacket.3
@@ -0,0 +1,42 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddRtpPacket\fR \- Add an RTP packet
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AddRtpPacket\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	bool setMBit = false,
+.br 
+	int32 transmitOffset = 0
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIsetMBit\fP
+Specifies the value of the RTP packet header marker bit for this packet. The value depends on the rules of the RTP payload used for this hint track.
+.TP 
+\fItransmitOffset\fP
+Specifies an offset to apply to the normal transmission time of this packet. The purpose of this offset is to allow smoothing of packet transmission over the duration of the hint.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddRtpPacket\fR creates a new RTP packet for the currently pending RTP hint sample for the specified hint track. It also enables subsequent calls to MP4AddRtpImmediateData() and MP4AddRtpSampleData to add data to the RTP packets. After all the RTP packets for the hint have been created, MP4WriteRtpHint() should be called to write the hint to the track.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpHint(3) MP4AddRtpImmediateData (3) MP4AddRtpSampleData (3) MP4WriteRtpHint (3)
diff --git a/doc/MP4AddRtpSampleData.3 b/doc/MP4AddRtpSampleData.3
new file mode 100644
index 0000000..95642c6
--- /dev/null
+++ b/doc/MP4AddRtpSampleData.3
@@ -0,0 +1,47 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddRtpSampleData\fR \- Add media sample data to an RTP packet
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AddRtpSampleData\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	MP4SampleId \fIsampleId\fP,
+.br 
+	u_int32_t \fIdataOffset\fP,
+.br 
+	u_int32_t \fIdataLength\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIsampleId\fP
+Specifies the reference media sample id from which the media data should be taken.
+.TP 
+\fIdataOffset\fP
+Specifies the byte offset in the specified media sample where data should be taken from for the current RTP packet.
+.TP 
+\fIdataLength\fP
+Specifies the length in bytes of the media data that should be included in the current RTP packet.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddRtpSampleData\fR adds a reference in the current pending RTP packet to the media data in the specified media sample of the reference media track. Note this is a reference, not a copy, of the media data.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpPacket (3) MP4AddRtpImmediateData (3)
diff --git a/doc/MP4AddRtpVideoHint.3 b/doc/MP4AddRtpVideoHint.3
new file mode 100644
index 0000000..a845e88
--- /dev/null
+++ b/doc/MP4AddRtpVideoHint.3
@@ -0,0 +1,49 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddRtpVideoHint\fR \- Add an RTP video specific hint
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AddRtpVideoHint\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	bool \fIisBFrame\fP = false,
+.br 
+	u_int32_t \fItimestampOffset\fP = 0
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIisBFrame\fP
+Specifies if this hint will contain packets for a video B frame
+.TP 
+\fItimestampOffset\fP
+Specifies a positive offset to add to the RTP timestamp for this hint. Caveat: the value is in the hint track timescale.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddRtpVideoHint\fR is an extended version of MP4AddRtpHint specifically to handle MPEG video frames. 
+.LP 
+The isBFrame parameter allows the packets in the RTP hint to be marked as belonging to a video B frame. This can be useful to a streaming server if packets must be dropped due to system load or network congestion. No other video frames are dependent on the contents of B frames, so they are least damaging type of frames to drop.
+.LP 
+The timestampOffset parameter allows an offset to be added to the RTP timestamp of the packets in the RTP hint. This is necessary for MPEG video that contains B frames since the video frames are transmitted out of order with respect to when they should be rendered. I.e I and P frames are transmitted before any B frames that depend on them. The RTP timestamp must represent the rendering time of the data in the packets hence an offset must be added.
+.LP 
+Note: The timestampOffset is equivalent to the sample rendering offset of a video media track. See MP4GetSampleRenderingOffset().
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpHint(3) MP4AddRtpPacket(3) MP4WriteRtpHint (3)
diff --git a/doc/MP4AddTrackEdit.3 b/doc/MP4AddTrackEdit.3
new file mode 100644
index 0000000..a3809fd
--- /dev/null
+++ b/doc/MP4AddTrackEdit.3
@@ -0,0 +1,64 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AddTrackEdit\fR \- Add an edit segment to a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4TrackId \fBMP4AddTrackEdit\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP,
+.br 
+	MP4Timestamp \fIstartTime\fP = 0,
+.br 
+	MP4Duration \fIduration\fP = 0,
+.br 
+	bool \fIdwell\fP = false
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the desired position in the edit list sequence for the new edit segment. If the value is MP4_INVALID_EDIT_ID, then the edit segment is added at the end of the existing edit list. Note editId's start with the value of 1, not 0.
+.TP 
+\fIstartTime\fP
+Specifies the starting time of the edit segment in the track time scale.
+.TP 
+\fIduration\fP
+Specifies the duration of the edit segment in the track time scale.
+.TP 
+\fIdwell\fP
+If false, the track media should be played at its normal rate. If true, the media should be paused for the duration of this edit segment. This is a mechanism by which one can delay the start of a media track.
+ 
+.SH "RETURN VALUES"
+.LP 
+Upon success, the edit id of the new edit segment. Upon an error, MP4_INVALID_EDIT_ID.
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AddTrackEdit\fR adds an edit segment to the track edit list.
+.LP 
+The track edit list is a feature that allows creation of an alternate timeline for the track, typically cutting out segments of the full track to form an shorten, cleaned up version. The edit segments that form the edit list are a sequence of track start times and durations, they do not alter the track media in any way. I.e. no data can be lost via edit list operations.
+.LP 
+To read out the editted version of the track, use MP4ReadSampleFromEditTime() instead of MP4ReadSample().
+.LP 
+To export the editted version of the track to a new track, potentially in a new mp4 file, see MP4CopyTrack().
+.LP 
+Note with many media encodings such as MPEG\-4, AAC, and MP3, care must be taken when choosing the edit segment start times. E.g. for video tracks a reference or key frame should be selected as the starting sample of any edit segment. For audio tracks, an audio sample start time should be used.
+
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4DeleteTrackEdit(3) MP4ReadSampleFromEditTime(3) MP4CopyTrack(3)
diff --git a/doc/MP4AppendHintTrackSdp.3 b/doc/MP4AppendHintTrackSdp.3
new file mode 100644
index 0000000..d75eeba
--- /dev/null
+++ b/doc/MP4AppendHintTrackSdp.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AppendHintTrackSdp\fR \- Add to the SDP media level description of the hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AppendHintTrackSdp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	const char* sdpString
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIsdpString\fP
+Specifies the addition to the hint track sdp string.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AppendHintTrackSdp\fR appends the specified string to the SDP (IETF RFC 2327) media level fragment for the hint track. This is used by a streaming server to create a complete SDP description of the multimedia session represented by the file.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetHintTrackSdp(3) MP4SetHintTrackSdp(3)
diff --git a/doc/MP4AppendSessionSdp.3 b/doc/MP4AppendSessionSdp.3
new file mode 100644
index 0000000..9d6d011
--- /dev/null
+++ b/doc/MP4AppendSessionSdp.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4AppendSessionSdp\fR \- Add to the SDP session level description of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4AppendSessionSdp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	const char* sdpString
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIsdpString\fP
+Specifies the addition to the session sdp string.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4AppendSessionSdp\fR appends the specified string to the SDP (IETF RFC 2327) session level fragment for the file. This is used by a streaming server to create a complete SDP description of the multimedia session represented by the file.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetSessionSdp(3) MP4SetSessionSdp(3)
diff --git a/doc/MP4BinaryToBase16.3 b/doc/MP4BinaryToBase16.3
new file mode 100644
index 0000000..fbe16df
--- /dev/null
+++ b/doc/MP4BinaryToBase16.3
@@ -0,0 +1,37 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4BinaryToBase16\fR \- Convert binary data to a base 16 string
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+char* \fBMP4BinaryToBase16\fR(
+.br 
+	const u_int8_t* \fIpData\fP,
+.br 
+	u_int32_t \fIdataSize\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIpData\fP
+Specifies the pointer to the binary data.
+.TP 
+\fIdataSize\fP
+Specifies the size in bytes of the binary data.
+.SH "RETURN VALUES"
+.LP 
+Upon success, a null terminated string representing the data in base 16. Upon error, NULL.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4BinaryToBase16\fR converts binary data to a base 16 string. This encoding maps groups of 4 bits into the character set [0\-9a\-f]. The string is in malloc'd memory, so the caller is responsible for free'ing the memory.
+.LP 
+This utility is useful for generating the SDP descriptions for some RTP payloads.
+.LP 
+Example:
+	0x12, 0xAB \-> "12ab"
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4BinaryToBase64.3 b/doc/MP4BinaryToBase64.3
new file mode 100644
index 0000000..d7d29dd
--- /dev/null
+++ b/doc/MP4BinaryToBase64.3
@@ -0,0 +1,37 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4BinaryToBase64\fR \- Convert binary data to a base 64 string
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+char* \fBMP4BinaryToBase64\fR(
+.br 
+	const u_int8_t* \fIpData\fP,
+.br 
+	u_int32_t \fIdataSize\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIpData\fP
+Specifies the pointer to the binary data.
+.TP 
+\fIdataSize\fP
+Specifies the size in bytes of the binary data.
+.SH "RETURN VALUES"
+.LP 
+Upon success, a null terminated string representing the data in base 64. Upon error, NULL.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4BinaryToBase64\fR converts binary data to a base 64 string. This encoding maps groups of 6 bits into the character set [A\-Za\-z0\-9+/=]. The string is in malloc'd memory, so the caller is responsible for free'ing the memory.
+.LP 
+This utility is useful for generating the SDP descriptions for some RTP payloads.
+.LP 
+Example:
+	0x12, 0xAB \-> "Eqs="
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4CloneTrack.3 b/doc/MP4CloneTrack.3
new file mode 100644
index 0000000..edfd672
--- /dev/null
+++ b/doc/MP4CloneTrack.3
@@ -0,0 +1,47 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4CloneTrack\fR \- Make a clone of a specified track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4TrackId \fBMP4CloneTrack\fR(
+.br 
+	MP4FileHandle \fIsrcFile\fP,
+.br 
+	MP4TrackId \fIsrcTrackId\fP,
+.br 
+	MP4FileHandle \fIdstFile\fP = MP4_INVALID_FILE_HANDLE,
+.br 
+	MP4TrackId \fIdstHintTrackReferenceTrack\fP = MP4_INVALID_TRACK_ID
+.br
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIsrcFile\fP
+Specifies the mp4 file of the source track of the operation.
+.TP 
+\fIsrcTrackId\fP
+Specifies the track id of the track to be cloned.
+.TP 
+\fIdstFile\fP
+Specifies the mp4 file of the new, cloned track. If the value is MP4_INVALID_FILE_HANDLE, the new track is created in the same file as the source track. 
+.TP
+\fIdstHintTrackReferenceTrack\fP
+When cloning a hint track, this parameter specifies the track id of the reference track in the destination file.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the track id of the new track. Upon an error, MP4_INVALID_TRACK_ID.
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4CloneTrack\fR creates a new track to an mp4 file that is a copy of an existing track with respect to the track media type, and other control information. 
+.LP 
+Note this function does not copy the media samples of the source track to the new track. If you want to do that use MP4CopyTrack() instead.
+
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4CopyTrack(3)
diff --git a/doc/MP4ConvertFromMovieDuration.3 b/doc/MP4ConvertFromMovieDuration.3
new file mode 100644
index 0000000..9afa50b
--- /dev/null
+++ b/doc/MP4ConvertFromMovieDuration.3
@@ -0,0 +1,37 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4ConvertFromMovieDuration\fR \- Convert a duration from the movie (file) time scale to a specified time scale.
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int64_t \fBMP4ConvertFromMovieDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4Duration \fIduration\fP,
+.br 
+	u_int32_t \fInewTimeScale\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIduration\fP
+Specifies the duration that is to be converted.
+.TP 
+\fInewTimeScale\fP
+Specifies the new time scale in ticks per second to which the duration should be converted.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the duration in the new time scale units. Upon error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4ConvertFromMovieDuration\fR converts a duration such as the total movie (file) duration from the movie time scale to another specified time scale.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetDuration(3)
diff --git a/doc/MP4ConvertFromTrackTimestamp.3 b/doc/MP4ConvertFromTrackTimestamp.3
new file mode 100644
index 0000000..62274a8
--- /dev/null
+++ b/doc/MP4ConvertFromTrackTimestamp.3
@@ -0,0 +1,43 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4ConvertFromTrackTimestamp\fR \- Convert a timestamp from the track time scale to a specified time scale.
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int64_t \fBMP4ConvertFromTrackTimestamp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4Timestamp \fItimestamp\fP,
+.br 
+	u_int32_t \fInewTimeScale\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track from which the timestamp originates.
+.TP 
+\fItimestamp\fP
+Specifies the timestamp that is to be converted.
+.TP 
+\fInewTimeScale\fP
+Specifies the new time scale in ticks per second to which the timestamp should be converted.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the timestamp in the new time scale units. Upon error, 0xFFFFFFFFFFFFFFFF.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4ConvertFromTrackTimestamp\fR converts a timestamp such as a sample start time from the track time scale to another specified time scale. This can be used by a player application to map all track samples to a common time scale.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4ConvertToTrackTimestamp(3)
diff --git a/doc/MP4ConvertToTrackTimestamp.3 b/doc/MP4ConvertToTrackTimestamp.3
new file mode 100644
index 0000000..ee994e0
--- /dev/null
+++ b/doc/MP4ConvertToTrackTimestamp.3
@@ -0,0 +1,42 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4ConvertToTrackTimestamp\fR \- Convert a timestamp from a specified time scale to the track time scale.
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Timestamp \fBMP4ConvertToTrackTimestamp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	u_int64_t \fItimestamp\fP,
+.br 
+	u_int32_t \fIoldTimeScale\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track from which the duration originates.
+.TP 
+\fItimestamp\fP
+Specifies the timestamp that is to be converted.
+.TP 
+\fIoldTimeScale\fP
+Specifies the time scale in ticks per second in which the timestamp is currently expressed.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the timestamp in the track time scale units. Upon error, MP4_INVALID_TIMESTAMP.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4ConvertToTrackTimestamp\fR converts a timestamp such as a sample start time from the specified time scale to the track time scale. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4ConvertFromTrackTimestamp(3)
diff --git a/doc/MP4CopyTrack.3 b/doc/MP4CopyTrack.3
new file mode 100644
index 0000000..7cf29c9
--- /dev/null
+++ b/doc/MP4CopyTrack.3
@@ -0,0 +1,54 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4CopyTrack\fR \- Make a copy of a specified track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4TrackId \fBMP4CopyTrack\fR(
+.br 
+	MP4FileHandle \fIsrcFile\fP,
+.br 
+	MP4TrackId \fIsrcTrackId\fP,
+.br 
+	MP4FileHandle \fIdstFile\fP = MP4_INVALID_FILE_HANDLE,
+.br 
+	bool \fIapplyEdits\fP = false,
+.br 
+	MP4TrackId \fIdstHintTrackReferenceTrack\fP = MP4_INVALID_TRACK_ID
+.br
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIsrcFile\fP
+Specifies the mp4 file of the source track of the operation.
+.TP 
+\fIsrcTrackId\fP
+Specifies the track id of the track to be copied.
+.TP 
+\fIdstFile\fP
+Specifies the mp4 file of the new, copied track. If the value  is MP4_INVALID_FILE_HANDLE, the new track is created in the same file as the source track. 
+.TP 
+\fIapplyEdits\fP
+Specifies if the track edit list is to be applied during the copying of media samples. If false, then all samples are copied, if true then only those samples included by the track edit list are copied.
+.TP
+\fIdstHintTrackReferenceTrack\fP
+When cloning a hint track, this parameter specifies the track id of the reference track in the destination file.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the track id of the new track. Upon an error, MP4_INVALID_TRACK_ID.
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4CopyTrack\fR creates a new track to an mp4 file that is a copy of an existing track with respect to the track media type, other control information, and media samples.
+.LP 
+The applyEdits parameter of this function allows for easy creation of standalone clips from a larger mp4 file. To do this use MP4AddTrackEdit() to specify the start and duration of the clip, and then use MP4CopyTrack() to export that portion of the media to a new mp4 file.
+.LP 
+Note if you do not want to copy the media samples, but just want to create a track with the same type and control information of the source track use MP4CloneTrack().
+
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4CloneTrack(3) MP4AddTrackEdit(3)
diff --git a/doc/MP4DeleteTrack.3 b/doc/MP4DeleteTrack.3
new file mode 100644
index 0000000..28ba106
--- /dev/null
+++ b/doc/MP4DeleteTrack.3
@@ -0,0 +1,35 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4DeleteTrack\fR \- Delete a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4DeleteTrack\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4DeleteTrack\fR deletes the control information associated with the specified track. The trackId will become invalid if this call succeeds.
+.LP 
+Note that the samples associated with this track are not deleted with this call. This can be accomplished via MP4Optimize(). The reason for this is that multiple tracks can reference the same samples so a global view must be taken when deleting them.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4DeleteTrackEdit.3 b/doc/MP4DeleteTrackEdit.3
new file mode 100644
index 0000000..3359dc4
--- /dev/null
+++ b/doc/MP4DeleteTrackEdit.3
@@ -0,0 +1,41 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4DeleteTrackEdit\fR \- Delete a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4DeleteTrackEdit\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment to be deleted.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4DeleteTrackEdit\fR deletes the specified track edit segment. Note that since editId's form a sequence, deleting an editId will cause all edit segments with editId's greater than the deleted one to be reassigned to their editId minus one.
+.LP 
+Deleting an edit segment does not delete any media samples.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddTrackEdit(3)
diff --git a/doc/MP4FindTrackId.3 b/doc/MP4FindTrackId.3
new file mode 100644
index 0000000..f0eea59
--- /dev/null
+++ b/doc/MP4FindTrackId.3
@@ -0,0 +1,51 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4FindTrackId\fR \- Find a track id
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4TrackId \fBMP4FindTrackId\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	u_int16_t \fIindex\fP,
+.br 
+	const char* \fItype\fP = NULL,
+.br 
+	u_int8_t \fIsubType\fP = 0
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIindex\fP
+Specifies which track is desired from matching tracks. 
+.TP 
+\fItype\fP
+Specifies the type of track to be matched. A NULL value implies any type of track. See MP4GetTrackType() for predefined values.
+.TP 
+\fIsubType\fP
+Specifies the subtype of the track to be matched. Subtypes are only defined for audio and video tracks, see MP4GetAudioTrackType() and MP4GetVideoTrackType() for predefined values. A zero value implies any subtype.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the track id of the specified track. Upon an error, MP4_INVALID_TRACK_ID.
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4FindTrackId\fR gets the track id associated with the index'th track of the specified track type. For example, to get the track id of the first video track:
+.LP 
+	MP4FindTrackId(hFile, 0, MP4_VIDEO_TRACK_TYPE);
+.LP 
+For audio and video tracks, a subtype can be specified to find a track of a particular encoding. For example, to get the track id of the first audio track encoded with MPEG\-1 audio:
+.LP 
+	MP4FindTrackId(hFile, 0, MP4_AUDIO_TRACK_TYPE, MP4_MPEG1_AUDIO_TYPE);
+.LP 
+Caveat: The track id's do not imply anything about the ordering of the track information within the mp4 file.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4FindTrackIndex(3)
diff --git a/doc/MP4FindTrackIndex.3 b/doc/MP4FindTrackIndex.3
new file mode 100644
index 0000000..c979250
--- /dev/null
+++ b/doc/MP4FindTrackIndex.3
@@ -0,0 +1,34 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4FindTrackIndex\fR \- Find a track index
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int16_t \fBMP4FindTrackIndex\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the index is desired. 
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, the track index of the specified track. Upon an error, 0xFFFF.
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4FindTrackIndex\fR gets the index of the track with the specified track id. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4FindTrackId(3)
diff --git a/doc/MP4GetAudioProfileLevel.3 b/doc/MP4GetAudioProfileLevel.3
new file mode 100644
index 0000000..eaf5a88
--- /dev/null
+++ b/doc/MP4GetAudioProfileLevel.3
@@ -0,0 +1,27 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetAudioProfileLevel\fR \- Gets the minimum MPEG\-4 audio profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetAudioProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The current audio profile/level for the file. See MP4SetAudioProfileLevel() for known values.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetAudioProfileLevel\fR returns the minumum profile/level of MPEG\-4 audio support necessary to render the contents of the file. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetAudioProfileLevel(3)
diff --git a/doc/MP4GetDuration.3 b/doc/MP4GetDuration.3
new file mode 100644
index 0000000..b39d4c5
--- /dev/null
+++ b/doc/MP4GetDuration.3
@@ -0,0 +1,29 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetDuration\fR \- Get the duration of the movie (file)
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Duration \fBMP4GetDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The duration of the movie (file) in movie (file) time scale units. 
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetDuration\fR returns the maximum duration of all the tracks in the specified mp4 file. 
+.LP 
+Caveat: the duration is the movie (file) time scale units.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTimeScale(3) MP4ConvertFromMovieDuration(3)
diff --git a/doc/MP4GetGraphicsProfileLevel.3 b/doc/MP4GetGraphicsProfileLevel.3
new file mode 100644
index 0000000..f531c66
--- /dev/null
+++ b/doc/MP4GetGraphicsProfileLevel.3
@@ -0,0 +1,27 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetGraphicsProfileLevel\fR \- Gets the minimum MPEG\-4 graphics profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetGraphicsProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The current graphics profile/level for the file. See MP4SetGraphicsProfileLevel() for known values.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetGraphicsProfileLevel\fR returns the minumum profile/level of MPEG\-4 graphics support necessary to render the contents of the file. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetGraphicsProfileLevel(3)
diff --git a/doc/MP4GetHintTrackReferenceTrackId.3 b/doc/MP4GetHintTrackReferenceTrackId.3
new file mode 100644
index 0000000..a44ae74
--- /dev/null
+++ b/doc/MP4GetHintTrackReferenceTrackId.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetHintTrackReferenceTrackId\fR \- Get the reference track id for a hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4TrackId \fBMP4GetHintTrackReferenceTrackId\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the track id of the reference media track. Upon an error, MP4_INVALID_TRACK_ID.
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetHintTrackReferenceTrackId\fR gets the track id of the reference media track associated with the specified hint track.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddHintTrack(3)
diff --git a/doc/MP4GetHintTrackRtpPayload.3 b/doc/MP4GetHintTrackRtpPayload.3
new file mode 100644
index 0000000..03b0767
--- /dev/null
+++ b/doc/MP4GetHintTrackRtpPayload.3
@@ -0,0 +1,53 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetHintTrackRtpPayload\fR \- Get the RTP payload parameters of the hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4GetHintTrackRtpPayload\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	char** \fIppPayloadName\fP = NULL,
+.br 
+	u_int8_t* \fIpPayloadNumber\fP = NULL,
+.br 
+	u_int16_t* \fIpMaxPayloadSize\fP = NULL
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIppPayloadName\fP
+Specifies a pointer to the variable to receive the string RTP payload name.
+.TP 
+\fIpPayloadNumber\fP
+Specifies a pointer to the variable to receive the RTP payload number.
+.TP 
+\fIpMaxPayloadSize\fP
+Specifies a pointer to the variable to receive the maximum RTP payload size in bytes.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetHintTrackRtpPayload\fR gets the RTP payload parameters for the hint track. The RTP payload is the set of rules by which media samples are packed into RTP packets. This call is typically used in constructing the SDP media level description for the hint track.
+.LP 
+The payloadName identifies which RTP payload is being used for the RTP packets created from the hint track. This value is sent to the receiver in the SDP description. For example, MP3 audio sent according to the rules in IETF RFC 2250 uses the name "MPA" for the RTP payload.
+.LP 
+The payloadNumber is a shorter form of the payloadName. This value is associated with the payload name in the SDP description and then sent in every RTP packet. Payload numbers 1 thru 95 are statically assigned in IETF RFC 1890, numbers 96 thru 127 are dynamically assigned within a session.
+.LP 
+The maxPayloadSize specifies the maximum number of bytes that should be placed in the RTP payload section of the RTP packets.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetHintTrackPayload(3)
diff --git a/doc/MP4GetHintTrackSdp.3 b/doc/MP4GetHintTrackSdp.3
new file mode 100644
index 0000000..4a84f0b
--- /dev/null
+++ b/doc/MP4GetHintTrackSdp.3
@@ -0,0 +1,34 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetHintTrackSdp\fR \- Get the SDP media level description associated with a hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+const char* \fBMP4GetHintTrackSdp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The SDP media level description fragment associated with the hint track.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetHintTrackSdp\fR returns the SDP (IETF RFC 2327) media level fragment associated with the hint track. This is used by a streaming server to create a complete SDP description of the multimedia session represented by the file.
+.LP 
+The mp4broadcaster test program provided with the MP4 library gives an example of using this call to create the complete SDP description.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetHintTrackSdp(3) MP4AppendHintTrackSdp(3) MP4GetSessionSdp(3)
diff --git a/doc/MP4GetNumberOfTracks.3 b/doc/MP4GetNumberOfTracks.3
new file mode 100644
index 0000000..2686fb0
--- /dev/null
+++ b/doc/MP4GetNumberOfTracks.3
@@ -0,0 +1,39 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetNumberOfTracks\fR \- Get the number of tracks
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int32_t \fBMP4GetNumberOfTracks\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	const char* \fItype\fP = NULL,
+.br 
+	u_int8_t subType = 0
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItype\fP
+Species the type of track for which a count is desired. A NULL value implies any type of track. See MP4GetTrackType() for predefined values.
+.TP 
+\fIsubType\fP
+Specifies the subtype of the tracks to be counted. Subtypes are only defined for audio and video tracks, see MP4GetAudioTrackType() and MP4GetVideoTrackType() for predefined values. A zero value implies any subtype.
+.SH "RETURN VALUES"
+.LP 
+The number of tracks of the specified type and subType in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetNumberOfTracks\fR returns how many tracks of the specified type and subtype exist in the mp4 file. This can be used to determine if an mp4 file contains a track of a given type of media, for instance audio or video. It can also be used to determine if multiple options may be available. For instance multiple audio tracks in different languages.
+.LP 
+For audio and video tracks, a subtype can be specified to only count tracks of a particular encoding.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4GetODProfileLevel.3 b/doc/MP4GetODProfileLevel.3
new file mode 100644
index 0000000..ba13663
--- /dev/null
+++ b/doc/MP4GetODProfileLevel.3
@@ -0,0 +1,27 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetODProfileLevel\fR \- Gets the minimum MPEG\-4 object descriptor profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetODProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The current object descriptor profile/level for the file. See MP4SetODProfileLevel() for known values.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetODProfileLevel\fR returns the minumum profile/level of MPEG\-4 object descriptor support necessary to render the contents of the file. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetODProfileLevel(3)
diff --git a/doc/MP4GetRtpHintNumberOfPackets.3 b/doc/MP4GetRtpHintNumberOfPackets.3
new file mode 100644
index 0000000..5f5b1df
--- /dev/null
+++ b/doc/MP4GetRtpHintNumberOfPackets.3
@@ -0,0 +1,32 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetRtpHintNumberOfPackets\fR \- Get the number of packets in an RTP hint
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int16_t \fBMP4GetRtpHintNumberOfPackets\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of packets in the current RTP hint. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetRtpHintNumberOfPackets\fR returns the number of packets contained in the current RTP hint as established by a call to MP4ReadRtpHint().
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4ReadRtpHint(3)
diff --git a/doc/MP4GetRtpPacketBFrame.3 b/doc/MP4GetRtpPacketBFrame.3
new file mode 100644
index 0000000..6fb5ec3
--- /dev/null
+++ b/doc/MP4GetRtpPacketBFrame.3
@@ -0,0 +1,38 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetRtpPacketBFrame\fR \- Get the B frame flag of an RTP packet
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+int8_t \fBMP4GetRtpPacketBFrame\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	u_int16_t \fIpacketIndex\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIpacketIndex\fP
+Specifies the packet to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the state of the B frame flag for the specified packet. Upon an error, \-1.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetRtpPacketBFrame\fR returns the state of the B Frame flag of an RTP packet. See MP4AddRtpHint for a description of this flag. 
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpHint(3) MP4ReadRtpPacket(3)
diff --git a/doc/MP4GetRtpPacketTransmitOffset.3 b/doc/MP4GetRtpPacketTransmitOffset.3
new file mode 100644
index 0000000..f8de7a8
--- /dev/null
+++ b/doc/MP4GetRtpPacketTransmitOffset.3
@@ -0,0 +1,38 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetRtpPacketTransmitOffset\fR \- Get the transmit offset of an RTP packet
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+int32_t \fBMP4GetRtpPacketTransmitOffset\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	u_int16_t \fIpacketIndex\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIpacketIndex\fP
+Specifies the packet to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The transmit offset for the specified packet in the hint track timescale.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetRtpPacketTransmitOffset\fR returns the transmit offset of an RTP packet. This offset may be set by some hinters to smooth out the packet transmission times and reduce network burstiness. A transmitter would need to apply this offset to the calculated transmission time based on the hint start time.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpPacket(3) MP4ReadRtpPacket(3)
diff --git a/doc/MP4GetRtpTimestampStart.3 b/doc/MP4GetRtpTimestampStart.3
new file mode 100644
index 0000000..84d1e23
--- /dev/null
+++ b/doc/MP4GetRtpTimestampStart.3
@@ -0,0 +1,38 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetRtpTimestampStart\fR \- Get the RTP start time of a hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Timestamp \fBMP4GetRtpTimestampStart\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, the RTP start time in the RTP time scale which is identical to the hint track time scale. Upon an error, MP4_INVALID_TIMESTAMP.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetRtpTimestampStart\fR returns the RTP timestamp start of the specified hint track. Typically this is a random value that is chosen when the first RTP packet is constructed by the MP4 library. However the  value can be set explicitly for the hint track and stored. Typically this is used if it is desired that timestamps start at zero.
+.LP 
+An application will need this value in order to construct RTCP Sender Reports that relate the hint track time to an real time clock. The mp4broadcaster test program provided with the MP4 library gives an example of this.
+.LP 
+See IETF RFC 1889 for details regarding RTP timestamps and RTCP.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetRtpTimestampStart(3)
diff --git a/doc/MP4GetSampleIdFromEditTime.3 b/doc/MP4GetSampleIdFromEditTime.3
new file mode 100644
index 0000000..4a71a3d
--- /dev/null
+++ b/doc/MP4GetSampleIdFromEditTime.3
@@ -0,0 +1,52 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetSampleIdFromEditTime\fR \- Get the sample id of a specified time in the edit list timeline
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4SampleId \fBMP4GetSampleIdFromEditTime\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4Timestamp \fIwhen\fP,
+.br 
+	MP4Timestamp* \fIpStartTime\fP = NULL,
+.br 
+	MP4Duration* \fIpDuration\fP = NULL
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIwhen\fP
+Specifies the time in the track time scale that is desired.
+.TP 
+\fIpStartTime\fP
+If non\-NULL, pointer to variable that will receive the starting timestamp for this sample. Caveat: The timestamp is in the track edit list timescale.
+.TP 
+\fIpDuration\fP
+If non\-NULL, pointer to variable that will receive the duration for this sample in the edit list timeline. Caveat: The duration is in the track timescale units.
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, the sample id that occurs at the specified time. Upon an error, MP4_INVALID_SAMPLE_ID.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetSampleIdFromEditTime\fR returns the sample id of the track sample in which the specified time occurs in the edit list timeline. 
+.LP 
+The specified time should be in the track time scale. See MP4ConvertToTrackTimestamp() for how to map a time value to this time scale.
+.LP 
+Since the edit list can cause the sample start time and duration to be different that it in the standard track timeline, it is strongly advised that the caller retrieve the new sample start time and duration via this function.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetSampleIdFromTime(3)
diff --git a/doc/MP4GetSceneProfileLevel.3 b/doc/MP4GetSceneProfileLevel.3
new file mode 100644
index 0000000..92d8cbc
--- /dev/null
+++ b/doc/MP4GetSceneProfileLevel.3
@@ -0,0 +1,27 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetSceneProfileLevel\fR \- Gets the minimum MPEG\-4 scene graph profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetSceneProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The current scene graph profile/level for the file. See MP4SetSceneProfileLevel() for known values.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetSceneProfileLevel\fR returns the minumum profile/level of MPEG\-4 scene graph support necessary to render the contents of the file. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetSceneProfileLevel(3)
diff --git a/doc/MP4GetSessionSdp.3 b/doc/MP4GetSessionSdp.3
new file mode 100644
index 0000000..e92c9f5
--- /dev/null
+++ b/doc/MP4GetSessionSdp.3
@@ -0,0 +1,29 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetSessionSdp\fR \- Get the SDP session level description of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+const char* \fBMP4GetSessionSdp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The SDP session level description fragment of the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetSessionSdp\fR returns the SDP (IETF RFC 2327) session level fragment for the file. This is used by a streaming server to create a complete SDP description of the multimedia session represented by the file.
+.LP 
+The mp4broadcaster test program provided with the MP4 library gives an example of using this call to create the complete SDP description.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetSessionSdp(3) MP4AppendSessionSdp(3) MP4GetHintTrackSdp(3)
diff --git a/doc/MP4GetTrackAudioMpeg4Type.3 b/doc/MP4GetTrackAudioMpeg4Type.3
new file mode 100644
index 0000000..77c2911
--- /dev/null
+++ b/doc/MP4GetTrackAudioMpeg4Type.3
@@ -0,0 +1,83 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackAudioMpeg4Type\fR \- Get the encoding type of an MPEG\-4 audio track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetTrackAudioMpeg4Type\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the MPEG\-4 audio type is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the MPEG\-4 audio type of the track. Upon error, MP4_MPEG4_INVALID_AUDIO_TYPE is returned.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackAudioMpeg4Type\fR returns the MPEG\-4 encoding type of the specified MPEG\-4 audio track in the mp4 file. If an mp4 audio track has type MP4_MPEG4_AUDIO_TYPE, this call can be used to determine which specific MPEG\-4 audio encoding is contained in the track. (Note: This information is retrieved from the audio track ES configuration.)
+.LP 
+Known audio encoding types are:
+.LP 
+MP4_MPEG4_AAC_MAIN_AUDIO_TYPE
+.br 
+	MPEG\-4 AAC Main profile
+.LP 
+MP4_MPEG4_AAC_LC_AUDIO_TYPE	
+.br 
+	MPEG\-4 AAC Low Complexity profile
+.LP 
+MP4_MPEG4_AAC_SSR_AUDIO_TYPE 
+.br 
+	MPEG\-4 AAC SSR profile
+.LP 
+MP4_MPEG4_AAC_LTP_AUDIO_TYPE 
+.br 
+	MPEG\-4 AAC Long Term Prediction profile
+.LP 
+MP4_MPEG4_AAC_SCALABLE_AUDIO_TYPE 
+.br 
+	MPEG\-4 AAC Scalable
+.LP 
+MP4_MPEG4_CELP_AUDIO_TYPE 
+.br 
+	MPEG\-4 CELP
+.LP 
+MP4_MPEG4_HVXC_AUDIO_TYPE 
+.br 
+	MPEG\-4 HVXC
+.LP 
+MP4_MPEG4_TTSI_AUDIO_TYPE 
+.br 
+	MPEG\-4 Text To Speech
+.LP 
+MP4_MPEG4_MAIN_SYNTHETIC_AUDIO_TYPE 
+.br 
+	MPEG\-4 Main Synthetic profile
+.LP 
+MP4_MPEG4_WAVETABLE_AUDIO_TYPE 
+.br 
+	MPEG\-4 Wavetable Synthesis profile
+.LP 
+MP4_MPEG4_MIDI_AUDIO_TYPE 
+.br 
+	MPEG\-4 MIDI profile
+.LP 
+MP4_MPEG4_ALGORITHMIC_FX_AUDIO_TYPE 
+.br 
+	MPEG\-4 Algorithmic Synthesis and Audio FX profile
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackAudioType(3)
diff --git a/doc/MP4GetTrackAudioType.3 b/doc/MP4GetTrackAudioType.3
new file mode 100644
index 0000000..d545ebb
--- /dev/null
+++ b/doc/MP4GetTrackAudioType.3
@@ -0,0 +1,64 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackAudioType\fR \- Get the encoding type of an audio track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetTrackAudioType\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the audio type is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the audio type of the track. Upon error, MP4_INVALID_AUDIO_TYPE is returned.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackAudioType\fR returns the encoding type of the specified audio track in the mp4 file.
+.LP 
+Known audio encoding types are:
+.LP 
+MP4_MPEG1_AUDIO_TYPE	
+.br 
+	MPEG\-1 Audio Layers I, II, & III
+.LP 
+MP4_MPEG2_AUDIO_TYPE	
+.br 
+	MPEG\-2 low bitrate extensions to MPEG\-1 Audio
+.br 
+	MP4_MP3_AUDIO_TYPE is an alias for this value
+.LP 
+MP4_MPEG2_AAC_MAIN_AUDIO_TYPE
+.br 
+	MPEG\-2 AAC Main profile
+.LP 
+MP4_MPEG2_AAC_LC_AUDIO_TYPE	
+.br 
+	MPEG\-2 AAC Low Complexity profile
+.LP 
+MP4_MPEG2_AAC_SSR_AUDIO_TYPE 
+.br 
+	MPEG\-2 AAC SSR profile
+.LP 
+MP4_MPEG4_AUDIO_TYPE	
+.br 
+	MPEG\-4 Audio, includes MPEG\-4 extensions to AAC
+.LP 
+MP4_PRIVATE_AUDIO_TYPE
+.br 
+	User private type
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddAudioTrack(3)
diff --git a/doc/MP4GetTrackBitRate.3 b/doc/MP4GetTrackBitRate.3
new file mode 100644
index 0000000..1ca6a3b
--- /dev/null
+++ b/doc/MP4GetTrackBitRate.3
@@ -0,0 +1,34 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackBitRate\fR \- Get the average bit rate in bits per second of the specified track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int32_t \fBMP4GetTrackBitRate\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the bit rate is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the average bit rate in bits per second of the track. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackBitRate\fR returns the average bit rate in bits per second in the specified track in the mp4 file.
+.LP 
+Note: hint tracks will not return their bit rate via this mechanism.
+.SH "SEE ALSO"
+.LP 
+MP4(3) 
diff --git a/doc/MP4GetTrackDuration.3 b/doc/MP4GetTrackDuration.3
new file mode 100644
index 0000000..4992968
--- /dev/null
+++ b/doc/MP4GetTrackDuration.3
@@ -0,0 +1,34 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackDuration\fR \- Get the duration of a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Duration \fBMP4GetTrackDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the duration is desired.
+.SH "RETURN VALUES"
+.LP 
+The duration in track time scale units of the track in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackDuration\fR returns the total duration of all the samples in the specified track in the mp4 file. 
+.LP 
+Caveat: The value is in units of the track time scale.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4GetTrackESConfiguration.3 b/doc/MP4GetTrackESConfiguration.3
new file mode 100644
index 0000000..a6746cb
--- /dev/null
+++ b/doc/MP4GetTrackESConfiguration.3
@@ -0,0 +1,44 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackESConfiguration\fR \- Get the elementary stream (ES) configuration of a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+void \fBMP4GetTrackESConfiguration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+	u_int8_t** \fIppConfig\fP,
+.br 
+	u_int32_t* \fIpConfigSize\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the ES configuration is desired.
+.TP 
+\fIppConfig\fP
+Specifies a pointer to a pointer variable that will be given the address of the configuration information.
+.TP 
+\fIpConfigSize\fP
+Specifies a pointer to a variable to hold the size of the ES configuration information.
+.SH "RETURN VALUES"
+.LP 
+Upon success, *ppConfig will point to a malloc'd block of memory with the ES configuration, and *pConfigSize will indicated the number of bytes of the ES configuration. Upon error, *ppConfig will be NULL, and *pConfigSize will be 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackESConfiguration\fR returns the elementary stream (ES) configuration of the specified track in the mp4 file. This information is codec specific and contains the configuration necessary for the given codec to decode the samples in the track. 
+.LP 
+Caveat: the returned block of memory has been malloc'd. The caller may safely modify the value without effecting the library, but the caller takes responsiblity for free'ing the memory.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4GetTrackEditDuration.3 b/doc/MP4GetTrackEditDuration.3
new file mode 100644
index 0000000..ab86c55
--- /dev/null
+++ b/doc/MP4GetTrackEditDuration.3
@@ -0,0 +1,39 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackEditDuration\fR \- Get the duration of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Duration \fBMP4GetTrackEditDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment for which the duration is desired.
+.SH "RETURN VALUES"
+.LP 
+The duration of the edit segment in track time scale units of the track in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackEditDuration\fR returns the duration of the specified track edit segment.
+.LP 
+Caveat: The value is in units of the track time scale.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetTrackEditDuration(3)
diff --git a/doc/MP4GetTrackEditDwell.3 b/doc/MP4GetTrackEditDwell.3
new file mode 100644
index 0000000..b1505e1
--- /dev/null
+++ b/doc/MP4GetTrackEditDwell.3
@@ -0,0 +1,37 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackEditDwell\fR \- Get the dwell value of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Duration \fBMP4GetTrackEditDwell\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment for which the dwell value is desired.
+.SH "RETURN VALUES"
+.LP 
+The dwell value of the edit segment of the track in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackEditDwell\fR returns the dwell value of the specified track edit segment. A value of true (1) indicates that during this edit segment the media will be paused; a value of false (0) indicates that during this edit segment the media will be played at its normal rate.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetTrackEditDwell(3)
diff --git a/doc/MP4GetTrackEditMediaStart.3 b/doc/MP4GetTrackEditMediaStart.3
new file mode 100644
index 0000000..7cb868f
--- /dev/null
+++ b/doc/MP4GetTrackEditMediaStart.3
@@ -0,0 +1,48 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackEditMediaStart\fR \- Get the media start time of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Timestamp \fBMP4GetTrackEditMediaStart\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment for which the media start time is desired.
+.SH "RETURN VALUES"
+.LP 
+The media start time of the edit segment in track time scale units of the track in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackEditMediaStart\fR returns the media start time of the  of the specified track edit segment. 
+.LP 
+Caveat: The value is in units of the track time scale.
+.LP 
+Note that this differs from the edit segment start time, MP4GetTrackEditStart(). For example:
+.LP 
+EditId	Start	MediaStart	Duration
+1		0		15			30
+.br 
+2		30		120			20
+.br 	
+3		50		3000			10
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetTrackEditMediaStart(3)
diff --git a/doc/MP4GetTrackEditStart.3 b/doc/MP4GetTrackEditStart.3
new file mode 100644
index 0000000..1e5a789
--- /dev/null
+++ b/doc/MP4GetTrackEditStart.3
@@ -0,0 +1,48 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackEditStart\fR \- Get the start time of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Timestamp \fBMP4GetTrackEditStart\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment for which the start time is desired.
+.SH "RETURN VALUES"
+.LP 
+The start time of the edit segment in track time scale units of the track in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackEditStart\fR returns the start time of the  of the specified track edit segment in the timeline of the track edit list.
+.LP 
+Caveat: The value is in units of the track time scale.
+.LP 
+Note that this differs from the edit segment media start time, MP4GetTrackEditMediaStart(). For example:
+.LP 
+EditId	Start	MediaStart	Duration
+1		0		15			30
+.br 
+2		30		120			20
+.br 	
+3		50		3000			10
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetTrackEditStart(3)
diff --git a/doc/MP4GetTrackEditTotalDuration.3 b/doc/MP4GetTrackEditTotalDuration.3
new file mode 100644
index 0000000..701c0d5
--- /dev/null
+++ b/doc/MP4GetTrackEditTotalDuration.3
@@ -0,0 +1,39 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackEditTotalDuration\fR \- Get the total duration of a sequence of track edit segments
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Duration \fBMP4GetTrackEditTotalDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP = MP4_INVALID_EDIT_ID
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment for which the total duration is desired. A value of MP4_INVALID_EDIT_ID specifies that all edit segments should be included.
+.SH "RETURN VALUES"
+.LP 
+The total duration of the edit segment sequence in track time scale units of the track in the mp4 file.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackEditTotalDuration\fR returns the total duration of the specified sequence of track edit segments from the first edit segment up to and including the specified edit segment. If the edit id value is MP4_INVALID_EDIT_ID, then the total duration of all of the edit segments is returned.
+.LP 
+Caveat: The value is in units of the track time scale.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackEditDuration(3)
diff --git a/doc/MP4GetTrackFixedSampleDuration.3 b/doc/MP4GetTrackFixedSampleDuration.3
new file mode 100644
index 0000000..f5e42c2
--- /dev/null
+++ b/doc/MP4GetTrackFixedSampleDuration.3
@@ -0,0 +1,32 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackFixedSampleDuration\fR \- Get the fixed duration of samples in a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+MP4Duration \fBMP4GetTrackFixedSampleDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of fixed duration of the samples in the track in track time scale units. Upon an error, MP4_INVALID_DURATION.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackFixedSampleDuration\fR returns the duration of samples in the specified track in the mp4 file, if this value is fixed for all samples. This is typically the case for audio tracks and video tracks. If the track samples have variable duration, then MP4_INVALID_DURATION is returned.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4GetTrackNumberOfEdits.3 b/doc/MP4GetTrackNumberOfEdits.3
new file mode 100644
index 0000000..1782b9f
--- /dev/null
+++ b/doc/MP4GetTrackNumberOfEdits.3
@@ -0,0 +1,32 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackNumberOfEdits\fR \- Get the number of edit segments for a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int32_t \fBMP4GetTrackNumberOfEdits\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the number of edit segments is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of edit segments for the track. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackNumberOfEdits\fR returns the number of edit segments in the specified track in the mp4 file. Edit id's are the consecutive sequence of numbers from 1 to the total number of edit segments, i.e. 1\-based indexing, not 0\-based indexing.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddTrackEdit(3) MP4DeleteTrackEdit(3)
diff --git a/doc/MP4GetTrackNumberOfSamples.3 b/doc/MP4GetTrackNumberOfSamples.3
new file mode 100644
index 0000000..093d5b2
--- /dev/null
+++ b/doc/MP4GetTrackNumberOfSamples.3
@@ -0,0 +1,32 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackNumberOfSamples\fR \- Get the number of samples in a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int32_t \fBMP4GetTrackNumberOfSamples\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the number of samples is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of samples in the track. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackNumberOfSamples\fR returns the number of samples in the specified track in the mp4 file. Sample id's are the consecutive sequence of numbers from 1 to the total number of samples, i.e. 1\-based indexing, not 0\-based indexing.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4GetTrackVideoFrameRate.3 b/doc/MP4GetTrackVideoFrameRate.3
new file mode 100644
index 0000000..0893fdb
--- /dev/null
+++ b/doc/MP4GetTrackVideoFrameRate.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackVideoFrameRate\fR \- Get the video frame rate of the specified video track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+float \fBMP4GetTrackVideoFrameRate\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the video frame rate is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of video frames per second of the track. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackVideoFrameRate\fR returns the frame rate of the video in the specified track in the mp4 file. If the video is variable rate, the average frame rate is returned. 
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) 
diff --git a/doc/MP4GetTrackVideoHeight.3 b/doc/MP4GetTrackVideoHeight.3
new file mode 100644
index 0000000..448cfce
--- /dev/null
+++ b/doc/MP4GetTrackVideoHeight.3
@@ -0,0 +1,35 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackVideoHeight\fR \- Get the video height in pixels of the specified video track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int16_t \fBMP4GetTrackVideoHeight\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the video height is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of pixels of the video height in the track. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackVideoWidth\fR returns the height of the video in pixels in the specified track in the mp4 file. 
+.LP 
+Caveat: Not all mp4 implementations set this value accurately. The mp4 specification states that the authoritative values are contained in the track ES configuration which is video encoding specific, and hence not interpretable by the mp4 library.
+If the value of 240 is returned, care should be taken to verify the accuracy of this value since this is the default value in the mp4 specification.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackVideoWidth(3) MP4GetTrackESConfiguration(3)
diff --git a/doc/MP4GetTrackVideoType.3 b/doc/MP4GetTrackVideoType.3
new file mode 100644
index 0000000..cf01e4b
--- /dev/null
+++ b/doc/MP4GetTrackVideoType.3
@@ -0,0 +1,74 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackVideoType\fR \- Get the encoding type of a video track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetTrackVideoType\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the video type is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the video type of the track. Upon error, MP4_INVALID_VIDEO_TYPE is returned.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackVideoType\fR returns the encoding type of the specified video track in the mp4 file.
+.LP 
+Known video encoding types are:
+.LP 
+MP4_MPEG1_VIDEO_TYPE	
+.br 
+	MPEG\-1 Video
+.LP 
+MP4_MPEG2_SIMPLE_VIDEO_TYPE	
+.br 
+	MPEG\-2 Simple Profile Video
+.LP 
+MP4_MPEG2_MAIN_VIDEO_TYPE
+.br 
+	MPEG\-2 Main Profile Video (Broadcast/DVD)
+.LP 
+MP4_MPEG2_SNR_VIDEO_TYPE	
+.br 
+	MPEG\-2 SNR Profile Video
+.LP 
+MP4_MPEG2_SPATIAL_VIDEO_TYPE 
+.br 
+	MPEG\-2 Spatial Scalability Profile Video
+.LP 
+MP4_MPEG2_HIGH_VIDEO_TYPE 
+.br 
+	MPEG\-2 High Profile Video (HDTV)
+.LP 
+MP4_MPEG2_442_VIDEO_TYPE 
+.br 
+	MPEG\-2 442 Profile Video (Studio)
+.LP 
+MP4_MPEG4_VIDEO_TYPE	
+.br 
+	MPEG\-4 Video
+.LP 
+MP4_JPEG_VIDEO_TYPE
+.br 
+	JPEG stills or motion JPEG
+.LP 
+MP4_PRIVATE_VIDEO_TYPE
+.br 
+	User private type
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddVideoTrack(3)
diff --git a/doc/MP4GetTrackVideoWidth.3 b/doc/MP4GetTrackVideoWidth.3
new file mode 100644
index 0000000..df095fc
--- /dev/null
+++ b/doc/MP4GetTrackVideoWidth.3
@@ -0,0 +1,35 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetTrackVideoWidth\fR \- Get the video width in pixels of the specified video track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int16_t \fBMP4GetTrackVideoWidth\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track for which the video width is desired.
+.SH "RETURN VALUES"
+.LP 
+Upon success, the number of pixels of the video width in the track. Upon an error, 0.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetTrackVideoWidth\fR returns the width of the video in pixels in the specified track in the mp4 file. 
+.LP 
+Caveat: Not all mp4 implementations set this value accurately. The mp4 specification states that the authoritative values are contained in the track ES configuration which is video encoding specific, and hence not interpretable by the mp4 library.
+If the value of 320 is returned, care should be taken to verify the accuracy of this value since this is the default value in the mp4 specification.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackVideoHeight(3) MP4GetTrackESConfiguration(3)
diff --git a/doc/MP4GetVideoProfileLevel.3 b/doc/MP4GetVideoProfileLevel.3
new file mode 100644
index 0000000..427ce9c
--- /dev/null
+++ b/doc/MP4GetVideoProfileLevel.3
@@ -0,0 +1,27 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4GetVideoProfileLevel\fR \- Gets the minimum MPEG\-4 video profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+u_int8_t \fBMP4GetVideoProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+The current video profile/level for the file. See MP4SetVideoProfileLevel() for known values.
+.SH "DESCRIPTION"
+.LP 
+\fBMP4GetVideoProfileLevel\fR returns the minumum profile/level of MPEG\-4 video support necessary to render the contents of the file. 
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4SetVideoProfileLevel(3)
diff --git a/doc/MP4MakeIsmaCompliant.3 b/doc/MP4MakeIsmaCompliant.3
new file mode 100644
index 0000000..c2117cf
--- /dev/null
+++ b/doc/MP4MakeIsmaCompliant.3
@@ -0,0 +1,36 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4MakeIsmaCompliant\fR \- Add ISMA compliant OD and Scene tracks
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4MakeIsmaCompliant\fR(
+.br 
+	const char* \fIfileName\fP, 
+.br 
+	u_int32_t \fIverbosity\fP = 0
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIfileName\fP
+Specifies the path name of the file to be modified.
+.TP 
+\fIverbosity\fP
+Specifies a bitmask of diagnostic details the library should print to stdout during its functioning. See MP4SetVerbosity() for values.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4MakeIsmaCompliant\fR modifies an mp4 file so that it complies with the minimal MPEG\-4 Systems requirements defined by the Internet Streaming Media Alliance (ISMA, http://www.isma.tv)
+.LP 
+This involves creating an OD and Scene track, and using them to describe a simple scene of one audio, or one video, or one of each. An SDP version of this information is also added to the mp4 file.
+.LP 
+Caveat: whether the file is truly ISMA compliant also depends on the contents of the media and hint tracks. This function does not guarantee that these tracks are compliant.
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4ReadRtpHint.3 b/doc/MP4ReadRtpHint.3
new file mode 100644
index 0000000..2a23923
--- /dev/null
+++ b/doc/MP4ReadRtpHint.3
@@ -0,0 +1,44 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4ReadRtpHint\fR \- Read an RTP hint
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4ReadRtpHint\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4SampleId \fIsampleId\fP,
+.br 
+	u_int16_t* \fIpNumPackets\fP = NULL
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIsampleId\fP
+Specifies which hint sample is to be read. Caveat: the first sample has id 1 not 0.
+.TP 
+\fIpNumPackets\fP
+Pointer to variable that will be hold the number of packets in the hint.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4ReadRtpHint\fR reads the specified hint sample from the specified hint track and enables subsequent calls to MP4ReadRtpPacket() to read the individual RTP packets associated with this hint. If desired, the number of RTP packets associated with this hint is returned.
+.LP 
+Note that a hint track sample is just like any other track sample. I.e MP4ReadSample(), MP4GetSampleSize(), MP4GetSampleTime(), etc. are all valid. The RTP specific functions are provided to interpret the information contain in the hint track samples that give instructions on how to form the actual RTP packets.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4ReadRtpPacket(3)
diff --git a/doc/MP4ReadRtpPacket.3 b/doc/MP4ReadRtpPacket.3
new file mode 100644
index 0000000..80b42ff
--- /dev/null
+++ b/doc/MP4ReadRtpPacket.3
@@ -0,0 +1,75 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4ReadRtpPacket\fR \- Read an RTP packet
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4ReadRtpPacket\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	u_int16_t \fIpacketIndex\fP,
+.br 
+	u_int8_t** \fIppBytes\fP,
+.br 
+	u_int32_t* \fIpNumBytes\fP,
+.br 	
+	u_int32_t \fIssrc\fP = 0,
+.br 
+	bool includeHeader = true,
+.br 
+	bool includePayload = true
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIpacketIndex\fP
+Specifies which packet is to be read. Valid values range from zero to the number of packets in this hint minus one.
+.TP 
+\fIpNumPackets\fP
+Pointer to variable that will be hold the number of packets in the hint.
+.TP 
+\fIppBytes\fP
+Pointer to the pointer to the packet data. See DESCRIPTION for details on this argument. 
+.TP 
+\fIpNumBytes\fP
+Pointer to variable that will be hold the size in bytes of the packet.
+.TP 
+\fIssrc\fP
+Specifies the RTP SSRC to be used when constructing the RTP packet header.
+.TP 
+\fIincludeHeader\fP
+Specifies whether the library should include the standard 12 byte RTP header to the returned packet. The header is constructed from the information in the hint sample and the specified ssrc.
+.TP 
+\fIincludePayload\fP
+Specifies whether the library should include the packet payload (RTP payload header and media data) in the returned packet.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4ReadRtpPacket\fR reads the specified packet from the current hint sample, as previously read by MP4ReadRtpHint().
+.LP 
+The argument, ppBytes, allows for two possible approaches for buffering:
+.LP 
+If the calling application wishes to handle its own buffering it can set *ppBytes to the buffer it wishes to use. The calling application is responsible for ensuring that the buffer is large enough to hold the packet. This can be done by using MP4GetRtpPayload() to retrieve the maximum packet payload size and hence how large the receiving buffer must be. Caveat: the value returned by MP4GetRtpPayload is the maxiumum payload size, if the RTP packet header is going to be included by the library this value should be incremented by 12.
+.LP 
+If the value of *ppBytes is NULL, then an appropriately sized buffer is automatically malloc'ed for the sample data and *ppBytes set to this pointer. The calling application is responsible for free'ing this memory. 
+.LP 
+The application is expected to provide the value of the RTP SSRC identifier which uniquely identifies the originator of the media stream. For most applications, a single random value can be provided. The value should be the same for all packets for the duration of the RTP transmission. If the parameter "includeHeader" is false, then this value has no effect.
+.LP 
+By default the library constructs the standard 12 byte RTP header from the information in the hint sample, and the specified SSRC. It then concatenates the RTP header with the packet payload, that is the RTP payload specific header and the media data for the packet. The "includeHeader" and "includePayload" parameters allow control over these steps, so that either just the packet payloads or just the RTP headers can be returned. A potential use of this feature is if the calling application wishes to construct an extended RTP header with non\-standard options.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4ReadRtpHint(3)
diff --git a/doc/MP4ReadSampleFromEditTime.3 b/doc/MP4ReadSampleFromEditTime.3
new file mode 100644
index 0000000..e930b91
--- /dev/null
+++ b/doc/MP4ReadSampleFromEditTime.3
@@ -0,0 +1,81 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4ReadSampleFromEditTime\fR \- Read a track sample based on a specified edit list time
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4ReadSampleFromEditTime\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4Timestamp \fIeditWhen\fP,
+.br 
+	u_int8_t** \fIppBytes\fP,
+.br 
+	u_int32_t* \fIpNumBytes\fP,
+.br 
+	MP4Timestamp* \fIpStartTime\fP = NULL,
+.br 
+	MP4Duration* \fIpDuration\fP = NULL,
+.br 
+	MP4Duration* \fIpRenderingOffset\fP = NULL,
+.br 
+	bool \fIpIsSyncSample\fP = NULL
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditWhen\fP
+Specifies which sample is to be read based on a time in the edit list timeline. See MP4GetSampleIdFromEditTime() for details.
+.TP 
+\fIppBytes\fP
+Pointer to the pointer to the sample data. See DESCRIPTION for details on this argument. 
+.TP 
+\fIpNumBytes\fP
+Pointer to variable that will be hold the size in bytes of the sample.
+.TP 
+\fIpStartTime\fP
+If non\-NULL, pointer to variable that will receive the starting timestamp for this sample. Caveat: The timestamp is in the track timescale.
+.TP 
+\fIpDuration\fP
+If non\-NULL, pointer to variable that will receive the duration for this sample. Caveat: The duration is in the track timescale units.
+.TP 
+\fIpRenderingOffset\fP
+If non\-NULL, pointer to variable that will receive the rendering offset for this sample. Currently the only media type that needs this feature is MPEG video. Caveat: The offset is in the track timescale units.
+.TP 
+\fIpIsSyncSample\fP
+If non\-NULL, pointer to variable that will receive the state of the sync/random access flag for this sample.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4ReadSampleFromEditTime\fR reads the sample corresponding to the time on the track edit list timeline from the specified track. Typically this sample is then decoded in a codec dependent fashion and rendered in an appropriate fashion.
+.LP 
+The argument, ppBytes, allows for two possible approaches for buffering:
+.LP 
+If the calling application wishes to handle its own buffering it can set *ppBytes to the buffer it wishes to use. The calling application is responsible for ensuring that the buffer is large enough to hold the sample. This can be done by using either MP4GetSampleSize() or MP4GetTrackMaxSampleSize() to determine before\-hand how large the receiving buffer must be.
+.LP 
+If the value of *ppBytes is NULL, then an appropriately sized buffer is automatically malloc'ed for the sample data and *ppBytes set to this pointer. The calling application is responsible for free'ing this memory. 
+
+The last four arguments are pointers to variables that can receive optional sample information. 
+.LP 
+Typically for audio none of these are needed. MPEG audio such as MP3 or AAC has a fixed sample duration and every sample can be accessed at random. 
+.LP 
+For video, all of these optional values could be needed. MPEG video can be encoded at a variable frame rate, with only occasional random access points, and with "B frames" which cause the rendering (display) order of the video frames to differ from the storage/decoding order.
+.LP 
+Other media types fall between these two extremes.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4ReadSample(3) MP4GetSampleIdFromEditTime(3) MP4ReadSampleFromTime(3)
diff --git a/doc/MP4SetAudioProfileLevel.3 b/doc/MP4SetAudioProfileLevel.3
new file mode 100644
index 0000000..9b03409
--- /dev/null
+++ b/doc/MP4SetAudioProfileLevel.3
@@ -0,0 +1,67 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetAudioProfileLevel\fR \- Sets the minimum MPEG\-4 audio profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetAudioProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP,
+.br 
+        u_int8_t \fIprofileLevel\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetAudioProfileLevel\fR sets the minumum profile/level of MPEG\-4 audio support necessary to render the contents of the file.
+.LP 
+ISO/IEC 14496\-1:2001 MPEG\-4 Systems defines the following values:
+.br 
+	0x00		Reserved
+.br 
+	0x01		Main Profile @ Level 1
+.br 
+	0x02		Main Profile @ Level 2
+.br 
+	0x03		Main Profile @ Level 3
+.br 
+	0x04		Main Profile @ Level 4
+.br 
+	0x05		Scalable Profile @ Level 1
+.br 
+	0x06		Scalable Profile @ Level 2
+.br 
+	0x07		Scalable Profile @ Level 3
+.br 
+	0x08		Scalable Profile @ Level 4
+.br 
+	0x09		Speech Profile @ Level 1
+.br 
+	0x0A		Speech Profile @ Level 2
+.br 
+	0x0B		Synthesis Profile @ Level 1
+.br 
+	0x0C		Synthesis Profile @ Level 2
+.br 
+	0x0D		Synthesis Profile @ Level 3
+.br 
+	0x0E\-0x7F	Reserved
+.br 
+	0x80\-0xFD	User private
+.br 
+	0xFE		No audio profile specified
+.br 
+	0xFF		No audio required
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetAudioProfileLevel(3)
diff --git a/doc/MP4SetGraphicsProfileLevel.3 b/doc/MP4SetGraphicsProfileLevel.3
new file mode 100644
index 0000000..984aaa6
--- /dev/null
+++ b/doc/MP4SetGraphicsProfileLevel.3
@@ -0,0 +1,43 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetGraphicsProfileLevel\fR \- Sets the minimum MPEG\-4 graphics profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetGraphicsProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP,
+.br 
+        u_int8_t \fIprofileLevel\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetGraphicsProfileLevel\fR sets the minumum profile/level of MPEG\-4 graphics support necessary to render the contents of the file.
+.LP 
+ISO/IEC 14496\-1:2001 MPEG\-4 Systems defines the following values:
+.br 
+	0x00		Reserved
+.br 
+	0x01		Simple 2D Profile @ Level 1
+.br 
+	0x02\-0x7F	Reserved
+.br 
+	0x80\-0xFD	User private
+.br 
+	0xFE		No audio profile specified
+.br 
+	0xFF		No audio required
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetGraphicsProfileLevel(3)
diff --git a/doc/MP4SetHintTrackRtpPayload.3 b/doc/MP4SetHintTrackRtpPayload.3
new file mode 100644
index 0000000..a7764ee
--- /dev/null
+++ b/doc/MP4SetHintTrackRtpPayload.3
@@ -0,0 +1,67 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetHintTrackRtpPayload\fR \- Set the RTP payload parameters of the hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetHintTrackRtpPayload\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	const char* \fIpPayloadName\fP,
+.br 
+	u_int8_t* \fIpPayloadNumber\fP,
+.br 
+	u_int16_t \fImaxPayloadSize\fP = 0
+.br 
+	bool \fIinclude_rtp_map\fP = true
+.br 
+	bool \fIinclude_mpeg4_esid\fP = true
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIhintTrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIpPayloadName\fP
+Specifies the RTP payload name.
+.TP 
+\fIpPayloadNumber\fP
+Specifies a pointer to the RTP payload number.
+.TP 
+\fImaxPayloadSize\fP
+Specifies the maximum RTP payload size in bytes.
+.TP 
+\fIinclude_rtp_map\fP
+Specifies if the a=rtpmap statement is included.
+.TP 
+\fIinclude_mpeg4_esid\fP
+Specifies if the a=mpeg4-esid statement is included.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetHintTrackRtpPayload\fR sets the RTP payload parameters for the hint track. The RTP payload is the set of rules by which media samples are packed into RTP packets.
+.LP 
+The payload name identifies which RTP payload is being used for the RTP packets created from the hint track. This value is sent to the receiver in the SDP description. For example, MP3 audio sent according to the rules in IETF RFC 2250 uses the name "MPA" for the RTP payload.
+.LP 
+The payload number is a shorter form of the payload name. This value is associated with the payload name in the SDP description and then sent in every RTP packet. Payload numbers 1 thru 95 are statically assigned in IETF RFC 1890, numbers 96 thru 127 are dynamically assigned within a session. If the RTP payload in use is one of the statically assigned ones, you should pass this value to the library. If you need a dynamic payload number assigned, pass the define value MP4_SET_DYNAMIC_PAYLOAD for this parameter and the library will choose an valid available number and return this value.
+.LP 
+The maxPayloadSize specifies the maximum number of bytes that should be placed in the RTP payload section of the RTP packets. It is desirable that RTP packets not exceed the maximum transmission unit (MTU) of the IP network they travel over since otherwise the packets must be fragmented at the IP level which consumes router resources and can lead to less robust behavior in the face of packet loss.
+.LP 
+The default value for maxPayloadSize is 1460, which is the MTU for an Ethernet or similar network minus the standard sizes of the IP, UDP, and RTP headers (1500 \- 20 \- 8 \- 12 = 1460).
+.LP 
+If you anticipate streaming over IP networks with smaller MTU sizes, or that extensions to the network headers might be used, a more conservative value should be chosen. The minimum MTU for an IP network is 576 bytes.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetHintTrackPayload(3)
diff --git a/doc/MP4SetHintTrackSdp.3 b/doc/MP4SetHintTrackSdp.3
new file mode 100644
index 0000000..ff3dd1b
--- /dev/null
+++ b/doc/MP4SetHintTrackSdp.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetHintTrackSdp\fR \- Set the SDP media level description of the hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetHintTrackSdp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	const char* sdpString
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIsdpString\fP
+Specifies the new value of the hint track sdp string.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetHintTrackSdp\fR sets the SDP (IETF RFC 2327) media level fragment for the hint track. This is used by a streaming server to create a complete SDP description of the multimedia session represented by the file.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetHintTrackSdp(3) MP4AppendHintTrackSdp(3)
diff --git a/doc/MP4SetODProfileLevel.3 b/doc/MP4SetODProfileLevel.3
new file mode 100644
index 0000000..07d71c7
--- /dev/null
+++ b/doc/MP4SetODProfileLevel.3
@@ -0,0 +1,41 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetODProfileLevel\fR \- Sets the minimum MPEG\-4 object descriptor profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetODProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP,
+.br 
+        u_int8_t \fIprofileLevel\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetODProfileLevel\fR sets the minumum profile/level of MPEG\-4 object descriptor support necessary to render the contents of the file.
+.LP 
+ISO/IEC 14496\-1:2001 MPEG\-4 Systems defines the following values:
+.br 
+	0x00		Reserved
+.br 
+	0x01\-0x7F	Reserved
+.br 
+	0x80\-0xFD	User private
+.br 
+	0xFE		No object descriptor profile specified
+.br 
+	0xFF		No object descriptor required
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetODProfileLevel(3)
diff --git a/doc/MP4SetRtpTimestampStart.3 b/doc/MP4SetRtpTimestampStart.3
new file mode 100644
index 0000000..54c5f44
--- /dev/null
+++ b/doc/MP4SetRtpTimestampStart.3
@@ -0,0 +1,39 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetRtpTimestampStart\fR \- Set the RTP start time of a hint track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetRtpTimestampStart\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fIhintTrackId\fP,
+.br 
+	MP4Timestamp \fIstartTime\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIstartTime\fP
+Specifies the desired RTP start timestamp for the hint track.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetRtpTimestampStart\fR sets the RTP timestamp start of the specified hint track. Typically this is a random value that is chosen when the first RTP packet is constructed by the MP4 library. However the  value can be set explicitly for the hint track and stored. Typically this is used if it is desired that timestamps start at zero.
+.LP 
+See IETF RFC 1889 for details regarding RTP timestamps and RTCP.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetRtpTimestampStart(3)
diff --git a/doc/MP4SetSceneProfileLevel.3 b/doc/MP4SetSceneProfileLevel.3
new file mode 100644
index 0000000..ae36ccf
--- /dev/null
+++ b/doc/MP4SetSceneProfileLevel.3
@@ -0,0 +1,43 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetSceneProfileLevel\fR \- Sets the minimum MPEG\-4 scene graph profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetSceneProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP,
+.br 
+        u_int8_t \fIprofileLevel\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetSceneProfileLevel\fR sets the minumum profile/level of MPEG\-4 scene graph support necessary to render the contents of the file.
+.LP 
+ISO/IEC 14496\-1:2001 MPEG\-4 Systems defines the following values:
+.br 
+	0x00		Reserved
+.br 
+	0x01		Simple 2D Profile @ Level 1
+.br 
+	0x02\-0x7F	Reserved
+.br 
+	0x80\-0xFD	User private
+.br 
+	0xFE		No scene graph profile specified
+.br 
+	0xFF		No scene graph required
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetSceneProfileLevel(3)
diff --git a/doc/MP4SetSessionSdp.3 b/doc/MP4SetSessionSdp.3
new file mode 100644
index 0000000..5d85cfc
--- /dev/null
+++ b/doc/MP4SetSessionSdp.3
@@ -0,0 +1,33 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetSessionSdp\fR \- Set the SDP session level description of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetSessionSdp\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	const char* sdpString
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fIsdpString\fP
+Specifies the new value of the session sdp string.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetSessionSdp\fR sets the SDP (IETF RFC 2327) session level fragment for the file. This is used by a streaming server to create a complete SDP description of the multimedia session represented by the file.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetSessionSdp(3) MP4AppendSessionSdp(3)
diff --git a/doc/MP4SetTrackESConfiguration.3 b/doc/MP4SetTrackESConfiguration.3
new file mode 100644
index 0000000..ba99eb7
--- /dev/null
+++ b/doc/MP4SetTrackESConfiguration.3
@@ -0,0 +1,43 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetTrackESConfiguration\fR \- Set the elementary stream (ES) configuration of a track
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetTrackESConfiguration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP
+.br 
+	const u_int8_t* \fIpConfig\fP,
+.br 
+	u_int32_t \fIconfigSize\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIpConfig\fP
+Specifies a pointer to the ES configuration information.
+.TP 
+\fIconfigSize\fP
+Specifies the size of the ES configuration information.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetTrackESConfiguration\fR sets the elementary stream (ES) configuration of the specified track in the mp4 file. This information is codec specific and contains the configuration necessary for the given codec to decode the samples in the track. 
+
+.SH "SEE ALSO"
+.LP 
+MP4(3)
diff --git a/doc/MP4SetTrackEditDuration.3 b/doc/MP4SetTrackEditDuration.3
new file mode 100644
index 0000000..0e666c6
--- /dev/null
+++ b/doc/MP4SetTrackEditDuration.3
@@ -0,0 +1,45 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetTrackEditDuration\fR \- Set the duration of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetTrackEditDuration\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP,
+.br 
+	MP4Duration \fIduration\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment to which the operation applies. Caveat: the first edit has id 1 not 0.
+.TP 
+\fIduration\fP
+Species the new value for the duration in track time scale units.
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetTrackEditDuration\fR sets the duration of the specified edit segment from the specified track in the track time scale units. See MP4ConvertToTrackDuration() for how to map this value from another time scale.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackEditDuration(3)
diff --git a/doc/MP4SetTrackEditDwell.3 b/doc/MP4SetTrackEditDwell.3
new file mode 100644
index 0000000..15324f0
--- /dev/null
+++ b/doc/MP4SetTrackEditDwell.3
@@ -0,0 +1,46 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetTrackEditDwell\fR \- Set the dwell value of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetTrackEditDwell\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP,
+.br 
+	bool \fIdwell\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment to which the operation applies. Caveat: the first edit has id 1 not 0.
+.TP 
+\fIdwell\fP
+Species the new dwell value.
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetTrackEditDwell\fR sets the dwell value of the specified edit segment from the specified track.
+.LP 
+A value of true (1) indicates that during this edit segment the media will be paused; a value of false (0) indicates that during this edit segment the media will be played at its normal rate.
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackEditDwell(3)
diff --git a/doc/MP4SetTrackEditMediaStart.3 b/doc/MP4SetTrackEditMediaStart.3
new file mode 100644
index 0000000..9fbcf29
--- /dev/null
+++ b/doc/MP4SetTrackEditMediaStart.3
@@ -0,0 +1,54 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetTrackEditMediaStart\fR \- Set the media start time of a track edit segment
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetTrackEditMediaStart\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4EditId \fIeditId\fP,
+.br 
+	MP4Timestamp \fImediaStartTime\fP
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the track to which the operation applies.
+.TP 
+\fIeditId\fP
+Specifies the edit segment to which the operation applies. Caveat: the first edit has id 1 not 0.
+.TP 
+\fImediaStartTime\fP
+Species the new value for the media start in track time scale units.
+
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetTrackEditMediaStart\fR sets the media start time of the specified edit segment from the specified track in the track time scale units. See MP4ConvertToTrackTimestamp() for how to map this value from another time scale.
+.LP 
+Note that this differs from the edit segment start time. For example:
+.LP 
+EditId	Start	MediaStart	Duration
+1		0		15			30
+.br 
+2		30		120			20
+.br 	
+3		50		3000			10
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetTrackEditMediaStart(3)
diff --git a/doc/MP4SetVideoProfileLevel.3 b/doc/MP4SetVideoProfileLevel.3
new file mode 100644
index 0000000..538bcfb
--- /dev/null
+++ b/doc/MP4SetVideoProfileLevel.3
@@ -0,0 +1,81 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4SetVideoProfileLevel\fR \- Sets the minimum MPEG\-4 video profile and level required to render the contents of the file
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4SetVideoProfileLevel\fR(
+.br 
+        MP4FileHandle \fIhFile\fP,
+.br 
+        u_int8_t \fIprofileLevel\fP
+.br 
+)
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4SetVideoProfileLevel\fR sets the minumum profile/level of MPEG\-4 video support necessary to render the contents of the file.
+.LP 
+ISO/IEC 14496\-1:2001 MPEG\-4 Systems defines the following values:
+.br 
+	0x00		Reserved
+.br 
+	0x01		Simple Profile @ Level 3
+.br 
+	0x02		Simple Profile @ Level 2
+.br 
+	0x03		Simple Profile @ Level 1
+.br 
+	0x04		Simple Scalable Profile @ Level 2
+.br 
+	0x05		Simple Scalable Profile @ Level 1
+.br 
+	0x06		Core Profile @ Level 2
+.br 
+	0x07		Core Profile @ Level 1
+.br 
+	0x08		Main Profile @ Level 4
+.br 
+	0x09		Main Profile @ Level 3
+.br 
+	0x0A		Main Profile @ Level 2
+.br 
+	0x0B		N\-Bit Profile @ Level 2
+.br 
+	0x0C		Hybrid Profile @ Level 2
+.br 
+	0x0D		Hybrid Profile @ Level 1
+.br 
+	0x0E		Basic Animated Texture @ Level 2
+.br 
+	0x0F		Basic Animated Texture @ Level 1
+.br 
+	0x10		Scalable Texture @ Level 3
+.br 
+	0x11		Scalable Texture @ Level 2
+.br 
+	0x12		Scalable Texture @ Level 1
+.br 
+	0x13		Simple Face Animation @ Level 2
+.br 
+	0x14		Simple Face Animation @ Level 1
+.br 
+	0x15\-0x7F	Reserved
+.br 
+	0x80\-0xFD	User private
+.br 
+	0xFE		No audio profile specified
+.br 
+	0xFF		No audio required
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4GetVideoProfileLevel(3)
diff --git a/doc/MP4WriteRtpHint.3 b/doc/MP4WriteRtpHint.3
new file mode 100644
index 0000000..53220ca
--- /dev/null
+++ b/doc/MP4WriteRtpHint.3
@@ -0,0 +1,43 @@
+.TH "MP4" "3" "Version 0.9" "Cisco Systems Inc." "MP4 File Format Library"
+.SH "NAME"
+.LP 
+\fBMP4WriteRtpHint\fR \- Write an RTP hint
+.SH "SYNTAX"
+.LP 
+#include <mp4.h>
+.LP 
+bool \fBMP4WriteRtpHint\fR(
+.br 
+	MP4FileHandle \fIhFile\fP,
+.br 
+	MP4TrackId \fItrackId\fP,
+.br 
+	MP4Duration \fIduration\fP,
+.br 
+	bool \fIisSyncSample\fP = true
+.br 
+);
+.SH "ARGUMENTS"
+.LP 
+.TP 
+\fIhFile\fP
+Specifies the mp4 file to which the operation applies.
+.TP 
+\fItrackId\fP
+Specifies the hint track to which the operation applies.
+.TP 
+\fIduration\fP
+The duration for this hint sample. Typically this is the same duration as for the corresponding sample in the reference media track. Caveat: The duration should be in the hint track timescale units, again typically the same as the reference media track.
+.TP 
+\fIisSyncSample\fP
+The sync/random access flag for this sample. Typically this is the same as for the corresponding sample in the reference media track.
+.SH "RETURN VALUES"
+.LP 
+Upon success, true (1). Upon an error, false (0).
+.SH "DESCRIPTION"
+.LP 
+\fBMP4WriteRtpHint\fR writes the current pending hint created with MP4AddRtpHint() to the specified hint track.
+
+.SH "SEE ALSO"
+.LP 
+MP4(3) MP4AddRtpHint (3)
diff --git a/doc/doxygen/Doxyfile.m4 b/doc/doxygen/Doxyfile.m4
new file mode 100644
index 0000000..a2d83ec
--- /dev/null
+++ b/doc/doxygen/Doxyfile.m4
@@ -0,0 +1,1468 @@
+changequote(<<,>>)dnl
+include(<<project.m4>>)dnl
+# Doxyfile 1.5.7
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+#       TAG = value [value, ...]
+# For lists items can also be appended using:
+#       TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file 
+# that follow. The default is UTF-8 which is also the encoding used for all 
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the 
+# iconv built into libc) for the transcoding. See 
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded 
+# by quotes) that should identify the project.
+
+PROJECT_NAME           = PROJECT_name
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. 
+# This could be handy for archiving the generated documentation or 
+# if some version control system is used.
+
+#PROJECT_NUMBER         = PROJECT_version
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) 
+# base path where the generated documentation will be put. 
+# If a relative path is entered, it will be relative to the location 
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = __MAKE_ABS_TOP_BUILDDIR/doc/api
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 
+# 4096 sub-directories (in 2 levels) under the output directory of each output 
+# format and will distribute the generated files over these directories. 
+# Enabling this option can be useful when feeding doxygen a huge amount of 
+# source files, where putting all generated files in the same directory would 
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS         = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all 
+# documentation generated by doxygen is written. Doxygen will use this 
+# information to generate all constant output in the proper language. 
+# The default language is English, other supported languages are: 
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, 
+# Croatian, Czech, Danish, Dutch, Farsi, Finnish, French, German, Greek, 
+# Hungarian, Italian, Japanese, Japanese-en (Japanese with English messages), 
+# Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, Polish, 
+# Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, Slovene, 
+# Spanish, Swedish, and Ukrainian.
+
+OUTPUT_LANGUAGE        = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will 
+# include brief member descriptions after the members that are listed in 
+# the file and class documentation (similar to JavaDoc). 
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend 
+# the brief description of a member or function before the detailed description. 
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the 
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator 
+# that is used to form the text in various listings. Each string 
+# in this list, if found as the leading text of the brief description, will be 
+# stripped from the text and the result after processing the whole list, is 
+# used as the annotated text. Otherwise, the brief description is used as-is. 
+# If left blank, the following values are used ("$name" is automatically 
+# replaced with the name of the entity): "The $name class" "The $name widget" 
+# "The $name file" "is" "provides" "specifies" "contains" 
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF       = 
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then 
+# Doxygen will generate a detailed section even if there is only a brief 
+# description.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all 
+# inherited members of a class in the documentation of that class as if those 
+# members were ordinary class members. Constructors, destructors and assignment 
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full 
+# path before files name in the file list and in the header files. If set 
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES        = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag 
+# can be used to strip a user-defined part of the path. Stripping is 
+# only done if one of the specified strings matches the left-hand part of 
+# the path. The tag can be used to show relative paths in the file list. 
+# If left blank the directory from which doxygen is run is used as the 
+# path to strip.
+
+STRIP_FROM_PATH        = __MAKE_ABS_TOP_SRCDIR
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of 
+# the path mentioned in the documentation of a class, which tells 
+# the reader which header file to include in order to use a class. 
+# If left blank only the name of the header file containing the class 
+# definition is used. Otherwise one should specify the include paths that 
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH    = 
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter 
+# (but less readable) file names. This can be useful is your file systems 
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen 
+# will interpret the first line (until the first dot) of a JavaDoc-style 
+# comment as the brief description. If set to NO, the JavaDoc 
+# comments will behave just like regular Qt-style comments 
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF      = YES
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will 
+# interpret the first line (until the first dot) of a Qt-style 
+# comment as the brief description. If set to NO, the comments 
+# will behave just like regular Qt-style comments (thus requiring 
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen 
+# treat a multi-line C++ special comment block (i.e. a block of //! or /// 
+# comments) as a brief description. This used to be the default behaviour. 
+# The new default is to treat a multi-line C++ comment block as a detailed 
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented 
+# member inherits the documentation from any documented member that it 
+# re-implements.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce 
+# a new page for each member. If set to NO, the documentation of a member will 
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. 
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that acts 
+# as commands in the documentation. An alias has the form "name=value". 
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to 
+# put the command \sideeffect (or @sideeffect) in the documentation, which 
+# will result in a user-defined paragraph with heading "Side Effects:". 
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES                = 
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C 
+# sources only. Doxygen will then generate output that is more tailored for C. 
+# For instance, some of the names that are used will be different. The list 
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Java. For instance, namespaces will be presented as packages, qualified 
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran 
+# sources only. Doxygen will then generate output that is more tailored for 
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL 
+# sources. Doxygen will then generate output that is tailored for 
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want 
+# to include (a tag file for) the STL sources as input, then you should 
+# set this tag to YES in order to let doxygen match functions declarations and 
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. 
+# func(std::string) {}). This also make the inheritance and collaboration 
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. 
+# Doxygen will parse them like normal C++ but will assume all classes use public 
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter 
+# and setter methods for a property. Setting this option to YES (the default) 
+# will make doxygen to replace the get and set methods by a property in the 
+# documentation. This will only work if the methods are indeed getting or 
+# setting a simple type. If this is not the case, or you want to show the 
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC 
+# tag is set to YES, then doxygen will reuse the documentation of the first 
+# member in the group (if any) for the other members of the group. By default 
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of 
+# the same type (for instance a group of public functions) to be put as a 
+# subgroup of that type (e.g. under the Public Functions section). Set it to 
+# NO to prevent subgrouping. Alternatively, this can be done per class using 
+# the \nosubgrouping command.
+
+SUBGROUPING            = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum 
+# is documented as struct, union, or enum with the name of the typedef. So 
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct 
+# with name TypeT. When disabled the typedef will appear as a member of a file, 
+# namespace, or class. And the struct will be named TypeS. This can typically 
+# be useful for C code in case the coding convention dictates that all compound 
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to 
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is 
+# probably good enough. For larger projects a too small cache size can cause 
+# doxygen to be busy swapping symbols to and from disk most of the time 
+# causing a significant performance penality. 
+# If the system has enough physical memory increasing the cache will improve the 
+# performance by keeping more symbols in memory. Note that the value works on 
+# a logarithmic scale so increasing the size by one will rougly double the 
+# memory usage. The cache size is given by this formula: 
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, 
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in 
+# documentation are documented, even if no documentation was available. 
+# Private class members and static file members will be hidden unless 
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL            = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class 
+# will be included in the documentation.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file 
+# will be included in the documentation.
+
+EXTRACT_STATIC         = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) 
+# defined locally in source files will be included in the documentation. 
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. When set to YES local 
+# methods, which are defined in the implementation section but not in 
+# the interface are included in the documentation. 
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be 
+# extracted and appear in the documentation as a namespace called 
+# 'anonymous_namespace{file}', where file will be replaced with the base 
+# name of the file that contains the anonymous namespace. By default 
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all 
+# undocumented members of documented classes, files or namespaces. 
+# If set to NO (the default) these members will be included in the 
+# various overviews, but no documentation section is generated. 
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all 
+# undocumented classes that are normally visible in the class hierarchy. 
+# If set to NO (the default) these classes will be included in the various 
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all 
+# friend (class|struct|union) declarations. 
+# If set to NO (the default) these declarations will be included in the 
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any 
+# documentation blocks found inside the body of a function. 
+# If set to NO (the default) these blocks will be appended to the 
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation 
+# that is typed after a \internal command is included. If the tag is set 
+# to NO (the default) then the documentation will be excluded. 
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate 
+# file names in lower-case letters. If set to YES upper-case letters are also 
+# allowed. This is useful if you have classes or files whose names only differ 
+# in case and if your file system supports case sensitive file names. Windows 
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES       = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen 
+# will show members with their full class and namespace scopes in the 
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen 
+# will put a list of the files that are included by a file in the documentation 
+# of that file.
+
+SHOW_INCLUDE_FILES     = NO
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] 
+# is inserted in the documentation for inline members.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen 
+# will sort the (detailed) documentation of file and class members 
+# alphabetically by member name. If set to NO the members will appear in 
+# declaration order.
+
+SORT_MEMBER_DOCS       = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the 
+# brief documentation of file, namespace and class members alphabetically 
+# by member name. If set to NO (the default) the members will appear in 
+# declaration order.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the 
+# hierarchy of group names into alphabetical order. If set to NO (the default) 
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be 
+# sorted by fully-qualified names, including namespaces. If set to 
+# NO (the default), the class list will be sorted only by class name, 
+# not including the namespace part. 
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the 
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME     = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or 
+# disable (NO) the todo list. This list is created by putting \todo 
+# commands in the documentation.
+
+GENERATE_TODOLIST      = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or 
+# disable (NO) the test list. This list is created by putting \test 
+# commands in the documentation.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or 
+# disable (NO) the bug list. This list is created by putting \bug 
+# commands in the documentation.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or 
+# disable (NO) the deprecated list. This list is created by putting 
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional 
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS       = 
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines 
+# the initial value of a variable or define consists of for it to appear in 
+# the documentation. If the initializer consists of more lines than specified 
+# here it will be hidden. Use a value of 0 to hide initializers completely. 
+# The appearance of the initializer of individual variables and defines in the 
+# documentation can be controlled using \showinitializer or \hideinitializer 
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated 
+# at the bottom of the documentation of classes and structs. If set to YES the 
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES        = NO
+
+# If the sources in your project are distributed over multiple directories 
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy 
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES       = NO
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the 
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES             = NO
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the 
+# Namespaces page.  This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES        = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that 
+# doxygen should invoke to get the current version for each file (typically from 
+# the version control system). Doxygen will invoke the program by executing (via 
+# popen()) the command <command> <input-file>, where <command> is the value of 
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file 
+# provided by doxygen. Whatever the program writes to standard output 
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER    = 
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by 
+# doxygen. The layout file controls the global structure of the generated output files 
+# in an output format independent way. The create the layout file that represents 
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a 
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name 
+# of the layout file.
+
+LAYOUT_FILE            = 
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated 
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are 
+# generated by doxygen. Possible values are YES and NO. If left blank 
+# NO is used.
+
+WARNINGS               = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings 
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will 
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for 
+# potential errors in the documentation, such as not documenting some 
+# parameters in a documented function, or documenting parameters that 
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for 
+# functions that are documented, but have no documentation for their parameters 
+# or return value. If set to NO (the default) doxygen will only warn about 
+# wrong or incomplete parameter documentation, but not about the absence of 
+# documentation.
+
+WARN_NO_PARAMDOC       = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that 
+# doxygen can produce. The string should contain the $file, $line, and $text 
+# tags, which will be replaced by the file and line number from which the 
+# warning originated and the warning text. Optionally the format may contain 
+# $version, which will be replaced by the version of the file (if it could 
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning 
+# and error messages should be written. If left blank the output is written 
+# to stderr.
+
+WARN_LOGFILE           = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain 
+# documented source files. You may enter file names like "myfile.cpp" or 
+# directories like "/usr/src/myproject". Separate the files or directories 
+# with spaces.
+
+INPUT = __MAKE_ABS_TOP_SRCDIR/doc/doxygen/mainpage.h \
+    __MAKE_ABS_TOP_SRCDIR/include \
+    __MAKE_ABS_TOP_SRCDIR/libplatform \
+    __MAKE_ABS_TOP_SRCDIR/src/itmf \
+    __MAKE_ABS_TOP_SRCDIR/src/qtff
+
+# This tag can be used to specify the character encoding of the source files 
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is 
+# also the default input encoding. Doxygen uses libiconv (or the iconv built 
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for 
+# the list of possible encodings.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the 
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank the following patterns are tested: 
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx 
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS          = *.h
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories 
+# should be searched for input files as well. Possible values are YES and NO. 
+# If left blank NO is used.
+
+RECURSIVE              = YES
+
+# The EXCLUDE tag can be used to specify files and/or directories that should 
+# excluded from the INPUT source files. This way you can easily exclude a 
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE                = 
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or 
+# directories that are symbolic links (a Unix filesystem feature) are excluded 
+# from the input.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the 
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude 
+# certain files from those directories. Note that the wildcards are matched 
+# against the file with absolute path, so to exclude all test directories 
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = 
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names 
+# (namespaces, classes, functions, etc.) that should be excluded from the 
+# output. The symbol name can be a fully qualified name, a word, or if the 
+# wildcard * is used, a substring. Examples: ANamespace, AClass, 
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS        = 
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or 
+# directories that contain example code fragments that are included (see 
+# the \include command).
+
+EXAMPLE_PATH           = __MAKE_ABS_TOP_SRCDIR
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the 
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp 
+# and *.h) to filter out the source-files in the directories. If left 
+# blank all files are included.
+
+EXAMPLE_PATTERNS       = 
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be 
+# searched for input files to be used with the \include or \dontinclude 
+# commands irrespective of the value of the RECURSIVE tag. 
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE      = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or 
+# directories that contain image that are included in the documentation (see 
+# the \image command).
+
+IMAGE_PATH             =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should 
+# invoke to filter for each input file. Doxygen will invoke the filter program 
+# by executing (via popen()) the command <filter> <input-file>, where <filter> 
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an 
+# input file. Doxygen will then use the output that the filter program writes 
+# to standard output.  If FILTER_PATTERNS is specified, this tag will be 
+# ignored.
+
+INPUT_FILTER           = 
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern 
+# basis.  Doxygen will compare the file name with each pattern and apply the 
+# filter if there is a match.  The filters are a list of the form: 
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further 
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER 
+# is applied to all files.
+
+FILTER_PATTERNS        = 
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using 
+# INPUT_FILTER) will be used to filter the input files when producing source 
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES    = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will 
+# be generated. Documented entities will be cross-referenced with these sources. 
+# Note: To get rid of all source code in the generated output, make sure also 
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER         = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body 
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct 
+# doxygen to hide any special comment blocks from generated source code 
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES 
+# then for each documented function all documented 
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES 
+# then for each documented function all documented entities 
+# called/used by that function will be listed.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.  Otherwise they will link to the documentstion.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code 
+# will point to the HTML generated by the htags(1) tool instead of doxygen 
+# built-in source browser. The htags tool is part of GNU's global source 
+# tagging system (see http://www.gnu.org/software/global/global.html). You 
+# will need version 4.8.6 or higher.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen 
+# will generate a verbatim copy of the header file for each class for 
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS       = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then 
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns 
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX    = 5
+
+# In case all classes in a project start with a common prefix, all 
+# classes will be put under the same header in the alphabetical index. 
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that 
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX          = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will 
+# generate HTML output.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for 
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank 
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard header.
+
+HTML_HEADER            = __MAKE_ABS_TOP_BUILDDIR/doc/doxygen/header.html
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for 
+# each generated HTML page. If it is left blank doxygen will generate a 
+# standard footer.
+
+HTML_FOOTER            = __MAKE_ABS_TOP_BUILDDIR/doc/doxygen/footer.html
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading 
+# style sheet that is used by each HTML page. It can be used to 
+# fine-tune the look of the HTML output. If the tag is left blank doxygen 
+# will generate a default style sheet. Note that doxygen will try to copy 
+# the style sheet file to the HTML output directory, so don't put your own 
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET        = __MAKE_ABS_TOP_SRCDIR/doc/doxygen/project.css
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, 
+# files or namespaces will be aligned in HTML using tables. If set to 
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS     = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML 
+# documentation will contain sections that can be hidden and shown after the 
+# page has loaded. For this to work a browser that supports 
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox 
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS  = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files 
+# will be generated that can be used as input for Apple's Xcode 3 
+# integrated development environment, introduced with OSX 10.5 (Leopard). 
+# To create a documentation set, doxygen will generate a Makefile in the 
+# HTML output directory. Running make will produce the docset in that 
+# directory and running "make install" will install the docset in 
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find 
+# it at startup. 
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET        = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the 
+# feed. A documentation feed provides an umbrella under which multiple 
+# documentation sets from a single provider (such as a company or product suite) 
+# can be grouped.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that 
+# should uniquely identify the documentation set bundle. This should be a 
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen 
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files 
+# will be generated that can be used as input for tools like the 
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) 
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP      = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can 
+# be used to specify the file name of the resulting .chm file. You 
+# can add a path in front of the file if the result should not be 
+# written to the html output directory.
+
+CHM_FILE               = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can 
+# be used to specify the location (absolute path including file name) of 
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run 
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION           = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag 
+# controls if a separate .chi index file is generated (YES) or that 
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI           = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING     = 
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag 
+# controls whether a binary table of contents is generated (YES) or a 
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members 
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER 
+# are set, an additional index file will be generated that can be used as input for 
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated 
+# HTML documentation.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can 
+# be used to specify the file name of the resulting .qch file. 
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE               = 
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#namespace">Qt Help Project / Namespace</a>.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating 
+# Qt Help Project output. For more information please see 
+# <a href="http://doc.trolltech.com/qthelpproject.html#virtual-folders">Qt Help Project / Virtual Folders</a>.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can 
+# be used to specify the location of Qt's qhelpgenerator. 
+# If non-empty doxygen will try to run qhelpgenerator on the generated 
+# .qhp file .
+
+QHG_LOCATION           = 
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at 
+# top of each HTML page. The value NO (the default) enables the index and 
+# the value YES disables it.
+
+DISABLE_INDEX          = NO
+
+# This tag can be used to set the number of enum values (range [1..20]) 
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE   = 3
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to FRAME, a side panel will be generated
+# containing a tree-like index structure (just like the one that 
+# is generated for HTML Help). For this to work a browser that supports 
+# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, 
+# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are 
+# probably better off using the HTML help feature. Other possible values 
+# for this tag are: HIERARCHIES, which will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list;
+# ALL, which combines the behavior of FRAME and HIERARCHIES; and NONE, which
+# disables this behavior completely. For backwards compatibility with previous
+# releases of Doxygen, the values YES and NO are equivalent to FRAME and NONE
+# respectively.
+
+GENERATE_TREEVIEW      = NONE
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be 
+# used to set the initial width (in pixels) of the frame in which the tree 
+# is shown.
+
+TREEVIEW_WIDTH         = 250
+
+# Use this tag to change the font size of Latex formulas included 
+# as images in the HTML documentation. The default is 10. Note that 
+# when you change the font size after a successful doxygen run you need 
+# to manually remove any form_*.png images from the HTML output directory 
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE       = 10
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will 
+# generate Latex output.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be 
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to 
+# generate index for LaTeX. If left blank `makeindex' will be used as the 
+# default command name.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact 
+# LaTeX documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used 
+# by the printer. Possible values are: a4, a4wide, letter, legal and 
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE             = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX 
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES         = 
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for 
+# the generated latex document. The header should contain everything until 
+# the first chapter. If it is left blank doxygen will generate a 
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER           = 
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated 
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will 
+# contain links (just like the HTML output) instead of page references 
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of 
+# plain latex in the generated Makefile. Set this option to YES to get a 
+# higher quality PDF documentation.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. 
+# command to the generated LaTeX files. This will instruct LaTeX to keep 
+# running if errors occur, instead of asking the user for help. 
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE        = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not 
+# include the index chapters (such as File Index, Compound Index, etc.) 
+# in the output.
+
+LATEX_HIDE_INDICES     = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output 
+# The RTF output is optimized for Word 97 and may not look very pretty with 
+# other RTF readers or editors.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact 
+# RTF documents. This may be useful for small projects and may help to 
+# save some trees in general.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated 
+# will contain hyperlink fields. The RTF file will 
+# contain links (just like the HTML output) instead of page references. 
+# This makes the output suitable for online browsing using WORD or other 
+# programs which support those fields. 
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's 
+# config file, i.e. a series of assignments. You only have to provide 
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE    = 
+
+# Set optional variables used in the generation of an rtf document. 
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE    = 
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will 
+# generate man pages
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to 
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION          = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output, 
+# then it will generate one additional man file for each entity 
+# documented in the real man page(s). These additional files 
+# only source the real man page, but without them the man command 
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will 
+# generate an XML file that captures the structure of 
+# the code including all documentation.
+
+GENERATE_XML           = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. 
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be 
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT             = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_SCHEMA             = 
+
+# The XML_DTD tag can be used to specify an XML DTD, 
+# which can be used by a validating XML parser to check the 
+# syntax of the XML files.
+
+XML_DTD                = 
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will 
+# dump the program listings (including syntax highlighting 
+# and cross-referencing information) to the XML output. Note that 
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING     = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will 
+# generate an AutoGen Definitions (see autogen.sf.net) file 
+# that captures the structure of the code including all 
+# documentation. Note that this feature is still experimental 
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will 
+# generate a Perl module file that captures the structure of 
+# the code including all documentation. Note that this 
+# feature is still experimental and incomplete at the 
+# moment.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate 
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able 
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be 
+# nicely formatted so it can be parsed by a human reader.  This is useful 
+# if you want to understand what is going on.  On the other hand, if this 
+# tag is set to NO the size of the Perl module output will be much smaller 
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file 
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. 
+# This is useful so different doxyrules.make files included by the same 
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX = 
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor   
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will 
+# evaluate all C-preprocessor directives found in the sources and include 
+# files.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro 
+# names in the source code. If set to NO (the default) only conditional 
+# compilation will be performed. Macro expansion can be done in a controlled 
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION        = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES 
+# then the macro expansion is limited to the macros specified with the 
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files 
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that 
+# contain include files that are not input files but should be processed by 
+# the preprocessor.
+
+INCLUDE_PATH           = 
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard 
+# patterns (like *.h and *.hpp) to filter out the header-files in the 
+# directories. If left blank, the patterns specified with FILE_PATTERNS will 
+# be used.
+
+INCLUDE_FILE_PATTERNS  = 
+
+# The PREDEFINED tag can be used to specify one or more macro names that 
+# are defined before the preprocessor is started (similar to the -D option of 
+# gcc). The argument of the tag is a list of macros of the form: name 
+# or name=definition (no spaces). If the definition and the = are 
+# omitted =1 is assumed. To prevent a macro definition from being 
+# undefined via #undef or recursively expanded use the := operator 
+# instead of the = operator.
+
+PREDEFINED =  MP4V2_EXPORT MP4V2_DEPRECATED DEFAULT(x)=" =x" MP4V2_WFORMAT_PRINTF()=
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then 
+# this tag can be used to specify a list of macro names that should be expanded. 
+# The macro definition that is found in the sources will be used. 
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED      = 
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then 
+# doxygen's preprocessor will remove all function-like macros that are alone 
+# on a line, have an all uppercase name, and do not end with a semicolon. Such 
+# function macros are typically used for boiler-plate code, and will confuse 
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references   
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles. 
+# Optionally an initial location of the external documentation 
+# can be added for each tagfile. The format of a tag file without 
+# this location is as follows: 
+#   TAGFILES = file1 file2 ... 
+# Adding location for the tag files is done as follows: 
+#   TAGFILES = file1=loc1 "file2 = loc2" ... 
+# where "loc1" and "loc2" can be relative or absolute paths or 
+# URLs. If a location is present for each tag, the installdox tool 
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen 
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES               = 
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create 
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE       = 
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed 
+# in the class index. If set to NO only the inherited external classes 
+# will be listed.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed 
+# in the modules index. If set to NO, only the current project's groups will 
+# be listed.
+
+EXTERNAL_GROUPS        = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script 
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH              = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool   
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will 
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base 
+# or super classes. Setting the tag to NO turns the diagrams off. Note that 
+# this option is superseded by the HAVE_DOT option below. This is only a 
+# fallback. It is recommended to install and use dot, since it yields more 
+# powerful graphs.
+
+CLASS_DIAGRAMS         = YES
+
+# You can define message sequence charts within doxygen comments using the \msc 
+# command. Doxygen will then run the mscgen tool (see 
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the 
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where 
+# the mscgen tool resides. If left empty the tool is assumed to be found in the 
+# default search path.
+
+MSCGEN_PATH            = 
+
+# If set to YES, the inheritance and collaboration graphs will hide 
+# inheritance and usage relations if the target is undocumented 
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is 
+# available from the path. This tool is part of Graphviz, a graph visualization 
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section 
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT               = NO
+
+# By default doxygen will write a font called FreeSans.ttf to the output 
+# directory and reference it in all dot files that doxygen generates. This 
+# font does not include all possible unicode characters however, so when you need 
+# these (or just want a differently looking font) you can specify the font name 
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font, 
+# which can be done by putting it in a standard location or by setting the 
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory 
+# containing the font.
+
+DOT_FONTNAME           = FreeSans
+
+# By default doxygen will tell dot to use the output directory to look for the 
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a 
+# different font using DOT_FONTNAME you can set the path where dot 
+# can find it using this tag.
+
+DOT_FONTPATH           = 
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect inheritance relations. Setting this tag to YES will force the 
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for each documented class showing the direct and 
+# indirect implementation dependencies (inheritance, containment, and 
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen 
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and 
+# collaboration diagrams in a style similar to the OMG's Unified Modeling 
+# Language.
+
+UML_LOOK               = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the 
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT 
+# tags are set to YES then doxygen will generate a graph for each documented 
+# file showing the direct and indirect include dependencies of the file with 
+# other documented files.
+
+INCLUDE_GRAPH          = YES
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and 
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each 
+# documented header file showing the documented files that directly or 
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then 
+# doxygen will generate a call dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable call graphs 
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then 
+# doxygen will generate a caller dependency graph for every global function 
+# or class method. Note that enabling this option will significantly increase 
+# the time of a run. So in most cases it will be better to enable caller 
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen 
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES 
+# then doxygen will show the dependencies a directory has on other directories 
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images 
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT       = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be 
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH               = 
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that 
+# contain dot files that are included in the documentation (see the 
+# \dotfile command).
+
+DOTFILE_DIRS           = 
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of 
+# nodes that will be shown in the graph. If the number of nodes in a graph 
+# becomes larger than this value, doxygen will truncate the graph, which is 
+# visualized by representing a node as a red box. Note that doxygen if the 
+# number of direct children of the root node in a graph is already larger than 
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note 
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the 
+# graphs generated by dot. A depth value of 3 means that only nodes reachable 
+# from the root by following a path via at most 3 edges will be shown. Nodes 
+# that lay further from the root node will be omitted. Note that setting this 
+# option to 1 or 2 may greatly reduce the computation time needed for large 
+# code bases. Also note that the size of a graph can be further restricted by 
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent 
+# background. This is disabled by default, because dot on Windows does not 
+# seem to support this out of the box. Warning: Depending on the platform used, 
+# enabling this option may lead to badly anti-aliased labels on the edges of 
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output 
+# files in one run (i.e. multiple -o and -T options on the command line). This 
+# makes dot run faster, but since only newer versions of dot (>1.8.10) 
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will 
+# generate a legend page explaining the meaning of the various boxes and 
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will 
+# remove the intermediate dot files that are used to generate 
+# the various graphs.
+
+DOT_CLEANUP            = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine   
+#---------------------------------------------------------------------------
+
+# The SEARCHENGINE tag specifies whether or not a search engine should be 
+# used. If set to NO the values of all tags below this one will be ignored.
+
+SEARCHENGINE           = NO
diff --git a/doc/doxygen/banner.png b/doc/doxygen/banner.png
new file mode 100644
index 0000000..52dd326
--- /dev/null
+++ b/doc/doxygen/banner.png
Binary files differ
diff --git a/doc/doxygen/footer.html.m4 b/doc/doxygen/footer.html.m4
new file mode 100644
index 0000000..5a938c7
--- /dev/null
+++ b/doc/doxygen/footer.html.m4
@@ -0,0 +1,10 @@
+changequote(<<,>>)dnl
+include(<<project.m4>>)dnl
+<div class="footer">
+    <div>
+        generated on $datetime for <b>__PROJECT_name (__PROJECT_version)</b>
+        by&nbsp;<a href="http://www.doxygen.org">Doxygen</a> $doxygenversion
+    </div>
+</div>
+</body>
+</html>
diff --git a/doc/doxygen/header.html.m4 b/doc/doxygen/header.html.m4
new file mode 100644
index 0000000..d7f1d1b
--- /dev/null
+++ b/doc/doxygen/header.html.m4
@@ -0,0 +1,24 @@
+changequote(<<,>>)dnl
+include(<<project.m4>>)dnl
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+    <title>__PROJECT_name_formal $title</title>
+    <link href="project.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+<div id="sitebanner">
+    <div id="home">
+        <a href="__PROJECT_url_website"></a>
+    </div>
+    <div id="title">
+        $title
+    </div>
+    <div id="menu">
+        <ul>
+            <li><a href="__PROJECT_url_website">__PROJECT_name Home</a></li>
+            <li><a href="../index.html">Documentation</a></li>
+            <li class="active">API Reference</li>
+        </ul>
+    </div>
+</div>
diff --git a/doc/doxygen/mainpage.h b/doc/doxygen/mainpage.h
new file mode 100644
index 0000000..dbbc258
--- /dev/null
+++ b/doc/doxygen/mainpage.h
@@ -0,0 +1,63 @@
+/** @mainpage MP4v2 API Reference
+
+@section api Public API
+
+The public API is documented in the <a href="modules.html">modules</a>
+section (as seen at the top of this page).
+Other documentation available for C++ namespaces and classes are for
+internal documentation purposes and not to be used or depended-upon by
+code external to this project.
+
+@section introducation Introduction
+
+The MP4v2 library provides an API to create and modify mp4 files as defined
+by ISO base media file format, document number ISO/IEC 14496-12:2005(E). It
+is a very powerful and extensible format that can accomodate practically
+any type of media.
+
+The conceptual structure of an mp4 file is that it is a container for one
+or more tracks of media. Each track represents exactly one type of media
+such as audio or video. Each track is composed of high-level structural
+data which describes the raw media data stream in a manner that is as
+generic as possible. Tracks have their own timeline, properties and
+samples. An example of a sample is a frame of video. Thus, the file
+file describes how to synchronize the timeline of the tracks.
+
+In a self-contained file, the samples accounts for the majority of file
+size. For playback performance reasons, when an mp4 file contains multiple
+media tracks their samples (raw media data) are usually interleaved to
+provide optimal playback performance.
+
+@section invocation Invocation
+
+The mp4 library is focussed on providing an easy to use API for the mp4
+file format. It has been used with an encoder, a server, a player, and a
+number of mp4 utilities. However, it may not be adequate for multimedia
+editors that wish to work directly with mp4 files. It may be used by these
+type of tools to export an mp4 file.
+
+In providing a easy to use API not all the information in the mp4 file is
+directly exposed via the API. To accomodate applications that need access
+to information not otherwise available via the API there are file and
+track level generic get and set property routines that use arbitary string
+property names. To use these routines you will need to be familar with the
+mp4 file specification or be willing to wade through the output of a
+file-dump which may be produced using MP4Dump() or with some of the
+command-line tools bundled with MP4v2.
+
+@section headers Public Headers
+
+The public libary API is defined in @c <mp4v2/mp4v2.h> which includes all the
+necessary dependent header files. <b>You must never use any other header
+files</b> for public API. Using other header files or symbols which are
+not exported via the public API is expressly not supported and may change
+at any time without notice.
+
+The MP4v2 library can be used by either C or C++ programs. The calling
+convention is C but for convenience if C++ is used default function arguments
+have been added where appropriate.
+
+@example example/provider/provider.c
+@example example/itmf/generic.c
+@example example/itmf/tags.c
+*/
diff --git a/doc/doxygen/project.css b/doc/doxygen/project.css
new file mode 100644
index 0000000..3df0b10
--- /dev/null
+++ b/doc/doxygen/project.css
@@ -0,0 +1,347 @@
+* {
+    margin: 0px;
+    padding: 0px;
+}
+
+body,table {
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 13px;
+}
+
+body {
+    background-color: #3C464D;
+    min-width: 800px;
+    max-width: 1360px;
+    padding-top: 0.5em;
+    padding-bottom: 1em;
+}
+
+a {
+    color: #5382A1;
+    text-decoration: none;
+    font-weight: bold;
+}
+
+a:hover {
+    color: #9E005D;
+}
+
+h1 {
+    text-align: center;
+    font-size: 140%;
+}
+
+h2 {
+    font-size: 120%;
+}
+
+h3 {
+    font-size: 100%;
+}
+
+img {
+    border: none;
+    vertical-align: middle;
+}
+
+/* ----------------------------------*/
+
+dd,ul {
+    padding-left: 40px;
+}
+
+h1,h2,h3 {
+    margin-top: 8px;
+    margin-bottom: 8px;
+}
+
+h2,h3 {
+    font-weight: normal;
+    border-bottom: 2px solid;
+    margin-top: 2em;
+}
+
+h1 a, h2 a, h3 a {
+    color: black;
+}
+
+h1 small a {
+    color: #5382A1;
+    text-decoration: none;
+    font-weight: bold;
+}
+
+p,ul {
+    margin-top: 1em;
+    margin-bottom: 1em;
+}
+
+/* ----------------------------------*/
+
+dl ul,
+ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+/* ----------------------------------*/
+
+div#sitebanner,
+div.navigation, div.contents, div.footer {
+    margin-left: 40px;
+    margin-right: 40px;
+    background-color: #EDEFF2;
+    position: relative;
+}
+
+div.navigation div.tabs, div.navigation div.navpath, div.contents, div.footer div {
+    padding-left: 16px;
+    padding-right: 16px;
+}
+
+div.navigation, div.navigation, div.contents, div.footer {
+    -webkit-box-shadow: 5px 5px 5px black;
+}
+
+div#sitebanner {
+    background-color: gray;
+    background-image: url(banner.png);
+    background-repeat: no-repeat;
+    background-position: top left;
+    min-height: 128px;
+    max-height: 128px;
+    text-shadow: black 1px 1px 0px;
+    -webkit-border-top-left-radius: 1em;
+    -webkit-border-top-right-radius: 1em;
+    -webkit-box-shadow: 5px 5px 5px black;
+    -moz-border-radius-topleft: 1em;
+    -moz-border-radius-topright: 1em;
+}
+
+/* enable to make banner logo clickable */
+/*
+div#sitebanner div#home a {
+    display: block;
+    background: none;
+    position: absolute;
+    top: 8px;
+    left: 8px;
+    height: 80px;
+    width: 220px;
+}
+*/
+
+div#sitebanner div#title {
+    display: none;
+    position: absolute;
+    top: 40%;
+    right: 0;
+    left: 0;
+    text-align: center;
+    color: white;
+    color: #C9F08D;
+    font-weight: bold;
+    font-size: 150%;
+}
+
+div#sitebanner div#menu {
+    position: absolute;
+    top: 16px;
+    right: 16px;
+    color: white;
+    text-align: right;
+}
+
+div#sitebanner div#menu li {
+    list-style-type: none;
+}
+
+div#sitebanner div#menu li a {
+    color: white;
+    font-weight: normal;
+}
+
+div#sitebanner div#menu li.active,
+div#sitebanner div#menu li a:hover {
+    color: #C9F08D;
+}
+
+div.navigation {
+    background-color: #F8F9FA;
+}
+
+div.contents {
+    padding-top: 8px;
+    padding-bottom: 8px;
+}
+
+div.footer {
+    padding-top: 2em;
+    padding-bottom: 1em;
+    -webkit-border-bottom-left-radius: 1em;
+    -webkit-border-bottom-right-radius: 1em;
+    -moz-border-radius-bottomleft: 1em;
+    -moz-border-radius-bottomright: 1em;
+}
+
+div.footer div {
+    text-align: right;
+    font-size: x-small;
+    border-top: 2px solid #5382A1;
+}
+
+div.navigation > div {
+    padding-top: 4px;
+    padding-bottom: 4px;
+    border-bottom: 2px solid #5382A1;
+}
+
+div.navigation div.tabs ul {
+    margin: 0;
+    padding: 0;
+}
+
+div.navigation div.tabs ul li {
+    display: inline;
+    font-weight: bold;
+    margin-right: 1em;
+}
+
+div.navigation div.tabs ul li a {
+    color: #707277;
+}
+
+div.navigation div.tabs ul li a:hover {
+    color: #9E005D;
+}
+
+div.navigation div.tabs ul li.current a {
+    color: #5382A1;
+}
+
+div.navigation div.tabs ul li.current a:hover {
+    color: #9E005D;
+}
+
+/* ----------------------------------*/
+
+pre.fragment {
+    background-color: #FFFCE5;
+    border: 1px dashed;
+    margin: 1em;
+    padding: 0.5em;
+}
+
+pre.fragment span.comment {
+    color: #CF2FCF;
+}
+
+pre.fragment span.stringliteral {
+    color: #DB0D10;
+}
+
+div.navigation div.fragment {
+    padding-top: 8px;
+    padding-bottom: 8px;
+}
+
+/* ----------------------------------*/ 
+
+h1 + table {
+    margin-top: 1em;
+}
+
+table {
+    table-layout: auto;
+    border-spacing: 0;
+    border-collapse: collapse;
+}
+
+td.indexkey, td.indexvalue {
+    background-color: #D7D7D7;
+    border: 1px solid;
+    padding: 0.4em 0.5em;
+}
+
+td.memItemLeft, td.memItemRight {
+    background-color: #D7D7D7;
+}
+
+td.mdescLeft, td.mdescRight {
+    background-color: #E7E7E7;
+}
+
+td.memItemLeft, td.mdescLeft {
+    padding-left: 1em;
+}
+
+td.memItemRight, td.mdescRight {
+    padding-right: 1em;
+}
+
+hr {
+    border: 1px solid;
+    display: none;
+}
+
+/* ----------------------------------*/ 
+
+div.memitem {
+    border: 1px solid gray;
+    -webkit-border-radius: 0.5em;
+    -webkit-box-shadow: 2px 2px 2px black;
+    -moz-border-radius: 0.5em;
+}
+
+div.memproto {
+    background-color: #D7D7D7;
+    border-bottom: 1px solid gray;
+    padding-top: 0.5em;
+    padding-bottom: 0.5em;
+    -webkit-border-top-left-radius: 0.5em;
+    -webkit-border-top-right-radius: 0.5em;
+    -moz-border-radius-topleft: 0.5em;
+    -moz-border-radius-topright: 0.5em;
+}
+
+div.memdoc {
+    background-color: #E7E7E7;
+    padding-top: 0.5em;
+    padding-bottom: 0.5em;
+    -webkit-border-bottom-left-radius: 0.5em;
+    -webkit-border-bottom-right-radius: 0.5em;
+    -moz-border-radius-bottomleft: 0.5em;
+    -moz-border-radius-bottomright: 0.5em;
+}
+
+div.memproto,
+div.memdoc {
+    padding-left: 1em;
+    padding-right: 1em;
+}
+
+div.memproto td.memname {
+    padding-right: 1px;
+}
+
+div.memproto td.paramtype {
+    padding-left: 0.5em;
+    padding-right: 0.5em;
+}
+
+div.memproto td code {
+    color: #AA0000;
+    padding-left: 0.5em;
+    font-weight: bold;
+    text-align: right;
+}
+
+div.memdoc p:first-child {
+    margin-top: 0;
+}
+
+/* ----------------------------------*/ 
+
+dl.see p {
+    margin: 0;
+}
diff --git a/doc/html/banner.png b/doc/html/banner.png
new file mode 100644
index 0000000..52dd326
--- /dev/null
+++ b/doc/html/banner.png
Binary files differ
diff --git a/doc/html/footer.html.m4 b/doc/html/footer.html.m4
new file mode 100644
index 0000000..54504a8
--- /dev/null
+++ b/doc/html/footer.html.m4
@@ -0,0 +1,10 @@
+changequote(<<,>>)dnl
+include(<<project.m4>>)dnl
+</div>
+<div id="footer">
+    <div>
+        generated on __PROJECT_build for <b>__PROJECT_name (__PROJECT_version)</b>
+    </div>
+</div>
+</body>
+</html>
diff --git a/doc/html/header.html.m4 b/doc/html/header.html.m4
new file mode 100644
index 0000000..bf11af1
--- /dev/null
+++ b/doc/html/header.html.m4
@@ -0,0 +1,26 @@
+changequote(<<,>>)dnl
+include(<<project.m4>>)dnl
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
+    <title>__TITLE__</title>
+    <link href="project.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+<div id="page">
+<div id="sitebanner">
+    <div id="home">
+        <a href="__PROJECT_url_website"></a>
+    </div>
+    <div id="title">
+        __SHORT_TITLE__
+    </div>
+    <div id="menu">
+        <ul>
+            <li><a href="__PROJECT_url_website">__PROJECT_name Home</a></li>
+__MENU__
+            <!-- <li class="active">Documentation</li> -->
+        </ul>
+    </div>
+</div>
+<div id="content">
diff --git a/doc/html/project.css b/doc/html/project.css
new file mode 100644
index 0000000..2745b92
--- /dev/null
+++ b/doc/html/project.css
@@ -0,0 +1,198 @@
+* {
+    margin: 0px;
+    padding: 0px;
+}
+
+body,table {
+    font-family: Arial, Helvetica, sans-serif;
+    font-size: 13px;
+}
+
+body {
+    background-color: #3C464D;
+    min-width: 800px;
+    max-width:  1360px;
+    padding-top: 0.5em;
+    padding-bottom: 1em;
+}
+
+a {
+    color: #5382A1;
+    text-decoration: none;
+    font-weight: bold;
+}
+
+a:hover {
+    color: #9E005D;
+}
+
+h1 {
+    text-align: center;
+    font-size: 140%;
+}
+
+h2 {
+    font-size: 120%;
+}
+
+h3 {
+    font-size: 100%;
+}
+
+img {
+    border: none;
+    vertical-align: middle;
+}
+
+/* ----------------------------------*/
+
+dd,ul {
+    padding-left: 40px;
+}
+
+h1,h2,h3 {
+    margin-top: 1.5em;
+    margin-bottom: 1.0em;
+}
+
+h2 {
+    border-bottom: 2px solid;
+}
+
+p,ul {
+    margin-top: 1em;
+}
+
+dd,ol {
+    padding-left: 40px;
+}
+
+ul ul {
+    margin-top: 0;
+    margin-bottom: 0;
+}
+
+/* ----------------------------------*/
+
+blockquote, dl, pre {
+    margin: 1em 4em;
+}
+
+pre {
+    background-color: #CCCCCC;
+    padding: 0.5em;
+    border: 1px solid #888888;
+}
+
+dl span.samp, dl span.command {
+    font-weight: bold;
+}
+
+/* ----------------------------------*/
+
+div#page {
+    margin-left: 40px;
+    margin-right: 40px;
+    position: relative;
+    background-color: #EDEFF2;
+    -webkit-border-radius: 1em;
+    -webkit-box-shadow: 5px 5px 5px black;
+    -moz-border-radius: 1em;
+}
+
+div#sitebanner, div#content, div#footer div {
+    padding-left: 16px;
+    padding-right: 16px;
+}
+
+div#sitebanner {
+    position: relative;
+    background-color: gray;
+    background-image: url(banner.png);
+    background-repeat: no-repeat;
+    background-position: top left;
+    min-height: 128px;
+    max-height: 128px;
+    text-shadow: black 1px 1px 0px;
+    -webkit-border-top-left-radius: 1em;
+    -webkit-border-top-right-radius: 1em;
+    -moz-border-radius-topleft: 1em;
+    -moz-border-radius-topright: 1em;
+}
+
+/* enable to make banner logo clickable */
+/*
+div#sitebanner div#home a {
+    display: block;
+    background: none;
+    position: absolute;
+    top: 8px;
+    left: 8px;
+    height: 80px;
+    width: 220px;
+}
+*/
+
+div#sitebanner div#title {
+    display: none;
+    position: absolute;
+    top: 40%;
+    left: 50%;
+    text-align: lef;
+    color: white;
+    color: #C9F08D;
+    font-weight: bold;
+    font-size: 150%;
+}
+
+div#sitebanner div#menu {
+    position: absolute;
+    top: 16px;
+    right: 16px;
+    color: white;
+    text-align: right;
+}
+
+div#sitebanner div#menu li {
+    list-style-type: none;
+}
+
+div#sitebanner div#menu li a {
+    color: white;
+    font-weight: normal;
+}
+
+div#sitebanner div#menu li.active,
+div#sitebanner div#menu li a:hover {
+    color: #C9F08D;
+}
+
+div#contents {
+    padding-top: 8px;
+    padding-bottom: 8px;
+}
+
+div#footer {
+    padding-top: 2em;
+    padding-bottom: 1em;
+    -webkit-border-bottom-left-radius: 1em;
+    -webkit-border-bottom-right-radius: 1em;
+    -moz-border-radius-bottomleft: 1em;
+    -moz-border-radius-bottomright: 1em;
+}
+
+div#footer div {
+    text-align: right;
+    font-size: x-small;
+    border-top: 2px solid #5382A1;
+}
+
+/* ----------------------------------*/
+
+div.contents ul {
+    list-style-type: none;
+    padding-left: 2em;
+}
+
+div.contents ul {
+}
diff --git a/doc/texi/Authors.texi b/doc/texi/Authors.texi
new file mode 100644
index 0000000..9ec4920
--- /dev/null
+++ b/doc/texi/Authors.texi
@@ -0,0 +1,27 @@
+@input texinfo @c -*- Texinfo -*-
+@c %**start of header
+@setfilename Authors.info
+@include base/article.texi
+@paragraphindent none
+@c %**end of header
+
+@majorheading @value{PROJECT.name.formal} Authors
+
+This document lists significant contributors to @value{PROJECT.name}. If any contributors are missing please contact one of the project members. More extensive contributor information may be found in sources, docs, and other files where appropriate.
+
+@example
+Edward Groenendaal          egroenen@@cisco.com
+Jonathon Rubin              jb.rubin@@gmail.com
+Kona Blend                  kona8lend@@gmail.com
+Rouven Wessling             mp4v2@@rouvenwessling.de
+@end example
+
+@heading Authors from original MPEG4IP/libmp4v2
+
+@example
+Dave Mackie                 dmackie@@cisco.com
+Alix Marchandise-Franquet   alix@@cisco.com
+Bill May                    wmay@@cisco.com
+Ximpo Group Ltd.            mp4v2@@ximpo.com
+Danijel Kopcinovic          danijel.kopcinovic@@adnecto.net
+@end example
diff --git a/doc/texi/BuildRepository.texi b/doc/texi/BuildRepository.texi
new file mode 100644
index 0000000..dcae56e
--- /dev/null
+++ b/doc/texi/BuildRepository.texi
@@ -0,0 +1,42 @@
+@input texinfo @c -*- Texinfo -*-
+@c %**start of header
+@setfilename BuildRepository.info
+@include base/article.texi
+@include build/commands.texi
+@paragraphindent none
+@c %**end of header
+
+@majorheading @value{PROJECT.name.formal} Building the Repository
+@contents
+
+@chapter Overview
+@include build/overview.texi
+
+@chapter Introduction
+This document describes the recommended process to build @value{PROJECT.name} from the repository. This process is a @b{superset} of the process to build from a @value{TERM.srcdist} bundle.  If you are interested in building from a @value{TERM.srcdist} bundle then this document is not for you.
+
+@chapter Quickstart
+This chapter is for the impatient or those just looking for a quick summary of all the commands used in a typical build. You may skip this summary and jump to @ref{Build Process}.
+
+@example
+@COMMAND.checkout{}
+@COMMAND.bootstrap{}
+@COMMAND.configure{}
+@COMMAND.build{}
+@COMMAND.install{}
+@COMMAND.dist{}
+@end example
+
+@anchor{Build Process}
+@chapter Build Process
+
+@include build/process.checkout.texi
+@include build/process.bootstrap.texi
+@include build/process.configure.texi
+@include build/process.build.texi
+@include build/process.install.texi
+@include build/process.dist.texi
+@include build/process.doc.texi
+@include build/process.google.texi
+
+@include build/platforms.texi
diff --git a/doc/texi/BuildSource.texi b/doc/texi/BuildSource.texi
new file mode 100644
index 0000000..233ecdb
--- /dev/null
+++ b/doc/texi/BuildSource.texi
@@ -0,0 +1,36 @@
+@input texinfo @c -*- Texinfo -*-
+@c %**start of header
+@setfilename BuildSource.info
+@include base/article.texi
+@include build/commands.texi
+@paragraphindent none
+@c %**end of header
+
+@majorheading @value{PROJECT.name.formal} Building the Source
+@contents
+
+@chapter Overview
+@include build/overview.texi
+
+@chapter Introduction
+This document describes the recommended process to build @value{PROJECT.name} from a @value{TERM.srcdist} bundle. This process is a @b{subset} of the process to build directly from the project's repository. If you are interested in building from the repository then this document is not for you.
+
+@chapter Quickstart
+This chapter is for the impatient or those just looking for a quick summary of all the commands used in a typical build. You may skip this summary and jump to @ref{Build Process}.
+
+@example
+@COMMAND.extract{}
+@COMMAND.configure{}
+@COMMAND.build{}
+@COMMAND.install{}
+@end example
+
+@anchor{Build Process}
+@chapter Build Process
+
+@include build/process.extract.texi
+@include build/process.configure.texi
+@include build/process.build.texi
+@include build/process.install.texi
+
+@include build/platforms.texi
diff --git a/doc/texi/Documentation.texi b/doc/texi/Documentation.texi
new file mode 100644
index 0000000..7b85fa1
--- /dev/null
+++ b/doc/texi/Documentation.texi
@@ -0,0 +1,47 @@
+@input texinfo @c -*- Texinfo -*-
+@c %**start of header
+@setfilename Documentation.info
+@include base/article.texi
+@paragraphindent none
+@c %**end of header
+
+@titlepage
+@end titlepage
+
+@majorheading @value{PROJECT.name.formal} Documentation
+@include releasenotes/intro.@value{PROJECT.repo.type}.texi
+
+@heading Introduction
+Project @value{PROJECT.name} consists of a C-library and various command-line utilities which permit creation and modification of MP4 files as defined by as defined by @cite{ISO base media file format}, document number @cite{ISO/IEC 14496-12:2005(E)}.
+
+@heading Official website
+The official @url{@value{PROJECT.url.website},website} makes various @url{@value{PROJECT.url.downloads},downloads} available and archives online browesable documentation for releases, including API Reference. Questions, bug-reports and general feedback may be submitted to the @url{@value{PROJECT.url.discussion},discussion} group.
+
+@ifnothtml
+@unnumbered Articles
+   See Release Notes (@file{doc/ReleaseNotes.txt})
+@* See Building the Source (@file{doc/BuildSource.txt})
+@* See Building the Repository (@file{doc/BuildRepository.txt})
+@* See Authors (@file{doc/Authors.txt})
+
+@unnumbered Guides
+   See Command-line Tools Guide (@file{doc/ToolGuide.txt})
+@end ifnothtml
+
+@ifhtml
+@unnumbered Articles
+@enumerate
+@item @url{ReleaseNotes.html,Release Notes}.
+@item @url{BuildSource.html,Building the Source}.
+@item @url{BuildRepository.html,Building the Repository}.
+@item @url{Authors.html,Authors}.
+@end enumerate
+@unnumbered Guides
+@enumerate
+@item @url{ToolGuide.html,Command-line Tools Guide}.
+@end enumerate
+@unnumbered Reference
+@enumerate
+@item @url{api/index.html,API Reference}.
+@end enumerate
+@end ifhtml
diff --git a/doc/texi/ReleaseNotes.texi b/doc/texi/ReleaseNotes.texi
new file mode 100644
index 0000000..2e3d189
--- /dev/null
+++ b/doc/texi/ReleaseNotes.texi
@@ -0,0 +1,63 @@
+@input texinfo @c -*- Texinfo -*-
+@c %**start of header
+@setfilename ReleaseNotes.info
+@include base/article.texi
+@paragraphindent none
+@c %**end of header
+
+@titlepage
+@end titlepage
+
+@majorheading @value{PROJECT.name.formal} Release Notes
+@include releasenotes/intro.@value{PROJECT.repo.type}.texi
+@contents
+
+@chapter What's New?
+[TODO]
+
+@chapter Breaking Changes
+@section Removed deprecated metadata/free-form API.
+The following functions have been removed and replaced with iTMF Generic and Tags API. Detailed documentation can be found under API modules section.
+
+DeleteMetadataAtom(),
+DeleteMetadataFreeForm(),
+DeleteMetadataGenre(),
+GetMetadataByIndex(),
+GetMetadataCoverArt(),
+GetMetadataCoverArtCount(),
+GetMetadataDisk(),
+GetMetadataFreeForm(),
+GetMetadataGenre(),
+GetMetadataString(),
+GetMetadataTrack(),
+GetMetadataUint16(),
+GetMetadataUint8(),
+MetadataDelete(),
+SetMetadataCoverArt(),
+SetMetadataDisk(),
+SetMetadataFreeForm(),
+SetMetadataGenre(),
+SetMetadataString(),
+SetMetadataTrack(),
+SetMetadataUint16(),
+SetMetadataUint8().
+
+@appendix Changelog
+@appendixsection Version 2.0
+@include changelog/version-2.0.texi
+
+@appendixsection Version 1.9
+@include changelog/version-1.9.texi
+
+@appendixsection Version 1.6
+@include changelog/version-1.6.texi
+
+@appendix SCM repository details
+@example
+url:  @value{PROJECT.repo.url}
+root: @value{PROJECT.repo.root}
+uuid: @value{PROJECT.repo.uuid}
+rev:  @value{PROJECT.repo.rev}
+date: @value{PROJECT.repo.date}
+type: @value{PROJECT.repo.type}
+@end example
diff --git a/doc/texi/ToolGuide.texi b/doc/texi/ToolGuide.texi
new file mode 100644
index 0000000..a951f26
--- /dev/null
+++ b/doc/texi/ToolGuide.texi
@@ -0,0 +1,95 @@
+@input texinfo @c -*- Texinfo -*-
+@c %**start of header
+@setfilename ToolGuide.info
+@include base/guide.texi
+@paragraphindent none
+@c %**end of header
+
+@majorheading @value{PROJECT.name.formal} Command-line Tools Guide
+@contents
+
+@chapter Overview
+@value{PROJECT.name} bundles several command-line tools which, in general, allow some basic manipulation of mp4 files which have been created by other means. They are not meant to be a complete solution to management of mp4 file structure.
+
+The following is a brief summary of the tools available and the functionality offered. Other tools may be packaged with the distribution but are not yet stable enough to even document. User beware.
+
+@table @samp
+@item mp4file
+Operates on the entire file with actions such as list (summary information), optimization and ASCII dumps.
+
+@item mp4track
+Operates on individual tracks with actions such as colr-box and pasp-box manipulation.
+
+@item mp4art
+Operates on iTunes Metadata Cover-art Boxes with actions such as list, add, replace, remove and extraction of Cover-art images.
+@end table
+
+@chapter Introduction
+The tools are invoked by their command-name, followed by one or more options, actions, parameters for actions, and finally one or more files on which the tool will operate. Options are specified in one of two ways; in @b{short} or @b{long} syntax. A short-syntax option is prefixed with exactly one @i{dash} while a long-syntax option is prefixed with exactly two @i{dashes}. Depending on the option, it may or may not expect an argument. Specifying an option which expects an argument usually follows either of the following patterns:
+
+@example
+toolname --something value ...
+toolname --something=value ...
+@end example
+
+The rest of this guide will use the @i{equals} sign method.
+
+@chapter Common Options
+Many of the tools share a common set of options which. These common options usually have identically behaving short or long syntax. In some cases short-syntax differs from long-syntax in that it may not require an argument. This style is used sparingly and only when truly convenient. Even though it is common practice in many unix-style tools to permit @i{optional} arguments, the tools used in this project will tend to avoid that because it can create a great deal of confusion.
+
+The following is a list of common options available:
+
+@table @samp
+@item -y, --dryrun
+do not actually create or modify any files.
+In situations where the command will create new or modify existing files, specifying this option will cause the tool to do as much as possible stopping short of performing any actual writes. This is useful to guard against user mistakes or unexpected behavior.
+
+@item -k, --keepgoing
+continue batch processing even after errors.
+When actions involve multiple files or operations, the default behavior is to stop and exit on the first error encountered. Specify this option if it is desirable to record the error but continue processing.
+
+@item -o, --overwrite
+overwrite existing files when creating.
+In situations where a new file will be created, the default behavior is to not overwrite a file if it already exists. Use this option to allow overwriting.
+
+@item -f, --force
+force overwrite even if file is read-only.
+If overwriting is enabled, file permissions may prevent writes. Specify this option to try and overwrite the file anyways. This usually involves deleting the file, then creating a new one.
+
+@item -q, --quiet
+equivalent to --verbose 0.
+Default behavior is to print a low amount of informative information, usually one line of text per action/file. Specify this option to omit normal messages. Errors will still be reported.
+
+@item -d, --debug NUM
+increase debug or long-option to set NUM.
+File I/O with mp4 file structures have special debug options available to users interested in all the fine details. Default is level 1 . The short-syntax is accumulative and takes no argument, while long-syntax takes an argument. For exmaple, the following are equivalent and would set level 3: @samp{-dd} or @samp{-d -d} or @samp{--debug=3}. The following levels are available:
+@enumerate 0
+@item  supressed
+@item  add warnings and errors (default)
+@item  add table details
+@item  add implicits
+@item  everything
+@end enumerate
+
+@item -v, --verbose NUM
+increase verbosity or long-option to set NUM.
+Tool activity by default will generally print one informative message per action/file. Specify this option to change the default behavior. The short-syntax is accumulative and takes no argument, while long-syntax takes an argument.
+@enumerate 0
+@item  warnings and errors
+@item  normal informative messages (default)
+@item  more informative messages
+@item  everything
+@end enumerate
+
+@item -h, --help
+print brief help or long-option for extended help.
+The short-syntax will produce brief help. Specify the long-option for more extensive help.
+
+@item --version
+print version information and exit.
+Extended version information used for SCM purposes is not listed in help, but is available by specifying @samp{--verionx}.
+@end table
+
+@include tool/mp4file.texi
+@include tool/mp4track.texi
+@include tool/mp4art.texi
diff --git a/doc/texi/base/article.texi b/doc/texi/base/article.texi
new file mode 100644
index 0000000..a5fdf76
--- /dev/null
+++ b/doc/texi/base/article.texi
@@ -0,0 +1,6 @@
+@ifhtml
+@exampleindent 0
+@end ifhtml
+
+@include base/project.texi
+@include base/glossary.texi
diff --git a/doc/texi/base/glossary.texi b/doc/texi/base/glossary.texi
new file mode 100644
index 0000000..c46a4f3
--- /dev/null
+++ b/doc/texi/base/glossary.texi
@@ -0,0 +1,9 @@
+@set OS.cygwin   Cygwin
+@set OS.freebsd  FreeBSD
+@set OS.linux    Linux
+@set OS.osx      Mac OS X
+@set OS.solaris  Solaris
+@set OS.unix     unix
+@set OS.windows  Windows
+
+@set TERM.srcdist  source-distribution
diff --git a/doc/texi/base/guide.texi b/doc/texi/base/guide.texi
new file mode 100644
index 0000000..a5fdf76
--- /dev/null
+++ b/doc/texi/base/guide.texi
@@ -0,0 +1,6 @@
+@ifhtml
+@exampleindent 0
+@end ifhtml
+
+@include base/project.texi
+@include base/glossary.texi
diff --git a/doc/texi/base/project.texi.m4 b/doc/texi/base/project.texi.m4
new file mode 100644
index 0000000..5a37bce
--- /dev/null
+++ b/doc/texi/base/project.texi.m4
@@ -0,0 +1,24 @@
+changequote(<<,>>)dnl
+include(<<project.m4>>)dnl
+@set PROJECT.name            __PROJECT_name
+@set PROJECT.name.lower      __PROJECT_name_lower
+@set PROJECT.name.upper      __PROJECT_name_upper
+@set PROJECT.name.formal     __PROJECT_name_formal
+@set PROJECT.url.website     __PROJECT_url_website
+@set PROJECT.url.downloads   __PROJECT_url_downloads
+@set PROJECT.url.discussion  __PROJECT_url_discussion
+@set PROJECT.irc             __PROJECT_irc
+@set PROJECT.bugreport       patsubst(__PROJECT_bugreport,@,@@)
+@set PROJECT.version         __PROJECT_version
+@set PROJECT.version.hex     __PROJECT_version_hex
+@set PROJECT.version.major   __PROJECT_version_major
+@set PROJECT.version.minor   __PROJECT_version_minor
+@set PROJECT.version.point   __PROJECT_version_point
+@set PROJECT.repo.url        __PROJECT_repo_url
+@set PROJECT.repo.branch     __PROJECT_repo_branch
+@set PROJECT.repo.root       __PROJECT_repo_root
+@set PROJECT.repo.uuid       __PROJECT_repo_uuid
+@set PROJECT.repo.rev        __PROJECT_repo_rev
+@set PROJECT.repo.date       __PROJECT_repo_date
+@set PROJECT.repo.type       __PROJECT_repo_type
+@set PROJECT.build           __PROJECT_build
diff --git a/doc/texi/build/commands.texi b/doc/texi/build/commands.texi
new file mode 100644
index 0000000..8ef9ec9
--- /dev/null
+++ b/doc/texi/build/commands.texi
@@ -0,0 +1,33 @@
+@macro COMMAND.checkout{}
+svn checkout @value{PROJECT.repo.url} @value{PROJECT.name.lower}
+cd @value{PROJECT.name.lower}
+@end macro
+
+@macro COMMAND.bootstrap{}
+autoreconf -fiv
+@end macro
+
+@macro COMMAND.extract{}
+tar xf @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.bz2
+cd @value{PROJECT.name.lower}-@value{PROJECT.version}/
+@end macro
+
+@macro COMMAND.configure{}
+rm -fr build/
+mkdir build/
+cd build/
+../configure 
+@end macro
+
+@macro COMMAND.build{}
+make
+@end macro
+
+@macro COMMAND.install{}
+make install
+make install-man
+@end macro
+
+@macro COMMAND.dist{}
+make dist
+@end macro
diff --git a/doc/texi/build/overview.texi b/doc/texi/build/overview.texi
new file mode 100644
index 0000000..e33c35b
--- /dev/null
+++ b/doc/texi/build/overview.texi
@@ -0,0 +1,3 @@
+The documented and supported method to build @value{PROJECT.name} uses the GNU build system (also known as the Autotools). You must first obtain the sources by either downloading and extracting the @value{TERM.srcdist} bundle or working directly @value{PROJECT.name}'s Subversion repository. We have build documents for both methods, but unless you are a member of the @value{PROJECT.name} project, you are @b{strongly encouraged} to use the @value{TERM.srcdist} method.
+
+On other supported platforms which lack Autotools we provide an alternative method for building the software. Please see the appropriate platform section.
diff --git a/doc/texi/build/platform.common.bundled.texi b/doc/texi/build/platform.common.bundled.texi
new file mode 100644
index 0000000..d5c00ee
--- /dev/null
+++ b/doc/texi/build/platform.common.bundled.texi
@@ -0,0 +1,3 @@
+@quotation Note
+It is recommended to use the platform distribution's bundled compiler for maximum C++ compatibility. If you build with a custom compiler it will likely introduce non-standard runtime requirements for your users. There are of course many valid reasons to build with unbundled compilers, but be aware that is generally unsupported and left as an exercise to the reader.
+@end quotation
diff --git a/doc/texi/build/platform.common.conf.bi.texi b/doc/texi/build/platform.common.conf.bi.texi
new file mode 100644
index 0000000..bb5e0eb
--- /dev/null
+++ b/doc/texi/build/platform.common.conf.bi.texi
@@ -0,0 +1,10 @@
+@subsection Bi-arch compilation
+The preferred method to produce a bi-arch binary is to specify the target (eg. 32-bit) with the following option to configure. This example will produce a 32-bit binary if compiling on a platform which defaults to producing 64-bit binaries. The inverse is also possible.
+
+@example
+../configure --enable-bi=32
+@end example
+
+@quotation Warning
+Currently bi-arch cross-compilation is not supported due to a bug with libtool which fails miserably during linking.
+@end quotation
diff --git a/doc/texi/build/platform.common.conf.default.texi b/doc/texi/build/platform.common.conf.default.texi
new file mode 100644
index 0000000..e127c60
--- /dev/null
+++ b/doc/texi/build/platform.common.conf.default.texi
@@ -0,0 +1,20 @@
+@subsection Default Binaries
+The preferred method to produce default binaries is to run configure without any options which will compile with debug+optimize and produce static+shared libraries and dynamic executables. 
+
+@example
+../configure
+@end example
+
+@subsection Release Binaries
+The preferred method to produce binaries suitable for releases, (ie. which does not contain debug information) is to pass the following to configure:
+
+@example
+../configure --disable-debug
+@end example
+
+@subsection Developer Binaries
+The preferred method to produce binaries suitable for development is to pass the following to configure.  Default Binaries will work, however for the best debugging experience we recommend no optimize and no static libraries.
+
+@example
+../configure --disable-optimize --disable-static
+@end example
diff --git a/doc/texi/build/platform.cygwin.texi b/doc/texi/build/platform.cygwin.texi
new file mode 100644
index 0000000..90574c7
--- /dev/null
+++ b/doc/texi/build/platform.cygwin.texi
@@ -0,0 +1,20 @@
+@section @value{OS.cygwin}
+Building on @value{OS.cygwin} is supported. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible:
+
+@itemize @bullet
+@item Intel 32-bit or 64-bit hardware
+@item @value{OS.cygwin}, gcc 4.3.2
+@item gcc 3.4.0 or higher is reported to work
+@end itemize
+
+@quotation Note
+As of this writing, @value{OS.cygwin} has available to it several versions of gcc; only one of which may be found and used in the path as @code{gcc} and @code{g++}. Configure will thus find what is probably the older more stable version of gcc in a typical @value{OS.cygwin} environment. If you desire to build with the newer gcc, it is found in the path as @code{gcc-4} and @code{g++-4} respectively and you must indicate to configure the desired versions. Defining the following variables beforing running configure should do the trick:
+@end quotation
+
+@example
+setenv CC gcc-4
+setenv CXX gcc-4
+../configure
+@end example
+
+@include build/platform.common.conf.default.texi
diff --git a/doc/texi/build/platform.freebsd.texi b/doc/texi/build/platform.freebsd.texi
new file mode 100644
index 0000000..a707ff6
--- /dev/null
+++ b/doc/texi/build/platform.freebsd.texi
@@ -0,0 +1,12 @@
+@section @value{OS.freebsd}
+Building on @value{OS.freebsd} is supported. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible:
+
+@itemize @bullet
+@item Intel 32-bit or 64-bit hardware
+@item @value{OS.freebsd} 7.0 Release, gcc 4.2.1
+@item gcc 3.4.0 or higher is reported to work
+@end itemize
+
+@include build/platform.common.bundled.texi
+@include build/platform.common.conf.default.texi
+@include build/platform.common.conf.bi.texi
diff --git a/doc/texi/build/platform.linux.texi b/doc/texi/build/platform.linux.texi
new file mode 100644
index 0000000..ddc04df
--- /dev/null
+++ b/doc/texi/build/platform.linux.texi
@@ -0,0 +1,12 @@
+@section @value{OS.linux}
+Building on @value{OS.linux} is well supported as it is used by maintainers of this project. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible:
+
+@itemize @bullet
+@item Intel 32-bit or 64-bit hardware
+@item Fedora 10, gcc 4.3.2
+@item gcc 3.4.0 or higher is reported to work
+@end itemize
+
+@include build/platform.common.bundled.texi
+@include build/platform.common.conf.default.texi
+@include build/platform.common.conf.bi.texi
diff --git a/doc/texi/build/platform.osx.texi b/doc/texi/build/platform.osx.texi
new file mode 100644
index 0000000..ac2bc05
--- /dev/null
+++ b/doc/texi/build/platform.osx.texi
@@ -0,0 +1,28 @@
+@section @value{OS.osx}
+Building on @value{OS.osx} is well supported as it is used by maintainers of this project. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible:
+
+@itemize @bullet
+@item Mac Intel hardware
+@item Mac OS X 10.5.7
+@item Xcode-3.1.2
+@item gcc 4.0.1 (Apple Inc. build 5493)
+@item gcc 4.2.1 (Apple Inc. build 5570)
+@end itemize
+
+@include build/platform.common.bundled.texi
+
+@include build/platform.common.conf.default.texi
+
+@subsection Universal Binaries - all architectures
+The preferred method to produce universal binaries for all supported architectures is to pass the following option to configure. As of this writing, architectures @{ i386, x86_64, ppc, ppc64 @} are built.
+
+@example
+../configure --enable-ub
+@end example
+
+@subsection Universal Binaries - selected architectures
+The preferred method to produce universal binaries for selected architectures is to specify a comma-delimited list specifying the desired architecture identifiers. Passing the following option will produce universal binaries for architectures @{ i386, x86_64 @}.
+
+@example
+../configure --enable-ub=i386,x86_64
+@end example
diff --git a/doc/texi/build/platform.solaris.texi b/doc/texi/build/platform.solaris.texi
new file mode 100644
index 0000000..87e9959
--- /dev/null
+++ b/doc/texi/build/platform.solaris.texi
@@ -0,0 +1,16 @@
+@section @value{OS.solaris}
+Building on @value{OS.solaris} is supported. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible:
+
+@itemize @bullet
+@item Intel 32-bit or 64-bit hardware
+@item @value{OS.solaris} 10u6, gcc 3.4.3
+@item gcc 3.4.0 or higher is reported to work
+@end itemize
+
+@include build/platform.common.bundled.texi
+@quotation Note
+@value{OS.solaris} does not (yet) really bundle a compiler. The recommendation is to use the companion-disk compiler for maximum C++ runtime compatibility. It is usually found in @file{/usr/sfw/bin}.
+@end quotation
+
+@include build/platform.common.conf.default.texi
+@include build/platform.common.conf.bi.texi
diff --git a/doc/texi/build/platform.windows.texi b/doc/texi/build/platform.windows.texi
new file mode 100644
index 0000000..ace30b9
--- /dev/null
+++ b/doc/texi/build/platform.windows.texi
@@ -0,0 +1,18 @@
+@section @value{OS.windows}
+Native builds on @value{OS.windows} is supported via Microsoft's Visual Studio package. Both the commercial version and free version (express) are known to work. The following are the recommended specifications for this platform; but is not necessarily the only configuration that is possible:
+
+@itemize @bullet
+@item Intel 32-bit or 64-bit hardware
+@item @value{OS.windows} 2000 or higher, Visual Studio 9.0 (aka. Visual Studio 2008)
+@item Visual Studio 9.0 Express is reported to work
+@end itemize
+
+Only 32-bit binaries are targeted, and win32-API is set to @value{OS.windows} 2000 or higher. Older versions of @value{OS.windows}, or win32-API are not supported.
+
+@value{PROJECT.name} has directory @file{vstudio9.0/} which contains the necessary solution+project files to produce a basic build of libmp4v2's DLL and several command-line executables. Enabling things such as debugging, optimization, etc, are all left as an exercise to the user.
+
+@quotation Warning
+Project meta-data is stored in header @file{project.h}. A proper source distribution is built using autotools and generates @file{TOP/include/mp4v2/project.h} correctly, which is then bundled with our source distribution. This is adequate for building on the @value{OS.windows} platform.
+
+However, if you are building from the repository, be warned that there is no method to automatically generate @file{project.h} on Windows. Instead, we periodically checkin a copy of this file (generated using autotools) as @file{vstudio9.0/include/mp4v2/project.h} which may from time to time get out of date. If it is significantly out of date, you should find the latest source distribution and copy the @file{project.h} from there.
+@end quotation
diff --git a/doc/texi/build/platforms.texi b/doc/texi/build/platforms.texi
new file mode 100644
index 0000000..ae6f8f2
--- /dev/null
+++ b/doc/texi/build/platforms.texi
@@ -0,0 +1,11 @@
+@chapter Platform Notes
+@value{PROJECT.name} builds on many unix-style platforms, also commonly referred to as posix-style systems. Building on @value{OS.osx}, @value{OS.linux}, @value{OS.freebsd}, @value{OS.solaris}, @value{OS.cygwin}, @value{OS.windows} are known to work.
+
+Similar platforms should also work. Please see the following platform specific notes for instructions on commonly used options for various platforms.
+
+@include build/platform.osx.texi
+@include build/platform.linux.texi
+@include build/platform.freebsd.texi
+@include build/platform.solaris.texi
+@include build/platform.cygwin.texi
+@include build/platform.windows.texi
diff --git a/doc/texi/build/process.bootstrap.texi b/doc/texi/build/process.bootstrap.texi
new file mode 100644
index 0000000..f8153e9
--- /dev/null
+++ b/doc/texi/build/process.bootstrap.texi
@@ -0,0 +1,15 @@
+@section Boostrap (Autotools)
+
+The following command causes forces Autotools to regenerate all files and install helper scripts needed at configure-time and to regenerate all files.
+
+@example
+@COMMAND.bootstrap{}
+@end example
+
+If you are a project member and preparing for a release, it is important to note that the versions of Autotools available in your path will directly effect files added to the bundle. At the time of writing, the following versions of Autotools are recommended; in some cases a minimum is hard-coded and warnings will be issued if in violation:
+
+@itemize
+@item GNU autoconf 2.61 or higher (lower versions might work)
+@item GNU automake 1.10 or higher (lower versions might work)
+@item GNU libtool 1.5.26 or higher (lower versions might work)
+@end itemize
diff --git a/doc/texi/build/process.build.texi b/doc/texi/build/process.build.texi
new file mode 100644
index 0000000..5e9d7a8
--- /dev/null
+++ b/doc/texi/build/process.build.texi
@@ -0,0 +1,9 @@
+@section Build
+
+The following command will build @value{PROJECT.name}.
+
+@example
+@COMMAND.build{}
+@end example
+
+On some platforms @code{make} refers to a BSD-flavor of make which is not compatible with this project. Check if @code{gmake} is installed, and if it is, substitute @code{gmake} wherever you may see @code{make} in this document. Otherwise you will need to install GNU make package version 3.81 or higher. Lower versions might work.
diff --git a/doc/texi/build/process.checkout.texi b/doc/texi/build/process.checkout.texi
new file mode 100644
index 0000000..5bf371a
--- /dev/null
+++ b/doc/texi/build/process.checkout.texi
@@ -0,0 +1,14 @@
+@section Checkout Sources
+
+Checkout sources from the project's Subversion repository.
+
+Sources are checked out from either the trunk, release or a branch. This document was generated from one of those, and for example purposes, we will use exactly the same URL which used to create the distribution which contains this document.
+
+If you are a project member, then you may add the appropriate login/password information as needed.
+
+@example
+@COMMAND.checkout{}
+@end example
+
+It is recommended to use Subversion 1.5.0 or higher.
+Lower versions might work.
diff --git a/doc/texi/build/process.configure.texi b/doc/texi/build/process.configure.texi
new file mode 100644
index 0000000..19f427d
--- /dev/null
+++ b/doc/texi/build/process.configure.texi
@@ -0,0 +1,40 @@
+@anchor{Configure}
+@section Configure
+The following command configures the project for a build.  It is highly recommended that you invoke configure from an empty directory.
+
+@example
+@COMMAND.configure{}
+@end example
+
+Please see @file{INSTALL} for details on configure usage, and standard options. Additionally, the following custom options have been added to @file{configure}:
+
+@table @samp
+
+@item --disable-debug
+Do not generate debug information.  Do not direct compiler to generate debugging information. By default the compiler will generate debug information if the platform supports it.
+
+@item --disable-optimize
+Do not optimize. Do not direct compiler to optimize code. By default compiler optimization is enabled if the platform supports it.
+
+@item --disable-fvisibility
+Do not set default ELF symbol visibility. By default configure attempts to detect if the compiler supports this feature. However on some platforms detecting incompatibilty of this feature might not be accurate in which case this option should be given.
+
+@item --disable-gch
+By default certain platforms are marked to use GCC precompiled headers. Generally this greatly decrease build times but may require more diligence for iterative development; that is to say dependencies may not properly be tracked and more frequent @command{make clean} may be required when headers are changed. Use this option to disable GCC precompiled headers.
+
+@item --disable-largefile
+On some 32-bit platforms or configurations it might be desirable to build without largefile (LFS) support. By default configure attempts to detect formal LFS support and enables it if found.
+
+@item --disable-util
+Do not build/install utilities. This is convenience option for users who desire to skip building the utilities (eg. command-line executables) which are enabled by default.
+
+@item --enable-bi=ARCH
+On bi-arch capable platforms it is possible to generate 32 or 64 bit code. This is supported by adding arguments @samp{-m32} or @samp{-m64}, respectively, when compiling or linking. Use this option to override the platform-specific default.
+
+@item --enable-ub[=ARCHS]
+On OSX systems it is possible to generate universal binaries. This is supported by adding one or more argument patterns @samp{-arch ARCH} when compiling or linking. Use this option to either target an architecture different from the platform default, or to produce universal binaries.
+
+@item --enable-dependency-tracking
+Enable automatic dependency tracking for include-files. By default this feature is disabled.
+
+@end table
diff --git a/doc/texi/build/process.dist.texi b/doc/texi/build/process.dist.texi
new file mode 100644
index 0000000..2d8b10b
--- /dev/null
+++ b/doc/texi/build/process.dist.texi
@@ -0,0 +1,17 @@
+@section Create Distribution
+
+The following command will create a @value{PROJECT.name} source distribution. It is during this step that shipped documentation is generated.
+
+@example
+@COMMAND.dist{}
+@end example
+
+This step in the build process introduces additional requirements to the host system. While most of the following utilities are generally available, @command{help2man} is used to generate man-pages; however if this command is not available the man-pages will be empty. This is acceptable for non-release builds but for full quality builds this command is required.
+
+@itemize
+@item GNU help2man 1.36 or higher (lower versions should work)
+@item GNU tar 1.15.1 or higher (lower versions should work)
+@item GNU gzip 1.3.10 or higher (lower versions should work)
+@item bzip2 1.0.4 or higher (lower versions should work)
+@item Info-ZIP zip 2.32 or higher (lower versions should work)
+@end itemize
diff --git a/doc/texi/build/process.doc.texi b/doc/texi/build/process.doc.texi
new file mode 100644
index 0000000..be17bc3
--- /dev/null
+++ b/doc/texi/build/process.doc.texi
@@ -0,0 +1,64 @@
+@section Build Documentation
+
+This step in the build process introduces some significant requirements to the host system:
+
+@itemize
+@item GNU texinfo 4.8 or higher (lower versions should work)
+@item Doxygen 1.5.7 or higher (lower versions should work)
+@end itemize
+
+Documentation that is shipped with @value{TERM.srcdist} is generated as part of the @ref{Build Process} step. This section documents builds all of the supported methods to build documentation. There are three kinds of documentation in this project; man-pages, articles and API. @b{Documentation must be build from the repository}.
+
+Man-pages are automatically generated for command-line utilities by using @command{html2man} which invokes standard options @command{--help} and @command{--version} and gleans the information, generating a man-page in @b{nroff} script. Note that the utilities will first be built as they are depdendencies of man-page generation.
+
+Artcles are usually hand-written and authored in Texinfo format which permits macro-expansions, simple formatting and conversion to several popular formats using the GNU @command{makeinfo}. A Texinfo @url{http://www.gnu.org/software/texinfo,manual} is available.
+
+API is documented inline to C and C++ source files, usually headers using Doxygen comment-formatting. Doxygen is then used to post-process these files and generate documentation in various formats. A Doxygen manual is available at it's main @url{http://www.doxygen.org,site} .
+
+The project's goal is to document as thoroughly as possible the public API in @value{PROJECT.name}. Since we never have enough time to document everything, we try to set some priorities in this regard. Generally the public API is the highest priority to document. Next most important is probably underlying utility code which is shared by many developers; for example @b{libplatform}.
+
+If you need examples of how to document C-compatible source see @file{include/mp4v2/mp4v2.h} and for C++-only source
+see @file{libplatform/io/File.h} .
+
+The following table describes the various make targets available for building docs. Note you must first have completed the @ref{Configure} step.
+
+@table @samp
+
+@item make man
+Generate man-pages for command-line utilities.
+
+@item make html
+Generate articles in HTML format from @file{texi} files.
+
+@item make txt
+Generate articles in plaintext format from @file{texi} files.
+
+@item make wiki
+Generate articles in Google Code Wiki format from @file{texi} files.
+
+@item make xml
+Generate articles in (Texinfo) XML format from @file{texi} files.
+
+@item make api
+Generate API in HTML format from header files.
+
+@item make articles
+Convenience; the equivalent of @command{make html txt wiki xml} .
+
+@item make doc
+Convenience; the equivalent of @command{make man articles api} .
+
+@end table
+
+And finally all of the document targets have a corresponding @b{clean} target which cleans up generated files. Simply prefix as follows:
+
+@example
+make manclean
+make htmlclean
+make txtclean
+make wikiclean
+make xmlclean
+make apiclean
+make articlesclean
+make docclean
+@end example
diff --git a/doc/texi/build/process.extract.texi b/doc/texi/build/process.extract.texi
new file mode 100644
index 0000000..b019cac
--- /dev/null
+++ b/doc/texi/build/process.extract.texi
@@ -0,0 +1,22 @@
+@section Extract
+Extract sources from a @value{TERM.srcdist} bundle. Releases are available from @url{@value{PROJECT.url.website}} in the @url{@value{PROJECT.url.downloads},downloads} section.
+
+@example
+@COMMAND.extract{}
+@end example
+
+Older versions of @code{tar} may not automatically uncompress the bundle, so you might have to either enter additional flags manually, or first decompress the bundle before extracting. Some possible command variations for uncompressing a @file{bz2} file:
+
+@example
+tar xjf @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.bz2
+bunzip2 -c @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.bz2 | tar xf -
+bzcat @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.bz2 | tar xf -
+@end example
+
+And for a @file{gz} file:
+
+@example
+tar xzf @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.gz
+gunzip -c @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.gz | tar xf -
+gzcat @value{PROJECT.name.lower}-@value{PROJECT.version}.tar.gz | tar xf -
+@end example
diff --git a/doc/texi/build/process.google.texi b/doc/texi/build/process.google.texi
new file mode 100644
index 0000000..52b4135
--- /dev/null
+++ b/doc/texi/build/process.google.texi
@@ -0,0 +1,24 @@
+@section Post Site and API Documentation to project website.
+
+This step is for project maintainers and can be used to update Site and API documentation. The following components are updated:
+
+@itemize
+@item Featured Wiki article: BuildRepository
+@item Featured Wiki article: BuildSource
+@item MP4v2 (trunk) docs (includes Release Notes and other articles, and API docs).
+@end itemize
+
+This procedure may only be run from a *nix platform and has only been tested on @value{OS.osx}.
+
+@table @samp
+
+@item make google.clean
+Clean any local working copy of google changeset.
+
+@item make google.post
+Generate required docs, sparse-checkout google tree, remove files which are no longer generated, add new files which are generated, and update existing files.
+
+@item svn ci -m "-refreshed GoogleCode site+api docs." google/.
+Check-in changes. This might take several minutes, especially if your upstream bandwidth is limited.
+
+@end table
diff --git a/doc/texi/build/process.install.texi b/doc/texi/build/process.install.texi
new file mode 100644
index 0000000..9087853
--- /dev/null
+++ b/doc/texi/build/process.install.texi
@@ -0,0 +1,8 @@
+@anchor{Install}
+@section Install
+
+The following command will install @value{PROJECT.name}.
+
+@example
+@COMMAND.install{}
+@end example
diff --git a/doc/texi/changelog/version-1.6.texi b/doc/texi/changelog/version-1.6.texi
new file mode 100644
index 0000000..4268820
--- /dev/null
+++ b/doc/texi/changelog/version-1.6.texi
@@ -0,0 +1,4 @@
+@enumerate
+@item
+Branched libmp4v2 module from mpeg4ip project version 1.6.1.
+@end enumerate
diff --git a/doc/texi/changelog/version-1.9.texi b/doc/texi/changelog/version-1.9.texi
new file mode 100644
index 0000000..8a6f8e4
--- /dev/null
+++ b/doc/texi/changelog/version-1.9.texi
@@ -0,0 +1,62 @@
+@enumerate
+@item
+Renamed public API top-level header to @code{<mp4v2/mp4v2.h>} and added namespaces for implementation (private) symbols. Only public API symbols are exported for dynamic (shared) or DLL libraries. All other symbols are now private, notwithstanding static-library usage.
+
+@item
+Added initial AC3 support.
+
+@item
+Added native Windows port. The port targets @code{WINVER 0x0500} which is Windows 2000 or higher.
+
+@item
+Added QuickTime and Nero chapter support. See @command{mp4chaps} command-line utility.
+@* Contributed by Ullrich Pollähne <u.pollaehne@@gmail.com>.
+
+@item
+Enhanced @command{mp4art} to support multiple cover-art atoms.
+
+@item
+Dropped @command{mp4dump} and replaced with @command{mp4file} which supports general file operations.
+
+@item
+Added @command{mp4track} with colr-box and pasp-box support.
+
+@item
+Fixed corruption of @b{udta.name} atom during read-in.
+
+@item
+Enhanced @command{mp4file --dump} to show hexdump-style canonical hex+ASCII data.
+
+@item
+Enhanced @command{mp4file --dump} to show human-readble value for tag @b{typeCode} values.
+
+@item
+Enhanced @command{mp4file --dump} to show human-readble value for ISO-639-2/T language codes.
+
+@item
+Added new public-API: MP4GetTrackLanguage().
+
+@item
+Added new public-API: MP4SetTrackLanguage().
+
+@item
+Added support for GCC precompiled-headers on various *nix platforms to speed up builds as we are using quite a few C++ headers.
+
+@item
+Enhanced @command{mp4track} to facilitate modification of @{ enabled, inmovie, inpreview, altgroup, volume, width, height, language, hdlrname, udtaname @} properties.
+
+@item
+Added new public-API: MP4GetTrackDurationPerChunk().
+
+@item
+Added new public-API: MP4SetTrackDurationPerChunk() to override default per-track chunk duration.
+
+@item
+Added new public-API: MP4WriteSampleDependency().
+
+@item
+Replaced (undocumented) public-API: MP4CreateEx() with MP4CreateProvider().
+
+@item
+Added new public-API for iTMF Generic functionality.
+@end enumerate
diff --git a/doc/texi/changelog/version-2.0.texi b/doc/texi/changelog/version-2.0.texi
new file mode 100644
index 0000000..ad57fac
--- /dev/null
+++ b/doc/texi/changelog/version-2.0.texi
@@ -0,0 +1,8 @@
+@enumerate
+@item
+Removed deprecated metadata/free-form API and replaced with iTMF Generic and Tags API.
+
+@item
+Added new public-API: MP4SetLogCallback().
+
+@end enumerate
diff --git a/doc/texi/releasenotes/intro.developer.texi b/doc/texi/releasenotes/intro.developer.texi
new file mode 100644
index 0000000..8260e9d
--- /dev/null
+++ b/doc/texi/releasenotes/intro.developer.texi
@@ -0,0 +1,5 @@
+@quotation Warning
+This is a @b{developer} release which includes all the normal pieces of a stable release but is considered @b{unstable}.
+The purpose of a developer release is to make available to @value{PROJECT.name} developers features which require collaborative testing.
+@end quotation
+
diff --git a/doc/texi/releasenotes/intro.stable.texi b/doc/texi/releasenotes/intro.stable.texi
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/texi/releasenotes/intro.stable.texi
diff --git a/doc/texi/tool/mp4art.texi b/doc/texi/tool/mp4art.texi
new file mode 100644
index 0000000..338e2f4
--- /dev/null
+++ b/doc/texi/tool/mp4art.texi
@@ -0,0 +1,55 @@
+@chapter mp4art
+This tool is used to manage iTunes Metadata Cover-art which is typically used to embed an image to a song file. For example, the songs in an album collection might all contain an image of the album cover art. This data is usually found in @samp{.m4a}, @samp{.m4v} and @samp{.mov} files.
+
+@table @samp
+@item --art-any
+act on all covr-boxes (default).
+Specifies the scope of the action to operate on all, if applicable, covr-boxes.
+
+@item --art-index IDX
+act on covr-box index IDX.
+Specifies the scope of the action to operate on single covr-box INDEX.
+@end table
+
+@table @samp
+@item --list
+list all covr-boxes.
+@example
+IDX     BYTES  CRC32     TYPE       FILE
+----------------------------------------------------------------------
+  0    173613  710a3ec9  JPEG       01 Life In Technicolor.m4a
+  0    173613  710a3ec9  JPEG       02 Cemeteries Of London.m4a
+  0    173613  710a3ec9  JPEG       03 Lost!.m4a
+  0    173613  710a3ec9  JPEG       04 42.m4a
+  0    173613  710a3ec9  JPEG       05 Lovers In Japan _ Reign Of Love.m4a
+  0    173613  710a3ec9  JPEG       06 Yes.m4a
+  0    173613  710a3ec9  JPEG       07 Viva La Vida.m4a
+  0    173613  710a3ec9  JPEG       08 Violet Hill.m4a
+  0    173613  710a3ec9  JPEG       09 Strawberry Swing.m4a
+  0    173613  710a3ec9  JPEG       10 Death And All His Friends.m4a
+@end example
+
+@item --add IMG
+add covr-box from IMG file.
+
+@item --replace IMG
+replace covr-box with IMG file.
+
+@item --remove
+remove covr-box.
+
+@item --extract
+extract covr-box.
+This will extract all covr-box data to image files in the format of @samp{BASENAME.art[INDEX].TYPE} .
+
+Example, add PNG image file:
+@example
+mp4art --add ACDC.png mysong.m4a
+@end example
+
+Example, extract image files from file:
+@example
+mp4art --extract mysong.m4a
+@end example
+
+@end table
diff --git a/doc/texi/tool/mp4file.texi b/doc/texi/tool/mp4file.texi
new file mode 100644
index 0000000..123f0e1
--- /dev/null
+++ b/doc/texi/tool/mp4file.texi
@@ -0,0 +1,40 @@
+@chapter mp4file
+
+@table @samp
+@item --list
+list (summary information).
+This will produce brief report when summarizing each mp4 file.
+@b{BRAND} shows the file's main brand identifier.
+@b{COMPAT} shows additional brands for which the file purports to be comaptible with.
+@b{SIZING} displays if the file has 64-bit extensions of any kind, otherwise 32-bit.
+Example output:
+@example
+BRAND  COMPAT              SIZING  FILE
+----------------------------------------------------------------------
+M4A    M4A,isom,mp42       32-bit  Song.m4a
+mp42   isom,mp42           32-bit  Movie1.m4v
+mp42   isom,mp42           32-bit  Movie2.m4v
+@end example
+
+@item --optimize
+optimize mp4 structure.
+This will rewrite the entire mp4 file which, if needed, will clean up
+any unused (free) sections, and re-order the atoms in a manner somewhat
+consistent with the best-practices described in the ISO base media file
+specification.
+
+@item --dump
+dump mp4 structure in human-readable format.
+An ASCII dump of mp4 atoms is printed to stdout. This action is heavily
+influenced by @samp{--debug} option.
+
+Example, list some files:
+@example
+mp4file --list *.mp4 *.m4a *.m4v
+@end example
+
+Example, dump a file with more than usual debugging information:
+@example
+mp4file -dd --dump movie.m4v
+@end example
+@end table
diff --git a/doc/texi/tool/mp4track.texi b/doc/texi/tool/mp4track.texi
new file mode 100644
index 0000000..8c5706e
--- /dev/null
+++ b/doc/texi/tool/mp4track.texi
@@ -0,0 +1,182 @@
+@chapter mp4track
+This tool is used to manage various aspects of individual tracks in an mp4 file. Some of the actions are mp4 (generic) while others may support standards based on mp4 files such as @samp{.m4a} or @samp{.m4v} files. Each action has an appropriate scope upon which it acts. See individual actions for details. The following parameters are used to set scopes for actions:
+
+@table @samp
+@item --track-any
+act on any/all tracks.
+
+@item --track-index IDX
+act on a single track specified by @b{index} value.
+A track index is 0-based and counts upwards for each track available.
+
+@item --track-id ID
+act on a single track specified by @b{id} value.
+A track id is a unique value assigned to each track and never changes.
+@end table
+
+The @b{list} action will produce a brief report of each track for each mp4 file.
+Many (but not all) of the values shown may be modified by actions documented later in this article.
+This will produce a brief report of each track for each mp4 file.
+
+@table @samp
+@item --list
+list all tracks in mp4.
+Example output:
+@example
+track[0] id=1
+  type           = video
+  enabled        = true
+  inMovie        = false
+  inPreview      = false
+  layer          = 0
+  alternateGroup = 0
+  volume         = 0.0000
+  width          = 850.96295166
+  height         = 360.00000000
+  language       = UNDEFINED(0)
+  handlerName    = 
+  userDataName   = <absent>
+@end example
+@end table
+
+The following group of actions are used to modify the values shown by @b{--list} action. The modification of these values should be done with great care on any files, and as always you are cautioned to @b{backup} your media files before modification.
+
+@table @samp
+@item --enabled BOOL
+set trak.tkhd.flags (enabled bit).
+When @b{true} indicates the track is enabled.
+
+@item --inmovie BOOL
+set trak.tkhd.flags (inMovie bit).
+When @b{true} indicates the track is used in the movie.
+
+@item --inpreview BOOL
+set trak.tkhd.flags (inPreview bit).
+When @b{true} indicates the track is used in the movie's preview.
+
+@item --layer NUM
+set trak.tkhd.layer.
+Specifies the front-to-back ordering of video tracks; tracks with lower numbers are closer to the viewer.
+@b{0} is the normal value, and @b{-1} would be in front of track 0, and so on.
+
+@item --altgroup NUM
+set trak.tkhd.alternate_group.
+An integer that specifies a group or collection of tracks. If this field is 0 there is no information on possible relations to other tracks. If this field is not 0, it should be the same for tracks that contain alternate data for one another and different for tracks belonging to different such groups. Only one track within an alternate group should be played or streamed at any one time, and must be distinguishable from other tracks in the group via attributes such as bitrate, codec, language, packet size etc. A group may have only one member.
+
+@item --volume FLOAT
+set trak.tkhd.volume.
+Specifies the track's relative audio volume. Full volume is @b{1.0} and is the normal value.
+
+@item --width FLOAT
+set trak.tkhd.width.
+Specifies the track's visual presentation width. By default this is the same as the pixel width of the images. All images in the sequence are scaled to this size @b{before} any overall transformation by the matrix.
+
+@item --height FLOAT
+set trak.tkhd.height.
+Specifies the track's visual presentation height. By default this is the same as the pixel width of the images. All images in the sequence are scaled to this size @b{before} any overall transformation by the matrix.
+
+@item --language CODE
+set trak.mdia.mdhd.language.
+Specifies the ISO-639-2/T langauge code of the track. For example, @samp{eng} for English, @samp{fra} for French.
+
+@item --hdlrname STR
+set trak.mdia.hdlr.name.
+Specifies a human-readable track type (for debugging and inspection purposes).
+
+@item --udtaname STR
+set trak.udta.name.value.
+Specifies an arbitrary track-name. This value is optional (may be absent).
+
+@item --udtaname-remove
+remove trak.udta.name atom.
+This action will remove the @b{optional} atom.
+
+@end table
+
+The @b{colr} related actions manage @b{Color Parameter} boxes which are used by QuickTime to map numerical values of pixels in a file to a common representation of color for video tracks. They may or may not be suitable for other Apple media players. Community feedback on compatibility is welcome.
+
+@table @samp
+@item --colr-list
+list all colr-boxes in mp4.
+
+@item --colr-add
+add colr-box to a video track.
+An individual track must be specified.
+
+@item --colr-set
+set colr-box parms.
+An individual track must be specified.
+
+@item --colr-remove
+remove colr-box from track.
+By default all colr-boxes will be removed unless an individual track is specified.
+
+@item --colr-parms CSV
+where CSV is IDX1,IDX2,IDX3 .
+Specify the exact parameters of an NCLC Color Parameter box as specified in the QuickTime specification.
+IDX1 correlates to the 16-bit primaries index.
+IDX2 correlates to the 16-bit transferFunction index.
+IDX3 correlates to the 16-bit matrixIndex index.
+Effects actions --colr-add, --colr-set.
+
+@item --colr-parm-hd
+equivalent to --colr-parms=1,1,1 .
+This is a convenience setting generally suitable for HD content.
+Effects actions --colr-add, --colr-set.
+
+@item --colr-parm-sd
+equivalent to --colr-parms=6,1,6 .
+This is a convenience setting generally suitable for SD content.
+Effects actions --colr-add, --colr-set.
+
+Example, add a colr-box tuned for HD content:
+@example
+mp4track --track-id=1 --colr-add --colr-parm-hd mymovie.m4v
+@end example
+
+Example, add a colr-box with arbitrary index parameters:
+@example
+mp4track --track-id=1 --colr-add --colr-parms=2,3,4 mymovie.m4v
+@end example
+
+@end table
+
+The @b{pasp} related actions manage @b{Picture Aspect Ratio} boxes which are used by QuickTime to specify height-to-width ratio of pixels for video tracks. They may or may not be suitable for other Apple media players. Community feedback on compatibility is welcome.
+
+@table @samp
+@item --pasp-list
+list all pasp-boxes in mp4.
+
+@item --pasp-add
+add pasp-box to a video track.
+An individual track must be specified.
+
+@item --pasp-set
+set pasp-box parms.
+An individual track must be specified.
+
+@item --pasp-remove
+remove pasp-box from track
+By default all pasp-boxes will be removed unless an individual track is specified.
+
+@item --pasp-parms CSV
+where CSV is hSPACING,vSPACING.
+Specify the exact parameters of Picture Aspect Ratio box as specified in the QuickTime specification.
+Effects actions --pasp-add, --pasp-set.
+
+Example, add a pasp-box with default (1,1) parameters for square pixels:
+@example
+mp4track --track-id=1 --pasp-add --pasp-parms=1,1 mymovie.m4v
+@end example
+
+Example, add a pasp-box for 16:9 digital 525 (NTSC):
+@example
+mp4track --track-id=1 --pasp-add --pasp-parms=40,33 mymovie.m4v
+@end example
+
+Example, add a pasp-box for 16:9 digital 625 (PAL):
+@example
+mp4track --track-id=1 --pasp-add --pasp-parms=118,81 mymovie.m4v
+@end example
+
+@end table
diff --git a/example/itmf/generic.c b/example/itmf/generic.c
new file mode 100644
index 0000000..3de14f2
--- /dev/null
+++ b/example/itmf/generic.c
@@ -0,0 +1,96 @@
+/* This is an example of iTMF Generic API.
+ * WARNING: this program will change/destroy certain tags in an mp4 file.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <mp4v2/mp4v2.h>
+
+int main( int argc, char** argv )
+{
+    if( argc != 2 ) {
+        printf( "usage: %s file.mp4\n", argv[0] );
+        return 1;
+    }
+
+    /* open file for modification */
+    MP4FileHandle file = MP4Modify( argv[1], MP4_DETAILS_ERROR, 0 );
+    if( file == MP4_INVALID_FILE_HANDLE ) {
+        printf( "MP4Modify failed\n" );
+        return 1;
+    }
+
+    /* show existing iTMF items */
+    MP4ItmfItemList* list = MP4ItmfGetItems( file );
+    printf( "list=%p\n", list );
+    if( list ) {
+        printf( "list size=%u\n", list->size );
+        uint32_t i;
+        for( i = 0; i < list->size; i++ ) {
+            MP4ItmfItem* item = &list->elements[i];
+            printf( "item[%u] type=%s\n", i, item->code );
+
+            if( item->mean )
+                printf( "    mean=%s\n", item->mean );
+            if( item->name )
+                printf( "    name=%s\n", item->name );
+
+            int j;
+            for( j = 0; j < item->dataList.size; j++ ) {
+                MP4ItmfData* data = &item->dataList.elements[j];
+                printf( "    data[%u] typeCode=%u valueSize=%u\n", j, data->typeCode, data->valueSize );
+            }
+        }
+
+        /* caller responsiblity to free */
+        MP4ItmfItemListFree( list );
+    }
+
+    /* add bogus item to file */
+    {
+        /* allocate item with 1 data element */
+        MP4ItmfItem* bogus = MP4ItmfItemAlloc( "bogu", 1 );
+
+        const char* const hello = "hello one";
+
+        MP4ItmfData* data = &bogus->dataList.elements[0];
+        data->typeCode = MP4_ITMF_BT_UTF8;
+        data->valueSize = strlen( hello );
+        data->value = (uint8_t*)malloc( data->valueSize );
+        memcpy( data->value, hello, data->valueSize );
+
+        /* add to mp4 file */
+        MP4ItmfAddItem( file, bogus );
+
+        /* caller responsibility to free */
+        MP4ItmfItemFree( bogus );
+    }
+
+    /* add bogus item with meaning and name to file */
+    {
+        /* allocate item with 1 data element */
+        MP4ItmfItem* bogus = MP4ItmfItemAlloc( "----", 1 );
+        bogus->mean = strdup( "com.garden.Tomato" );
+        bogus->name = strdup( "weight" );
+
+        const char* const hello = "hello two";
+
+        MP4ItmfData* data = &bogus->dataList.elements[0];
+        data->typeCode = MP4_ITMF_BT_UTF8;
+        data->valueSize = strlen( hello );
+        data->value = (uint8_t*)malloc( data->valueSize );
+        memcpy( data->value, hello, data->valueSize );
+
+        /* add to mp4 file */
+        MP4ItmfAddItem( file, bogus );
+
+        /* caller responsibility to free */
+        MP4ItmfItemFree( bogus );
+    }
+
+    /* free memory associated with structure and close */
+    MP4Close( file );
+
+    return 0;
+}
diff --git a/example/itmf/tags.c b/example/itmf/tags.c
new file mode 100644
index 0000000..a915865
--- /dev/null
+++ b/example/itmf/tags.c
@@ -0,0 +1,277 @@
+/* This is an example of iTMF Tags convenience API.
+ * WARNING: this program will change/destroy many tags in an mp4 file.
+ */
+
+#include <mp4v2/mp4v2.h>
+
+int main( int argc, char** argv )
+{
+    if( argc != 2 ) {
+        printf( "usage: %s file.mp4\n", argv[0] );
+        return 1;
+    }
+
+    /* open file for modification */
+    MP4FileHandle file = MP4Modify( argv[1], MP4_DETAILS_ERROR, 0 );
+    if( file == MP4_INVALID_FILE_HANDLE ) {
+        printf( "MP4Modify failed\n" );
+        return 1;
+    }
+
+    /* allocate */
+    const MP4Tags* tags = MP4TagsAlloc();
+
+    /* fetch data from MP4 file and populate structure */
+    MP4TagsFetch( tags, file );
+
+    /***************************************************************************
+     * print various tag values
+     */
+
+    if( tags->name )
+        printf( "name: %s\n", tags->name );
+
+    if( tags->artist )
+        printf( "artist: %s\n", tags->artist );
+
+    if( tags->albumArtist )
+        printf( "albumArtist: %s\n", tags->albumArtist );
+
+    if( tags->album )
+        printf( "album: %s\n", tags->album );
+
+    if( tags->grouping )
+        printf( "grouping: %s\n", tags->grouping );
+
+    if( tags->composer )
+        printf( "composer: %s\n", tags->composer );
+
+    if( tags->comments )
+        printf( "comments: %s\n", tags->comments );
+
+    if( tags->genre )
+        printf( "genre: %s\n", tags->genre );
+
+    if( tags->genreType )
+        printf( "genreType: %u\n", *tags->genreType );
+
+    if( tags->releaseDate )
+        printf( "releaseDate: %s\n", tags->releaseDate );
+
+    if( tags->track )
+        printf( "track: index=%u total=%u\n", tags->track->index, tags->track->total );
+
+    if( tags->disk )
+        printf( "disk: index=%u total=%u\n", tags->disk->index, tags->disk->total );
+
+    if( tags->tempo )
+        printf( "tempo: %u\n", *tags->tempo );
+
+    if( tags->compilation )
+        printf( "compilation: %u\n", *tags->compilation );
+
+    if( tags->tvShow )
+        printf( "tvShow: %s\n", tags->tvShow );
+
+    if( tags->tvNetwork )
+        printf( "tvNetwork: %s\n", tags->tvNetwork );
+
+    if( tags->tvEpisodeID )
+        printf( "tvEpisodeID: %s\n", tags->tvEpisodeID );
+
+    if( tags->tvSeason )
+        printf( "tvSeason: %u\n", *tags->tvSeason );
+
+    if( tags->tvEpisode )
+        printf( "tvEpisode: %u\n", *tags->tvEpisode );
+
+    if( tags->description )
+        printf( "description: %s\n", tags->description );
+
+    if( tags->longDescription )
+        printf( "longDescription: %s\n", tags->longDescription );
+
+    if( tags->lyrics )
+        printf( "lyrics: %s\n", tags->lyrics );
+
+    if( tags->sortName )
+        printf( "sortName: %s\n", tags->sortName );
+
+    if( tags->sortArtist )
+        printf( "sortArtist: %s\n", tags->sortArtist );
+
+    if( tags->sortAlbumArtist )
+        printf( "sortAlbumArtist: %s\n", tags->sortAlbumArtist );
+
+    if( tags->sortAlbum )
+        printf( "sortAlbum: %s\n", tags->sortAlbum );
+
+    if( tags->sortComposer )
+        printf( "sortComposer: %s\n", tags->sortComposer );
+
+    if( tags->sortTVShow )
+        printf( "sortTVShow: %s\n", tags->sortTVShow );
+
+    if( tags->artworkCount ) {
+        const MP4TagArtwork* art = tags->artwork; /* artwork != NULL when artworkCount > 0 */
+        uint32_t i;
+        for( i = 0; i < tags->artworkCount; i++, art++ )
+            printf( "art[%d]: type=%d size=%u data=%p\n", i, art->type, art->size, art->data );
+    }
+
+    if( tags->copyright )
+        printf( "copyright: %s\n", tags->copyright );
+
+    if( tags->encodingTool )
+        printf( "encodingTool: %s\n", tags->encodingTool );
+
+    if( tags->encodedBy )
+        printf( "encodedBy: %s\n", tags->encodedBy );
+
+    if( tags->purchaseDate )
+        printf( "purchaseDate: %s\n", tags->purchaseDate );
+
+    if( tags->podcast )
+        printf( "podcast: %u\n", *tags->podcast );
+
+    if( tags->keywords )
+        printf( "keywords: %s\n", tags->keywords );
+
+    if( tags->category )
+        printf( "category: %s\n", tags->category );
+
+    if( tags->hdVideo )
+        printf( "hdVideo: %u\n", *tags->hdVideo );
+
+    if( tags->mediaType )
+        printf( "mediaType: %u\n", *tags->mediaType );
+
+    if( tags->contentRating )
+        printf( "contentRating: %u\n", *tags->contentRating );
+
+    if( tags->gapless )
+        printf( "gapless: %u\n", *tags->gapless );
+
+    if( tags->contentID )
+        printf( "contentID: %u\n", *tags->contentID );
+
+    if( tags->artistID )
+        printf( "artistID: %u\n", *tags->artistID );
+
+    if( tags->playlistID )
+        printf( "playlistID: %llu\n", *tags->playlistID );
+
+    if( tags->genreID )
+        printf( "genreID: %u\n", *tags->genreID );
+
+    if( tags->xid )
+        printf( "xid: %s\n", tags->xid );
+
+    if( tags->iTunesAccount )
+        printf( "iTunesAccount: %s\n", tags->iTunesAccount );
+
+    if( tags->iTunesAccountType )
+        printf( "iTunesAccountType: %u\n", *tags->iTunesAccountType );
+
+    if( tags->iTunesCountry )
+        printf( "iTunesCountry: %u\n", *tags->iTunesCountry );
+
+    /**************************************************************************
+     * modify various tags values
+     */
+
+    uint8_t  n8;
+    uint16_t n16;
+    uint32_t n32;
+    uint64_t n64;
+
+    MP4TagTrack dtrack;
+    dtrack.index = 1;
+    dtrack.total = 1;
+
+    MP4TagDisk ddisk;
+    ddisk.index = 1;
+    ddisk.total = 1;
+
+    MP4TagsSetName              ( tags, "my name" );
+    MP4TagsSetArtist            ( tags, "my artist" );
+    MP4TagsSetAlbumArtist       ( tags, "my albumArtist" );
+    MP4TagsSetAlbum             ( tags, "my album" );
+    MP4TagsSetGrouping          ( tags, "my grouping" );
+    MP4TagsSetComposer          ( tags, "my composer" );
+    MP4TagsSetComments          ( tags, "my comments" );
+    MP4TagsSetGenre             ( tags, "my genre" );
+    n16 = 5; /* disco */
+    MP4TagsSetGenreType         ( tags, &n16 );
+    MP4TagsSetReleaseDate       ( tags, "my releaseDate" );
+    MP4TagsSetTrack             ( tags, &dtrack );
+    MP4TagsSetDisk              ( tags, &ddisk );
+    n16 = 60; /* bpm */
+    MP4TagsSetTempo             ( tags, &n16 );
+    n8 = 0; /* false */
+    MP4TagsSetCompilation       ( tags, &n8 );
+
+    MP4TagsSetTVShow            ( tags, "my tvShow" );
+    MP4TagsSetTVNetwork         ( tags, "my tvNetwork" );
+    MP4TagsSetTVEpisodeID       ( tags, "my tvEpisodeID" );
+    n32 = 0;
+    MP4TagsSetTVSeason          ( tags, &n32 );
+    n32 = 0;
+    MP4TagsSetTVEpisode         ( tags, &n32 );
+
+    MP4TagsSetDescription       ( tags, "my description" );
+    MP4TagsSetLongDescription   ( tags, "my longDescription" );
+    MP4TagsSetLyrics            ( tags, "my lyrics" );
+
+    MP4TagsSetSortName          ( tags, "my sortName" );
+    MP4TagsSetSortArtist        ( tags, "my sortArtist" );
+    MP4TagsSetSortAlbumArtist   ( tags, "my sortAlbumArtist" );
+    MP4TagsSetSortAlbum         ( tags, "my sortAlbum" );
+    MP4TagsSetSortComposer      ( tags, "my sortComposer" );
+    MP4TagsSetSortTVShow        ( tags, "my sortTVShow" );
+
+    MP4TagsSetCopyright         ( tags, "my copyright" );
+    MP4TagsSetEncodingTool      ( tags, "my encodingTool" );
+    MP4TagsSetEncodedBy         ( tags, "my encodedBy" );
+    MP4TagsSetPurchaseDate      ( tags, "my purchaseDate" );
+
+    n8 = 0; /* false */
+    MP4TagsSetPodcast           ( tags, &n8 );
+    MP4TagsSetKeywords          ( tags, "my keywords" );
+    MP4TagsSetCategory          ( tags, "my category" );
+
+    n8 = 0; /* false */
+    MP4TagsSetHDVideo           ( tags, &n8 ); // false
+    n8 = 9; /* movie */
+    MP4TagsSetMediaType         ( tags, &n8 ); // movie
+    n8 = 0; /* none */
+    MP4TagsSetContentRating     ( tags, &n8 ); // none
+    n8 = 0; /* false */
+    MP4TagsSetGapless           ( tags, &n8 ); // false
+
+    MP4TagsSetITunesAccount     ( tags, "my iTunesAccount" );
+    n8 = 0; /* iTunes */
+    MP4TagsSetITunesAccountType ( tags, &n8 );
+    n32 = 143441; /* USA */
+    MP4TagsSetITunesCountry     ( tags, &n32 );
+    n32 = 0;
+    MP4TagsSetContentID         ( tags, &n32 );
+    n32 = 0;
+    MP4TagsSetArtistID          ( tags, &n32 );
+    n64 = 0;
+    MP4TagsSetPlaylistID        ( tags, &n64 );
+    n32 = 0;
+    MP4TagsSetGenreID           ( tags, &n32 );
+    n32 = 0;
+    MP4TagsSetComposerID        ( tags, &n32 );
+    MP4TagsSetXID               ( tags, "my prefix:my scheme:my identifier" );
+
+    /* push data to mp4 file */
+    MP4TagsStore( tags, file );
+
+    /* free memory associated with structure and close */
+    MP4TagsFree( tags );
+    MP4Close( file );
+
+    return 0;
+}
diff --git a/example/provider/provider.c b/example/provider/provider.c
new file mode 100644
index 0000000..03e55e2
--- /dev/null
+++ b/example/provider/provider.c
@@ -0,0 +1,88 @@
+/* This example makes use of the MP4FileProvider API to use custom file
+ * input/output routines.
+ */
+
+#include <mp4v2/mp4v2.h>
+#include <stdio.h>
+
+/*****************************************************************************/
+
+static void* my_open( const char* name, MP4FileMode mode )
+{
+    const char* om;
+    switch( mode ) {
+        case FILEMODE_READ:     om = "rb";  break;
+        case FILEMODE_MODIFY:   om = "r+b"; break;
+        case FILEMODE_CREATE:   om = "w+b"; break;
+
+        case FILEMODE_UNDEFINED:
+        default:
+            om = "rb";
+            break;
+    }
+
+    return fopen( name, om );
+}
+
+static int my_seek( void* handle, int64_t pos )
+{
+    return fseeko( (FILE*)handle, pos, SEEK_SET ) != 0;
+}
+
+static int my_read( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize )
+{
+    if( fread( buffer, size, 1, (FILE*)handle ) != 1)
+        return 1;
+    *nin = size;
+    return 0;
+}
+
+static int my_write( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize )
+{
+    if( fwrite( buffer, size, 1, (FILE*)handle ) != 1)
+        return 1;
+    *nout = size;
+    return 0;
+}
+
+static int my_close( void* handle )
+{
+    return fclose( (FILE*)handle ) != 0;
+}
+
+/*****************************************************************************/
+
+int main( int argc, char** argv )
+{
+    if( argc != 2 ) {
+        printf( "usage: %s file.mp4\n", argv[0] );
+        return 1;
+    }
+
+    /* populate data structure with custom functions.
+     * safe to put on stack as it will be immediately copied internally.
+     */
+    MP4FileProvider provider;
+
+    provider.open  = my_open;
+    provider.seek  = my_seek;
+    provider.read  = my_read;
+    provider.write = my_write;
+    provider.close = my_close;
+
+    /* open file for read */
+    MP4FileHandle file = MP4ReadProvider( argv[1], 0, &provider );
+    if( file == MP4_INVALID_FILE_HANDLE ) {
+        printf( "MP4Read failed\n" );
+        return 1;
+    }
+
+    /* dump file contents */
+    if( !MP4Dump( file, stdout, 0 ))
+        printf( "MP4Dump failed\n" );
+
+    /* cleanup and close */
+    MP4Close( file );
+
+    return 0;
+}
diff --git a/include/mp4v2/chapter.h b/include/mp4v2/chapter.h
new file mode 100644
index 0000000..5b493a5
--- /dev/null
+++ b/include/mp4v2/chapter.h
@@ -0,0 +1,176 @@
+#ifndef MP4V2_CHAPTER_H
+#define MP4V2_CHAPTER_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_chapter MP4v2 Chapter
+ *  @{
+ *
+ *****************************************************************************/
+
+/** The maximum length of a QuickTime chapter title (in 8-bit chars)
+ */
+#define MP4V2_CHAPTER_TITLE_MAX 1023
+
+/** Chapter item.
+ *  This item defines various attributes for a chapter.
+ *  @ingroup mp4_chapter
+ */
+typedef struct MP4Chapter_s {
+    MP4Duration duration; /**< duration of chapter in milliseconds */
+    char title[MP4V2_CHAPTER_TITLE_MAX+1]; /**< title of chapter */
+} MP4Chapter_t;
+
+/** Known chapter types.
+ *  @ingroup mp4_chapter
+ */
+typedef enum {
+    MP4ChapterTypeNone = 0, /**< no chapters found return value */
+    MP4ChapterTypeAny  = 1, /**< any or all known chapter types */
+    MP4ChapterTypeQt   = 2, /**< QuickTime chapter type */
+    MP4ChapterTypeNero = 4  /**< Nero chapter type */
+} MP4ChapterType;
+
+/** Add a QuickTime chapter.
+ *
+ *  This function adds a QuickTime chapter to file <b>hFile</b>.
+ *
+ *  @param hFile handle of file to add chapter.
+ *  @param chapterTrackId ID of chapter track or #MP4_INVALID_TRACK_ID
+ *      if unknown.
+ *  @param chapterDuration duration (in the timescale of the chapter track).
+ *  @param chapterTitle title text for the chapter or NULL to use default
+ *      title format ("Chapter %03d", n) where n is the chapter number.
+ */
+MP4V2_EXPORT
+void MP4AddChapter(
+    MP4FileHandle hFile,
+    MP4TrackId    chapterTrackId,
+    MP4Duration   chapterDuration,
+    const char*   chapterTitle DEFAULT(0));
+
+/** Add a QuickTime chapter track.
+ *
+ *  This function adds a chapter (text) track to file <b>hFile</b>.
+ *  The optional parameter <b>timescale</b> may be supplied to give the new
+ *  chapter a specific timescale. Otherwise the chapter track will have
+ *  the same timescale as the reference track defined in parameter refTrackId.
+ *
+ *  @param hFile handle of file to add chapter track.
+ *  @param refTrackId ID of the track that will reference the chapter track.
+ *  @param timescale the timescale of the chapter track or 0 to use the
+ *      timescale of track specified by <b>refTrackId</b>.
+ *
+ *  @return ID of the created chapter track.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddChapterTextTrack(
+    MP4FileHandle hFile,
+    MP4TrackId    refTrackId,
+    uint32_t      timescale DEFAULT(0) );
+
+/** Add a Nero chapter.
+ *
+ *  This function adds a Nero chapter to file <b>hFile</b>.
+ *
+ *  @param hFile handle of file to add chapter.
+ *  @param chapterStart the start time of the chapter in 100 nanosecond units
+ *  @param chapterTitle title text for the chapter or NULL to use default
+ *      title format ("Chapter %03d", n) where n is the chapter number.
+ */
+MP4V2_EXPORT
+void MP4AddNeroChapter(
+    MP4FileHandle hFile,
+    MP4Timestamp  chapterStart,
+    const char*   chapterTitle DEFAULT(0));
+
+/** Convert chapters to another type.
+ *
+ *  This function converts existing chapters in file <b>hFile</b>
+ *  from one type to another type.
+ *  Conversion from Nero to QuickTime or QuickTime to Nero is supported.
+ *
+ *  @param hFile handle of file to convert.
+ *  @param toChapterType the chapter type to convert to:
+ *      @li #MP4ChapterTypeQt (convert from Nero to Qt)
+ *      @li #MP4ChapterTypeNero (convert from Qt to Nero)
+ *
+ *  @return the chapter type before conversion or #MP4ChapterTypeNone
+ *      if the source chapters do not exist
+ *      or invalid <b>toChapterType</b> was specified.
+ */
+MP4V2_EXPORT
+MP4ChapterType MP4ConvertChapters(
+    MP4FileHandle  hFile,
+    MP4ChapterType toChapterType DEFAULT(MP4ChapterTypeQt));
+
+/** Delete chapters.
+ *
+ *  This function deletes existing chapters in file <b>hFile</b>.
+ *
+ *  @param hFile handle of file to delete chapters.
+ *  @param chapterType the type of chapters to delete:
+ *      @li #MP4ChapterTypeAny (delete all known chapter types)
+ *      @li #MP4ChapterTypeQt
+ *      @li #MP4ChapterTypeNero
+ *  @param chapterTrackId ID of the chapter track if known,
+ *      or #MP4_INVALID_TRACK_ID.
+ *      Only applies when <b>chapterType</b>=#MP4ChapterTypeQt.
+ *
+ *  @return the type of deleted chapters
+ */
+MP4V2_EXPORT
+MP4ChapterType MP4DeleteChapters(
+    MP4FileHandle  hFile,
+    MP4ChapterType chapterType DEFAULT(MP4ChapterTypeQt),
+    MP4TrackId     chapterTrackId DEFAULT(MP4_INVALID_TRACK_ID) );
+
+/** Get list of chapters.
+ *
+ *  This function gets a chpter list from file <b>hFile</b>.
+ *
+ *  @param hFile handle of file to read.
+ *  @param chapterList address receiving array of chapter items.
+ *      If a non-NULL is received the caller is responsible for freeing the
+ *      memory with MP4Free().
+ *  @param chapterCount address receiving count of items in array.
+ *  @param chapterType the type of chapters to read:
+ *      @li #MP4ChapterTypeAny (any chapters, searched in order of Qt, Nero)
+ *      @li #MP4ChapterTypeQt
+ *      @li #MP4ChapterTypeNero
+ *
+ *  @result the first type of chapters found.
+ */
+MP4V2_EXPORT
+MP4ChapterType MP4GetChapters(
+    MP4FileHandle  hFile,
+    MP4Chapter_t** chapterList,
+    uint32_t*      chapterCount,
+    MP4ChapterType chapterType DEFAULT(MP4ChapterTypeQt));
+
+/** Set list of chapters OKOK.
+ *
+ *  This functions sets the complete chapter list in file <b>hFile</b>.
+ *  If any chapters of the same type already exist they will first
+ *  be deleted.
+ *
+ *  @param hFile handle of file to modify.
+ *  @param chapterList array of chapters items.
+ *  @param chapterCount count of items in array.
+ *  @param chapterType type of chapters to write:
+ *      @li #MP4ChapterTypeAny (chapters of all types are written)
+ *      @li #MP4ChapterTypeQt
+ *      @li #MP4ChapterTypeNero
+ *
+ *  @return the type of chapters written.
+ */
+MP4V2_EXPORT
+MP4ChapterType MP4SetChapters(
+    MP4FileHandle hFile,
+    MP4Chapter_t* chapterList,
+    uint32_t       chapterCount,
+    MP4ChapterType chapterType DEFAULT(MP4ChapterTypeQt));
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_CHAPTER_H */
diff --git a/include/mp4v2/file.h b/include/mp4v2/file.h
new file mode 100644
index 0000000..b2124d2
--- /dev/null
+++ b/include/mp4v2/file.h
@@ -0,0 +1,435 @@
+#ifndef MP4V2_FILE_H
+#define MP4V2_FILE_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_file MP4v2 File I/O
+ *  @{
+ *
+ *****************************************************************************/
+
+/** Bit: enable 64-bit data-atoms. */
+#define MP4_CREATE_64BIT_DATA 0x01
+/** Bit: enable 64-bit time-atoms. @note Incompatible with QuickTime. */
+#define MP4_CREATE_64BIT_TIME 0x02
+/** Bit: do not recompute avg/max bitrates on file close.  @note See http://code.google.com/p/mp4v2/issues/detail?id=66 */
+#define MP4_CLOSE_DO_NOT_COMPUTE_BITRATE 0x01
+
+/** Enumeration of file modes for custom file provider. */
+typedef enum MP4FileMode_e
+{
+    FILEMODE_UNDEFINED, /**< undefined */
+    FILEMODE_READ,      /**< file may be read */
+    FILEMODE_MODIFY,    /**< file may be read/written */
+    FILEMODE_CREATE    /**< file will be created/truncated for read/write */
+} MP4FileMode;
+
+/** Structure of functions implementing custom file provider.
+ *
+ *  Except for <b>open</b>, all the functions must return a true value
+ *  to indicate failure or false on success. The open function must return
+ *  a pointer or handle which represents the open file, otherwise NULL.
+ *
+ *  maxChunkSize is a hint suggesting what the max size of data should be read
+ *  as in underlying read/write operations. A value of 0 indicates there is no hint.
+ */
+typedef struct MP4FileProvider_s
+{
+    void* ( *open    )( const char* name, MP4FileMode mode );
+    int   ( *seek    )( void* handle, int64_t pos );
+    int   ( *read    )( void* handle, void* buffer, int64_t size, int64_t* nin, int64_t maxChunkSize );
+    int   ( *write   )( void* handle, const void* buffer, int64_t size, int64_t* nout, int64_t maxChunkSize );
+    int   ( *close   )( void* handle );
+    int   ( *getSize )( void* handle, int64_t* nout );
+} MP4FileProvider;
+
+/** Close an mp4 file.
+ *  MP4Close closes a previously opened mp4 file. If the file was opened
+ *  writable with MP4Create() or MP4Modify(), then MP4Close() will write
+ *  out all pending information to disk.
+ *
+ *  @param hFile handle of file to close.
+ *  @param flags bitmask that allows the user to set extra options for the
+ *       close commands.  Valid options include:
+ *          @li #MP4_CLOSE_DO_NOT_COMPUTE_BITRATE
+ */
+MP4V2_EXPORT
+void MP4Close(
+    MP4FileHandle hFile,
+    uint32_t    flags DEFAULT(0) );
+
+/** Create a new mp4 file.
+ *
+ *  MP4Create is the first call that should be used when you want to create
+ *  a new, empty mp4 file. It is equivalent to opening a file for writing,
+ *  but also involved with creation of necessary mp4 framework structures.
+ *  ie. invoking MP4Create() followed by MP4Close() will result in a file
+ *  with a non-zero size.
+ *
+ *  @param fileName pathname of the file to be created.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param flags bitmask that allows the user to set 64-bit values for
+ *      data or time atoms. Valid bits may be any combination of:
+ *          @li #MP4_CREATE_64BIT_DATA
+ *          @li #MP4_CREATE_64BIT_TIME
+ *
+ *  @return On success a handle of the newly created file for use in
+ *      subsequent calls to the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4Create(
+    const char* fileName,
+    uint32_t    flags DEFAULT(0) );
+
+/** Create a new mp4 file with extended options.
+ *
+ *  MP4CreateEx is an extended version of MP4Create().
+ *
+ *  @param fileName pathname of the file to be created.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param flags bitmask that allows the user to set 64-bit values for
+ *      data or time atoms. Valid bits may be any combination of:
+ *          @li #MP4_CREATE_64BIT_DATA
+ *          @li #MP4_CREATE_64BIT_TIME
+ *  @param add_ftyp if true an <b>ftyp</b> atom is automatically created.
+ *  @param add_iods if true an <b>iods</b> atom is automatically created.
+ *  @param majorBrand <b>ftyp</b> brand identifier.
+ *  @param minorVersion <b>ftyp</b> informative integer for the minor version
+ *      of the major brand.
+ *  @param compatibleBrands <b>ftyp</b> list of compatible brands.
+ *  @param compatibleBrandsCount is the count of items specified in
+ *      compatibleBrands.
+ *
+ *  @return On success a handle of the newly created file for use in
+ *      subsequent calls to the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4CreateEx(
+    const char* fileName,
+    uint32_t    flags DEFAULT(0),
+    int         add_ftyp DEFAULT(1),
+    int         add_iods DEFAULT(1),
+    char*       majorBrand DEFAULT(0),
+    uint32_t    minorVersion DEFAULT(0),
+    char**      compatibleBrands DEFAULT(0),
+    uint32_t    compatibleBrandsCount DEFAULT(0) );
+
+/** Create a new mp4 file.
+ *
+ *  MP4CreateProvider is the first call that should be used when you want to
+ *  create a new, empty mp4 file. It is equivalent to opening a file for
+ *  writing, but also involved with creation of necessary mp4 framework
+ *  structures. ie. invoking MP4CreateProvider() followed by MP4Close() will
+ *  result in a file with a non-zero size.
+ *
+ *  @param fileName pathname of the file to be created.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param flags bitmask that allows the user to set 64-bit values for
+ *      data or time atoms. Valid bits may be any combination of:
+ *          @li #MP4_CREATE_64BIT_DATA
+ *          @li #MP4_CREATE_64BIT_TIME
+ *  @param fileProvider custom implementation of file I/O operations.
+ *      All functions in structure must be implemented.
+ *      The structure is immediately copied internally.
+ *
+ *  @return On success a handle of the newly created file for use in
+ *      subsequent calls to the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4CreateProvider(
+    const char*            fileName,
+    uint32_t               flags DEFAULT(0),
+    const MP4FileProvider* fileProvider DEFAULT(NULL) );
+
+/** Create a new mp4 file with extended options.
+ *
+ *  MP4CreateProviderEx is an extended version of MP4CreateProvider().
+ *
+ *  @param fileName pathname of the file to be created.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param flags bitmask that allows the user to set 64-bit values for
+ *      data or time atoms. Valid bits may be any combination of:
+ *          @li #MP4_CREATE_64BIT_DATA
+ *          @li #MP4_CREATE_64BIT_TIME
+ *  @param fileProvider custom implementation of file I/O operations.
+ *      All functions in structure must be implemented.
+ *      The structure is immediately copied internally.
+ *  @param add_ftyp if true an <b>ftyp</b> atom is automatically created.
+ *  @param add_iods if true an <b>iods</b> atom is automatically created.
+ *  @param majorBrand <b>ftyp</b> brand identifier.
+ *  @param minorVersion <b>ftyp</b> informative integer for the minor version
+ *      of the major brand.
+ *  @param compatibleBrands <b>ftyp</b> list of compatible brands.
+ *  @param compatibleBrandsCount is the count of items specified in
+ *      compatibleBrands.
+ *
+ *  @return On success a handle of the newly created file for use in
+ *      subsequent calls to the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4CreateProviderEx(
+    const char*            fileName,
+    uint32_t               flags DEFAULT(0),
+    const MP4FileProvider* fileProvider DEFAULT(NULL),
+    int                    add_ftyp DEFAULT(1),
+    int                    add_iods DEFAULT(1),
+    char*                  majorBrand DEFAULT(0),
+    uint32_t               minorVersion DEFAULT(0),
+    char**                 compatibleBrands DEFAULT(0),
+    uint32_t               compatibleBrandsCount DEFAULT(0) );
+
+/** Dump mp4 file contents as ASCII either to stdout or the
+ *  log callback (@p see MP4SetLogCallback)
+ *
+ *  Dump is an invaluable debugging tool in that in can reveal all the details
+ *  of the mp4 control structures. However, the output will not make much sense
+ *  until you familiarize yourself with the mp4 specification (or the Quicktime
+ *  File Format specification).
+ *
+
+ *  Note that MP4Dump() will not print the individual values of control tables,
+ *  such as the size of each sample, unless the current log level is at least
+ *  #MP4_LOG_VERBOSE2.  @p see MP4LogSetLevel() for how to set this.
+ *
+ *  @param hFile handle of file to dump.
+ *  @param dumpImplicits prints properties which would not actually be
+ *      written to the mp4 file, but still exist in mp4 control structures.
+ *      ie. they are implicit given the current values of other controlling
+ *      properties.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4Dump(
+    MP4FileHandle hFile,
+    bool          dumpImplicits DEFAULT(0) );
+
+/** Return a textual summary of an mp4 file.
+ *
+ *  MP4FileInfo provides a string that contains a textual summary of the
+ *  contents of an mp4 file. This includes the track id's, the track type,
+ *  and track specific information. For example, for a video track, media
+ *  encoding, image size, frame rate, and bitrate are summarized.
+ *
+ *  Note that the returned string is malloc'ed, so it is the caller's
+ *  responsibility to free() the string. Also note that the returned string
+ *  contains newlines and tabs which may or may not be desirable. 
+ *
+ *  The following is an example of the output of MP4Info():
+@verbatim
+Track  Type   Info
+1      video  MPEG-4 Simple @ L3, 119.625 secs, 1008 kbps, 352x288 @ 24.00 fps
+2      audio  MPEG-4, 119.327 secs, 128 kbps, 44100 Hz
+3      hint   Payload MP4V-ES for track 1
+4      hint   Payload mpeg4-generic for track 2
+5      od     Object Descriptors
+6      scene  BIFS
+@endverbatim
+ *
+ *  @param fileName pathname to mp4 file to summarize.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param trackId specifies track to summarize. If the value is
+ *      #MP4_INVALID_TRACK_ID, the summary info is created for all
+ *      tracks in the file.
+ *
+ *  @return On success a malloc'd string containing summary information.
+ *      On failure, <b>NULL</b>.
+ *
+ *  @see MP4Info().
+ */
+MP4V2_EXPORT
+char* MP4FileInfo(
+    const char* fileName,
+    MP4TrackId  trackId DEFAULT(MP4_INVALID_TRACK_ID) );
+
+/** Accessor for the filename associated with a file handle
+ *
+ * @param hFile a file handle
+ *
+ * @return the NUL-terminated, UTF-8 encoded filename
+ * associated with @p hFile
+ */
+MP4V2_EXPORT
+const char* MP4GetFilename(
+    MP4FileHandle hFile );
+
+/** Return a textual summary of an mp4 file.
+ *
+ *  MP4FileInfo provides a string that contains a textual summary of the
+ *  contents of an mp4 file. This includes the track id's, the track type,
+ *  and track specific information. For example, for a video track, media
+ *  encoding, image size, frame rate, and bitrate are summarized.
+ *
+ *  Note that the returned string is malloc'ed, so it is the caller's
+ *  responsibility to free() the string. Also note that the returned string
+ *  contains newlines and tabs which may or may not be desirable. 
+ *
+ *  The following is an example of the output of MP4Info():
+@verbatim
+Track  Type   Info
+1      video  MPEG-4 Simple @ L3, 119.625 secs, 1008 kbps, 352x288 @ 24.00 fps
+2      audio  MPEG-4, 119.327 secs, 128 kbps, 44100 Hz
+3      hint   Payload MP4V-ES for track 1
+4      hint   Payload mpeg4-generic for track 2
+5      od     Object Descriptors
+6      scene  BIFS
+@endverbatim
+ *
+ *  @param hFile handle of file to summarize.
+ *  @param trackId specifies track to summarize. If the value is
+ *      #MP4_INVALID_TRACK_ID, the summary info is created for all
+ *      tracks in the file.
+ *
+ *  @return On success a malloc'd string containing summary information.
+ *      On failure, <b>NULL</b>.
+ *
+ *  @see MP4FileInfo().
+ */
+MP4V2_EXPORT
+char* MP4Info(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId DEFAULT(MP4_INVALID_TRACK_ID) );
+
+/** Modify an existing mp4 file.
+ *
+ *  MP4Modify is the first call that should be used when you want to modify
+ *  an existing mp4 file. It is roughly equivalent to opening a file in
+ *  read/write mode.
+ *
+ *  Since modifications to an existing mp4 file can result in a sub-optimal
+ *  file layout, you may want to use MP4Optimize() after you have  modified
+ *  and closed the mp4 file.
+ *
+ *  @param fileName pathname of the file to be modified.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param flags currently ignored.
+ *
+ *  @return On success a handle of the target file for use in subsequent calls
+ *      to the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4Modify(
+    const char* fileName,
+    uint32_t    flags DEFAULT(0) );
+
+/** Optimize the layout of an mp4 file.
+ *
+ *  MP4Optimize reads an existing mp4 file and writes a new version of the
+ *  file with the two important changes:
+ *
+ *  First, the mp4 control information is moved to the beginning of the file.
+ *  (Frequenty it is at the end of the file due to it being constantly
+ *  modified as track samples are added to an mp4 file). This optimization
+ *  is useful in that in allows the mp4 file to be HTTP streamed.
+ *
+ *  Second, the track samples are interleaved so that the samples for a
+ *  particular instant in time are colocated within the file. This
+ *  eliminates disk seeks during playback of the file which results in
+ *  better performance.
+ *
+ *  There are also two important side effects of MP4Optimize():
+ *
+ *  First, any free blocks within the mp4 file are eliminated.
+ *
+ *  Second, as a side effect of the sample interleaving process any media
+ *  data chunks that are not actually referenced by the mp4 control
+ *  structures are deleted. This is useful if you have called MP4DeleteTrack()
+ *  which only deletes the control information for a track, and not the
+ *  actual media data.
+ *
+ *  @param fileName pathname of (existing) file to be optimized.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param newFileName pathname of the new optimized file.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *      If NULL a temporary file in the same directory as the
+ *      <b>fileName</b> will be used and <b>fileName</b>
+ *      will be over-written upon successful completion.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4Optimize(
+    const char* fileName,
+    const char* newFileName DEFAULT(NULL) );
+
+
+/** Read an existing mp4 file.
+ *
+ *  MP4Read is the first call that should be used when you want to just
+ *  read an existing mp4 file. It is equivalent to opening a file for
+ *  reading, but in addition the mp4 file is parsed and the control
+ *  information is loaded into memory. Note that actual track samples are not
+ *  read into memory until MP4ReadSample() is called.
+ *
+ *  @param fileName pathname of the file to be read.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+(
+ *  @return On success a handle of the file for use in subsequent calls to
+ *      the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4Read(
+    const char* fileName );
+
+/** Read an existing mp4 file.
+ *
+ *  MP4ReadProvider is the first call that should be used when you want to just
+ *  read an existing mp4 file. It is equivalent to opening a file for
+ *  reading, but in addition the mp4 file is parsed and the control
+ *  information is loaded into memory. Note that actual track samples are not
+ *  read into memory until MP4ReadSample() is called.
+ *
+ *  @param fileName pathname of the file to be read.
+ *      On Windows, this should be a UTF-8 encoded string.
+ *      On other platforms, it should be an 8-bit encoding that is
+ *      appropriate for the platform, locale, file system, etc.
+ *      (prefer to use UTF-8 when possible).
+ *  @param fileProvider custom implementation of file I/O operations.
+ *      All functions in structure must be implemented.
+ *      The structure is immediately copied internally.
+ *
+ *  @return On success a handle of the file for use in subsequent calls to
+ *      the library.
+ *      On error, #MP4_INVALID_FILE_HANDLE.
+ */
+MP4V2_EXPORT
+MP4FileHandle MP4ReadProvider(
+    const char*            fileName,
+    const MP4FileProvider* fileProvider DEFAULT(NULL) );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_FILE_H */
diff --git a/include/mp4v2/file_prop.h b/include/mp4v2/file_prop.h
new file mode 100644
index 0000000..15435e8
--- /dev/null
+++ b/include/mp4v2/file_prop.h
@@ -0,0 +1,148 @@
+#ifndef MP4V2_FILE_PROP_H
+#define MP4V2_FILE_PROP_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_file_prop MP4v2 File Property
+ *  @{
+ *
+ *****************************************************************************/
+
+/* generic props */
+
+MP4V2_EXPORT
+bool MP4HaveAtom(
+    MP4FileHandle hFile,
+    const char*   atomName );
+
+MP4V2_EXPORT
+bool MP4GetIntegerProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    uint64_t*     retval );
+
+MP4V2_EXPORT
+bool MP4GetFloatProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    float*        retvalue );
+
+MP4V2_EXPORT
+bool MP4GetStringProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    const char**  retvalue );
+
+MP4V2_EXPORT
+bool MP4GetBytesProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    uint8_t**     ppValue,
+    uint32_t*     pValueSize );
+
+MP4V2_EXPORT
+bool MP4SetIntegerProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    int64_t       value );
+
+MP4V2_EXPORT
+bool MP4SetFloatProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    float         value );
+
+MP4V2_EXPORT
+bool MP4SetStringProperty(
+    MP4FileHandle hFile,
+    const char*   propName,
+    const char*   value );
+
+MP4V2_EXPORT
+bool MP4SetBytesProperty(
+    MP4FileHandle  hFile,
+    const char*    propName,
+    const uint8_t* pValue,
+    uint32_t       valueSize );
+
+/* specific props */
+
+MP4V2_EXPORT
+MP4Duration MP4GetDuration( MP4FileHandle hFile );
+
+/** Get the time scale of the movie (file).
+ *
+ *  MP4GetTimeScale returns the time scale in units of ticks per second for
+ *  the mp4 file. Caveat: tracks may use the same time scale as the movie
+ *  or may use their own time scale.
+ *
+ *  @param hFile handle of file for operation.
+ *
+ *  @return timescale (ticks per second) of the mp4 file.
+ */
+MP4V2_EXPORT
+uint32_t MP4GetTimeScale( MP4FileHandle hFile );
+
+/** Set the time scale of the movie (file).
+ *
+ *  MP4SetTimeScale sets the time scale of the mp4 file. The time scale is
+ *  in the number of clock ticks per second. Caveat:  tracks may use the
+ *  same time scale as the movie or may use their own time scale.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param value desired timescale for the movie.
+ *
+ *  @return On success, true. On failure, false.
+ */
+MP4V2_EXPORT
+bool MP4SetTimeScale( MP4FileHandle hFile, uint32_t value );
+
+/** Change the general timescale of file hFile.
+ *
+ *  This function changes the general timescale of the file <b>hFile</b>
+ *  to the new timescale <b>value</b> by recalculating all values that depend
+ *  on the timescale in "moov.mvhd".
+ *
+ *  If the timescale is already equal to value nothing is done.
+ *
+ *  @param hFile handle of file to change.
+ *  @param value the new timescale.
+ */
+MP4V2_EXPORT
+void MP4ChangeMovieTimeScale( MP4FileHandle hFile, uint32_t value );
+
+MP4V2_EXPORT
+uint8_t MP4GetODProfileLevel( MP4FileHandle hFile );
+
+MP4V2_EXPORT
+bool MP4SetODProfileLevel( MP4FileHandle hFile, uint8_t value );
+
+MP4V2_EXPORT
+uint8_t MP4GetSceneProfileLevel( MP4FileHandle hFile );
+
+MP4V2_EXPORT
+bool MP4SetSceneProfileLevel( MP4FileHandle hFile, uint8_t value );
+
+MP4V2_EXPORT
+uint8_t MP4GetVideoProfileLevel(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId DEFAULT(MP4_INVALID_TRACK_ID) );
+
+MP4V2_EXPORT
+void MP4SetVideoProfileLevel( MP4FileHandle hFile, uint8_t value );
+
+MP4V2_EXPORT
+uint8_t MP4GetAudioProfileLevel( MP4FileHandle hFile );
+
+MP4V2_EXPORT
+void MP4SetAudioProfileLevel( MP4FileHandle hFile, uint8_t value );
+
+MP4V2_EXPORT
+uint8_t MP4GetGraphicsProfileLevel( MP4FileHandle hFile );
+
+MP4V2_EXPORT
+bool MP4SetGraphicsProfileLevel( MP4FileHandle hFile, uint8_t value );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_FILE_PROP_H */
diff --git a/include/mp4v2/general.h b/include/mp4v2/general.h
new file mode 100644
index 0000000..f9713bf
--- /dev/null
+++ b/include/mp4v2/general.h
@@ -0,0 +1,495 @@
+#ifndef MP4V2_GENERAL_H
+#define MP4V2_GENERAL_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_general MP4v2 General
+ *  @{
+ *
+ *****************************************************************************/
+
+/* MP4 API types */
+typedef void*       MP4FileHandle;
+typedef uint32_t    MP4TrackId;
+typedef uint32_t    MP4SampleId;
+typedef uint64_t    MP4Timestamp;
+typedef uint64_t    MP4Duration;
+typedef uint32_t    MP4EditId;
+
+typedef enum {
+    MP4_LOG_NONE = 0,
+    MP4_LOG_ERROR = 1,
+    MP4_LOG_WARNING = 2,
+    MP4_LOG_INFO = 3,
+    MP4_LOG_VERBOSE1 = 4,
+    MP4_LOG_VERBOSE2 = 5,
+    MP4_LOG_VERBOSE3 = 6,
+    MP4_LOG_VERBOSE4 = 7
+}  MP4LogLevel;
+
+/*****************************************************************************/
+
+typedef void (*MP4LogCallback)(
+    MP4LogLevel loglevel,
+    const char* fmt,
+    va_list     ap );
+
+/*****************************************************************************/
+
+/** Encryption function pointer.
+ *
+ * @see MP4EncAndCopySample().
+ * @see MP4EncAndCopyTrack().
+ */
+typedef uint32_t (*encryptFunc_t)( uint32_t, uint32_t, uint8_t*, uint32_t*, uint8_t** );
+
+/*****************************************************************************/
+
+#define MP4_INVALID_FILE_HANDLE ((MP4FileHandle)NULL) /**< Constant: invalid MP4FileHandle. */
+#define MP4_INVALID_TRACK_ID    ((MP4TrackId)0)       /**< Constant: invalid MP4TrackId. */
+#define MP4_INVALID_SAMPLE_ID   ((MP4SampleId)0)      /**< Constant: invalid MP4SampleId. */
+#define MP4_INVALID_TIMESTAMP   ((MP4Timestamp)-1)    /**< Constant: invalid MP4Timestamp. */
+#define MP4_INVALID_DURATION    ((MP4Duration)-1)     /**< Constant: invalid MP4Duration. */
+#define MP4_INVALID_EDIT_ID     ((MP4EditId)0)        /**< Constant: invalid MP4EditId. */
+
+/* Macros to test for API type validity */
+#define MP4_IS_VALID_FILE_HANDLE(x) ((x) != MP4_INVALID_FILE_HANDLE)
+#define MP4_IS_VALID_TRACK_ID(x)    ((x) != MP4_INVALID_TRACK_ID)
+#define MP4_IS_VALID_SAMPLE_ID(x)   ((x) != MP4_INVALID_SAMPLE_ID)
+#define MP4_IS_VALID_TIMESTAMP(x)   ((x) != MP4_INVALID_TIMESTAMP)
+#define MP4_IS_VALID_DURATION(x)    ((x) != MP4_INVALID_DURATION)
+#define MP4_IS_VALID_EDIT_ID(x)     ((x) != MP4_INVALID_EDIT_ID)
+
+/*
+ * MP4 Known track type names - e.g. MP4GetNumberOfTracks(type)
+ *
+ * Note this first group of track types should be created
+ * via the MP4Add<Type>Track() functions, and not MP4AddTrack(type)
+ */
+#define MP4_OD_TRACK_TYPE       "odsm"  /**< Constant: OD track. */
+#define MP4_SCENE_TRACK_TYPE    "sdsm"  /**< Constant: scene track. */
+#define MP4_AUDIO_TRACK_TYPE    "soun"  /**< Constant: audio track. */
+#define MP4_VIDEO_TRACK_TYPE    "vide"  /**< Constant: video track. */
+#define MP4_HINT_TRACK_TYPE     "hint"  /**< Constant: hint track. */
+#define MP4_CNTL_TRACK_TYPE     "cntl"  /**< Constant: control track. */
+#define MP4_TEXT_TRACK_TYPE     "text"  /**< Constant: text track. */
+#define MP4_SUBTITLE_TRACK_TYPE "sbtl"  /**< Constant: subtitle track. */
+#define MP4_SUBPIC_TRACK_TYPE   "subp"  /**< Constant: subpic track. */
+/*
+ * This second set of track types should be created
+ * via MP4AddSystemsTrack(type)
+ */
+#define MP4_CLOCK_TRACK_TYPE    "crsm"  /**< Constant: clock track. */
+#define MP4_MPEG7_TRACK_TYPE    "m7sm"  /**< Constant: mpeg7 track. */
+#define MP4_OCI_TRACK_TYPE      "ocsm"  /**< Constant: OCI track. */
+#define MP4_IPMP_TRACK_TYPE     "ipsm"  /**< Constant: IPMP track. */
+#define MP4_MPEGJ_TRACK_TYPE    "mjsm"  /**< Constant: MPEGJ track. */
+
+#define MP4_IS_VIDEO_TRACK_TYPE(type) \
+    (!strcasecmp(type, MP4_VIDEO_TRACK_TYPE))
+
+#define MP4_IS_AUDIO_TRACK_TYPE(type) \
+    (!strcasecmp(type, MP4_AUDIO_TRACK_TYPE))
+
+#define MP4_IS_CNTL_TRACK_TYPE(type) \
+        (!strcasecmp(type, MP4_CNTL_TRACK_TYPE))
+
+#define MP4_IS_OD_TRACK_TYPE(type) \
+    (!strcasecmp(type, MP4_OD_TRACK_TYPE))
+
+#define MP4_IS_SCENE_TRACK_TYPE(type) \
+    (!strcasecmp(type, MP4_SCENE_TRACK_TYPE))
+
+#define MP4_IS_HINT_TRACK_TYPE(type) \
+    (!strcasecmp(type, MP4_HINT_TRACK_TYPE))
+
+#define MP4_IS_SYSTEMS_TRACK_TYPE(type) \
+    (!strcasecmp(type, MP4_CLOCK_TRACK_TYPE) \
+    || !strcasecmp(type, MP4_MPEG7_TRACK_TYPE) \
+    || !strcasecmp(type, MP4_OCI_TRACK_TYPE) \
+    || !strcasecmp(type, MP4_IPMP_TRACK_TYPE) \
+    || !strcasecmp(type, MP4_MPEGJ_TRACK_TYPE))
+
+/* MP4 Audio track types - see MP4AddAudioTrack()*/
+#define MP4_INVALID_AUDIO_TYPE          0x00
+#define MP4_MPEG1_AUDIO_TYPE            0x6B
+#define MP4_MPEG2_AUDIO_TYPE            0x69
+#define MP4_MP3_AUDIO_TYPE              MP4_MPEG2_AUDIO_TYPE
+#define MP4_MPEG2_AAC_MAIN_AUDIO_TYPE   0x66
+#define MP4_MPEG2_AAC_LC_AUDIO_TYPE     0x67
+#define MP4_MPEG2_AAC_SSR_AUDIO_TYPE    0x68
+#define MP4_MPEG2_AAC_AUDIO_TYPE        MP4_MPEG2_AAC_MAIN_AUDIO_TYPE
+#define MP4_MPEG4_AUDIO_TYPE            0x40
+#define MP4_PRIVATE_AUDIO_TYPE          0xC0
+#define MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE  0xE0 /* a private definition */
+#define MP4_VORBIS_AUDIO_TYPE           0xE1 /* a private definition */
+#define MP4_AC3_AUDIO_TYPE              0xE2 /* a private definition */
+#define MP4_ALAW_AUDIO_TYPE             0xE3 /* a private definition */
+#define MP4_ULAW_AUDIO_TYPE             0xE4 /* a private definition */
+#define MP4_G723_AUDIO_TYPE             0xE5 /* a private definition */
+#define MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE 0xE6 /* a private definition */
+
+/* MP4 MPEG-4 Audio types from 14496-3 Table 1.5.1 */
+#define MP4_MPEG4_INVALID_AUDIO_TYPE        0
+#define MP4_MPEG4_AAC_MAIN_AUDIO_TYPE       1
+#define MP4_MPEG4_AAC_LC_AUDIO_TYPE         2
+#define MP4_MPEG4_AAC_SSR_AUDIO_TYPE        3
+#define MP4_MPEG4_AAC_LTP_AUDIO_TYPE        4
+#define MP4_MPEG4_AAC_HE_AUDIO_TYPE         5
+#define MP4_MPEG4_AAC_SCALABLE_AUDIO_TYPE   6
+#define MP4_MPEG4_CELP_AUDIO_TYPE           8
+#define MP4_MPEG4_HVXC_AUDIO_TYPE           9
+#define MP4_MPEG4_TTSI_AUDIO_TYPE           12
+#define MP4_MPEG4_MAIN_SYNTHETIC_AUDIO_TYPE 13
+#define MP4_MPEG4_WAVETABLE_AUDIO_TYPE      14
+#define MP4_MPEG4_MIDI_AUDIO_TYPE           15
+#define MP4_MPEG4_ALGORITHMIC_FX_AUDIO_TYPE 16
+#define MP4_MPEG4_ALS_AUDIO_TYPE            31
+#define MP4_MPEG4_LAYER1_AUDIO_TYPE         32
+#define MP4_MPEG4_LAYER2_AUDIO_TYPE         33
+#define MP4_MPEG4_LAYER3_AUDIO_TYPE         34
+#define MP4_MPEG4_SLS_AUDIO_TYPE            35
+
+/* MP4 Audio type utilities following common usage */
+#define MP4_IS_MP3_AUDIO_TYPE(type) \
+    ((type) == MP4_MPEG1_AUDIO_TYPE || (type) == MP4_MPEG2_AUDIO_TYPE)
+
+#define MP4_IS_MPEG2_AAC_AUDIO_TYPE(type) \
+    (((type) >= MP4_MPEG2_AAC_MAIN_AUDIO_TYPE \
+        && (type) <= MP4_MPEG2_AAC_SSR_AUDIO_TYPE))
+
+#define MP4_IS_MPEG4_AAC_AUDIO_TYPE(mpeg4Type) \
+    (((mpeg4Type) >= MP4_MPEG4_AAC_MAIN_AUDIO_TYPE \
+        && (mpeg4Type) <= MP4_MPEG4_AAC_HE_AUDIO_TYPE) \
+      || (mpeg4Type) == MP4_MPEG4_AAC_SCALABLE_AUDIO_TYPE \
+          || (mpeg4Type) == 17)
+
+#define MP4_IS_AAC_AUDIO_TYPE(type) \
+    (MP4_IS_MPEG2_AAC_AUDIO_TYPE(type) \
+    || (type) == MP4_MPEG4_AUDIO_TYPE)
+
+/* MP4 Video track types - see MP4AddVideoTrack() */
+#define MP4_INVALID_VIDEO_TYPE          0x00
+#define MP4_MPEG1_VIDEO_TYPE            0x6A
+#define MP4_MPEG2_SIMPLE_VIDEO_TYPE     0x60
+#define MP4_MPEG2_MAIN_VIDEO_TYPE       0x61
+#define MP4_MPEG2_SNR_VIDEO_TYPE        0x62
+#define MP4_MPEG2_SPATIAL_VIDEO_TYPE    0x63
+#define MP4_MPEG2_HIGH_VIDEO_TYPE       0x64
+#define MP4_MPEG2_442_VIDEO_TYPE        0x65
+#define MP4_MPEG2_VIDEO_TYPE            MP4_MPEG2_MAIN_VIDEO_TYPE
+#define MP4_MPEG4_VIDEO_TYPE            0x20
+#define MP4_JPEG_VIDEO_TYPE             0x6C
+#define MP4_PRIVATE_VIDEO_TYPE          0xD0
+#define MP4_YUV12_VIDEO_TYPE            0xF0    /* a private definition */
+#define MP4_H263_VIDEO_TYPE             0xF2    /* a private definition */
+#define MP4_H261_VIDEO_TYPE             0xF3    /* a private definition */
+
+/* MP4 Video type utilities */
+#define MP4_IS_MPEG1_VIDEO_TYPE(type) \
+    ((type) == MP4_MPEG1_VIDEO_TYPE)
+
+#define MP4_IS_MPEG2_VIDEO_TYPE(type) \
+    (((type) >= MP4_MPEG2_SIMPLE_VIDEO_TYPE \
+        && (type) <= MP4_MPEG2_442_VIDEO_TYPE) \
+      || MP4_IS_MPEG1_VIDEO_TYPE(type))
+
+#define MP4_IS_MPEG4_VIDEO_TYPE(type) \
+    ((type) == MP4_MPEG4_VIDEO_TYPE)
+
+/* Mpeg4 Visual Profile Defines - ISO/IEC 14496-2:2001/Amd.2:2002(E) */
+#define MPEG4_SP_L1 (0x1)
+#define MPEG4_SP_L2 (0x2)
+#define MPEG4_SP_L3 (0x3)
+#define MPEG4_SP_L0 (0x8)
+#define MPEG4_SSP_L1 (0x11)
+#define MPEG4_SSP_L2 (0x12)
+#define MPEG4_CP_L1 (0x21)
+#define MPEG4_CP_L2 (0x22)
+#define MPEG4_MP_L2 (0x32)
+#define MPEG4_MP_L3 (0x33)
+#define MPEG4_MP_L4 (0x34)
+#define MPEG4_NBP_L2 (0x42)
+#define MPEG4_STP_L1 (0x51)
+#define MPEG4_SFAP_L1 (0x61)
+#define MPEG4_SFAP_L2 (0x62)
+#define MPEG4_SFBAP_L1 (0x63)
+#define MPEG4_SFBAP_L2 (0x64)
+#define MPEG4_BATP_L1 (0x71)
+#define MPEG4_BATP_L2 (0x72)
+#define MPEG4_HP_L1 (0x81)
+#define MPEG4_HP_L2 (0x82)
+#define MPEG4_ARTSP_L1 (0x91)
+#define MPEG4_ARTSP_L2 (0x92)
+#define MPEG4_ARTSP_L3 (0x93)
+#define MPEG4_ARTSP_L4 (0x94)
+#define MPEG4_CSP_L1 (0xa1)
+#define MPEG4_CSP_L2 (0xa2)
+#define MPEG4_CSP_L3 (0xa3)
+#define MPEG4_ACEP_L1 (0xb1)
+#define MPEG4_ACEP_L2 (0xb2)
+#define MPEG4_ACEP_L3 (0xb3)
+#define MPEG4_ACEP_L4 (0xb4)
+#define MPEG4_ACP_L1 (0xc1)
+#define MPEG4_ACP_L2 (0xc2)
+#define MPEG4_AST_L1 (0xd1)
+#define MPEG4_AST_L2 (0xd2)
+#define MPEG4_AST_L3 (0xd3)
+#define MPEG4_S_STUDIO_P_L1 (0xe1)
+#define MPEG4_S_STUDIO_P_L2 (0xe2)
+#define MPEG4_S_STUDIO_P_L3 (0xe3)
+#define MPEG4_S_STUDIO_P_L4 (0xe4)
+#define MPEG4_C_STUDIO_P_L1 (0xe5)
+#define MPEG4_C_STUDIO_P_L2 (0xe6)
+#define MPEG4_C_STUDIO_P_L3 (0xe7)
+#define MPEG4_C_STUDIO_P_L4 (0xe8)
+#define MPEG4_ASP_L0 (0xF0)
+#define MPEG4_ASP_L1 (0xF1)
+#define MPEG4_ASP_L2 (0xF2)
+#define MPEG4_ASP_L3 (0xF3)
+#define MPEG4_ASP_L4 (0xF4)
+#define MPEG4_ASP_L5 (0xF5)
+#define MPEG4_ASP_L3B (0xF7)
+#define MPEG4_FGSP_L0 (0xf8)
+#define MPEG4_FGSP_L1 (0xf9)
+#define MPEG4_FGSP_L2 (0xfa)
+#define MPEG4_FGSP_L3 (0xfb)
+#define MPEG4_FGSP_L4 (0xfc)
+#define MPEG4_FGSP_L5 (0xfd)
+
+/*****************************************************************************/
+
+/* 3GP specific utilities */
+
+MP4V2_EXPORT
+bool MP4Make3GPCompliant(
+    const char* fileName,
+    char*       majorBrand DEFAULT(0),
+    uint32_t    minorVersion DEFAULT(0),
+    char**      supportedBrands DEFAULT(NULL),
+    uint32_t    supportedBrandsCount DEFAULT(0),
+    bool        deleteIodsAtom DEFAULT(true) );
+
+/* NOTE this section of functionality has not yet been fully tested */
+
+MP4V2_EXPORT
+MP4EditId MP4AddTrackEdit(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId DEFAULT(MP4_INVALID_EDIT_ID),
+    MP4Timestamp  startTime DEFAULT(0),
+    MP4Duration   duration DEFAULT(0),
+    bool          dwell DEFAULT(false) );
+
+MP4V2_EXPORT
+bool MP4DeleteTrackEdit(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId );
+
+MP4V2_EXPORT
+uint32_t MP4GetTrackNumberOfEdits(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+MP4Timestamp MP4GetTrackEditStart(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId );
+
+MP4V2_EXPORT
+MP4Duration MP4GetTrackEditTotalDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId DEFAULT(MP4_INVALID_EDIT_ID) );
+
+MP4V2_EXPORT
+MP4Timestamp MP4GetTrackEditMediaStart(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId );
+
+MP4V2_EXPORT
+bool MP4SetTrackEditMediaStart(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId,
+    MP4Timestamp  startTime );
+
+MP4V2_EXPORT
+MP4Duration MP4GetTrackEditDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId );
+
+MP4V2_EXPORT
+bool MP4SetTrackEditDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId,
+    MP4Duration   duration );
+
+MP4V2_EXPORT
+int8_t MP4GetTrackEditDwell(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId );
+
+MP4V2_EXPORT
+bool MP4SetTrackEditDwell(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4EditId     editId,
+    bool          dwell );
+
+MP4V2_EXPORT
+bool MP4ReadSampleFromEditTime(
+    /* input parameters */
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Timestamp  when,
+    /* input/output parameters */
+    uint8_t** ppBytes,
+    uint32_t* pNumBytes,
+    /* output parameters */
+    MP4Timestamp* pStartTime DEFAULT(NULL),
+    MP4Duration*  pDuration DEFAULT(NULL),
+    MP4Duration*  pRenderingOffset DEFAULT(NULL),
+    bool*         pIsSyncSample DEFAULT(NULL) );
+
+MP4V2_EXPORT
+MP4SampleId MP4GetSampleIdFromEditTime(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Timestamp  when,
+    MP4Timestamp* pStartTime DEFAULT(NULL),
+    MP4Duration*  pDuration DEFAULT(NULL) );
+
+/* time conversion utilties */
+
+/* predefined values for timeScale parameter below */
+#define MP4_SECONDS_TIME_SCALE      1
+#define MP4_MILLISECONDS_TIME_SCALE 1000
+#define MP4_MICROSECONDS_TIME_SCALE 1000000
+#define MP4_NANOSECONDS_TIME_SCALE  1000000000
+
+#define MP4_SECS_TIME_SCALE     MP4_SECONDS_TIME_SCALE
+#define MP4_MSECS_TIME_SCALE    MP4_MILLISECONDS_TIME_SCALE
+#define MP4_USECS_TIME_SCALE    MP4_MICROSECONDS_TIME_SCALE
+#define MP4_NSECS_TIME_SCALE    MP4_NANOSECONDS_TIME_SCALE
+
+MP4V2_EXPORT
+uint64_t MP4ConvertFromMovieDuration(
+    MP4FileHandle hFile,
+    MP4Duration   duration,
+    uint32_t      timeScale );
+
+MP4V2_EXPORT
+uint64_t MP4ConvertFromTrackTimestamp(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Timestamp  timeStamp,
+    uint32_t      timeScale );
+
+MP4V2_EXPORT
+MP4Timestamp MP4ConvertToTrackTimestamp(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint64_t      timeStamp,
+    uint32_t      timeScale );
+
+/** Convert duration from track time scale to an arbitrary time scale.
+ *
+ *  MP4ConvertFromTrackDuration converts a duration such as a sample duration
+ *  from the track time scale to another time scale. This can be used by a
+ *  player application to map all track samples to a common time scale.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param duration value to be converted.
+ *  @param timeScale time scale in ticks per second.
+ *
+ *  @return On success, the duration in arbitrary time scale units.
+ *      On error, <b>0</b>.
+ *
+ *  @see MP4GetSampleDuration().
+ *  @see MP4ConvertToTrackDuration().
+ */
+MP4V2_EXPORT
+uint64_t MP4ConvertFromTrackDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Duration   duration,
+    uint32_t      timeScale );
+
+/** Convert duration from arbitrary time scale to track time scale.
+ *
+ *  MP4ConvertToTrackDuration converts a duration such as a sample duration
+ *  from the specified time scale to the track time scale.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param duration value to be converted.
+ *  @param timeScale time scale in ticks per second.
+ *
+ *  @return On success, the duration in track time scale units.
+ *      On error, #MP4_INVALID_DURATION.
+ *
+ *  @see MP4ConvertFromTrackDuration().
+ */
+MP4V2_EXPORT
+MP4Duration MP4ConvertToTrackDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint64_t      duration,
+    uint32_t      timeScale );
+
+MP4V2_EXPORT
+char* MP4BinaryToBase16(
+    const uint8_t* pData,
+    uint32_t       dataSize );
+
+MP4V2_EXPORT
+char* MP4BinaryToBase64(
+    const uint8_t* pData,
+    uint32_t       dataSize );
+
+MP4V2_EXPORT
+uint8_t* Base64ToBinary(
+    const char* pData,
+    uint32_t    decodeSize,
+    uint32_t*   pDataSize );
+
+MP4V2_EXPORT
+void MP4Free(
+    void* p );
+
+/** Set the function to call in place of default logging behavior
+ *
+ * @param cb_func the function to call
+ */
+MP4V2_EXPORT
+void MP4SetLogCallback(
+    MP4LogCallback cb_func );
+/** @} ***********************************************************************/
+
+/**
+ * Accessor for the maximum level for diagnostic information
+ *
+ * @return the maximum level for diagnostic information
+ *
+ * @see MP4LogSetLevel() for further details.
+ */
+MP4V2_EXPORT
+MP4LogLevel MP4LogGetLevel( void );
+
+/**
+ * Set the maximum level for diagnostic information
+ *
+ * @param verbosity the level to set
+ */
+MP4V2_EXPORT
+void MP4LogSetLevel( MP4LogLevel verbosity );
+
+#endif /* MP4V2_GENERAL_H */
diff --git a/include/mp4v2/isma.h b/include/mp4v2/isma.h
new file mode 100644
index 0000000..074a06e
--- /dev/null
+++ b/include/mp4v2/isma.h
@@ -0,0 +1,96 @@
+#ifndef MP4V2_ISMA_H
+#define MP4V2_ISMA_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_isma MP4v2 ISMA (Internet Streaming Media Alliance)
+ *  @{
+ *
+ *****************************************************************************/
+
+/** something */
+typedef struct mp4v2_ismacryp_session_params {
+    uint32_t    scheme_type;
+    uint16_t    scheme_version;
+    uint8_t     key_ind_len;
+    uint8_t     iv_len;
+    uint8_t     selective_enc;
+    const char* kms_uri;
+} mp4v2_ismacrypParams;
+
+/*
+ * API to initialize ismacryp properties to sensible defaults
+ * if input param is null then mallocs a params struct
+ */
+
+MP4V2_EXPORT
+mp4v2_ismacrypParams* MP4DefaultISMACrypParams( mp4v2_ismacrypParams* ptr );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddEncAudioTrack(
+    MP4FileHandle         hFile,
+    uint32_t              timeScale,
+    MP4Duration           sampleDuration,
+    mp4v2_ismacrypParams* icPp,
+    uint8_t               audioType DEFAULT(MP4_MPEG4_AUDIO_TYPE) );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddEncVideoTrack(
+    MP4FileHandle         hFile,
+    uint32_t              timeScale,
+    MP4Duration           sampleDuration,
+    uint16_t              width,
+    uint16_t              height,
+    mp4v2_ismacrypParams* icPp,
+    uint8_t               videoType DEFAULT(MP4_MPEG4_VIDEO_TYPE),
+    const char*           oFormat DEFAULT(NULL) );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddEncH264VideoTrack(
+    MP4FileHandle         dstFile,
+    uint32_t              timeScale,
+    MP4Duration           sampleDuration,
+    uint16_t              width,
+    uint16_t              height,
+    MP4FileHandle         srcFile,
+    MP4TrackId            srcTrackId,
+    mp4v2_ismacrypParams* icPp );
+
+MP4V2_EXPORT
+MP4TrackId MP4EncAndCloneTrack(
+    MP4FileHandle         srcFile,
+    MP4TrackId            srcTrackId,
+    mp4v2_ismacrypParams* icPp,
+    MP4FileHandle         dstFile DEFAULT(MP4_INVALID_FILE_HANDLE),
+    MP4TrackId            dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) );
+
+MP4V2_EXPORT
+MP4TrackId MP4EncAndCopyTrack(
+    MP4FileHandle         srcFile,
+    MP4TrackId            srcTrackId,
+    mp4v2_ismacrypParams* icPp,
+    encryptFunc_t         encfcnp,
+    uint32_t              encfcnparam1,
+    MP4FileHandle         dstFile DEFAULT(MP4_INVALID_FILE_HANDLE),
+    bool                  applyEdits DEFAULT(false),
+    MP4TrackId            dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) );
+
+MP4V2_EXPORT
+bool MP4MakeIsmaCompliant(
+    const char* fileName,
+    bool        addIsmaComplianceSdp DEFAULT(true) );
+
+MP4V2_EXPORT
+char* MP4MakeIsmaSdpIod(
+    uint8_t  videoProfile,
+    uint32_t videoBitrate,
+    uint8_t* videoConfig,
+    uint32_t videoConfigLength,
+    uint8_t  audioProfile,
+    uint32_t audioBitrate,
+    uint8_t* audioConfig,
+    uint32_t audioConfigLength );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_ISMA_H */
diff --git a/include/mp4v2/itmf_generic.h b/include/mp4v2/itmf_generic.h
new file mode 100644
index 0000000..b780b1e
--- /dev/null
+++ b/include/mp4v2/itmf_generic.h
@@ -0,0 +1,207 @@
+#ifndef MP4V2_ITMF_GENERIC_H
+#define MP4V2_ITMF_GENERIC_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_itmf_generic MP4v2 iTMF (iTunes Metadata Format) Generic
+ *  @{
+ *
+ *  This is a low-level API used to manage iTMF metadata.
+ *
+ *  It provides support for virtually any kind of iTMF metadata item,
+ *  including meaning atoms, sometimes referred to as reverse-DNS meanings.
+ *  Structures are directly modified; ie: there are no fuctions which
+ *  modify values for you. There is little type-safety, logic checks, or
+ *  specifications compliance checks. For these reasons it is recommended
+ *  to use iTMF Tags API when possible.
+ *
+ *  At the heart of this API is an #MP4ItmfItem which corresponds to an
+ *  iTMF metadata item atom. The item, and any recursive data structures
+ *  contained within require <b>manual</b> memory management. The general
+ *  rule to follow is that you must always check/free a ptr if you intend
+ *  to resize data. In cases where you know the existing data size is
+ *  exactly what is needed, you may overwrite the buffer contents.
+ *
+ *  Each item always has at least 1 data elements which corresponds to
+ *  a data atom. Additionally, each item has optional <b>mean</b> and
+ *  <b>name</b> values which correspond to mean and name atoms.
+ *
+ *  Each #MP4ItmfItem has a list of #MP4ItmfData. Similarily, care must
+ *  be taken to manage memory with one key difference; these structures
+ *  also have a valueSize field. If value is NULL then set valueSize=0.
+ *  Otherwise, set valueSize to the size (in bytes) of value buffer.
+ *
+ *  In rare cases where the number of data elements in a single item
+ *  is > 1, the user must manually free/alloc/copy the <b>elements</b>
+ *  buffer and update <b>size</b> accordingly.
+ *
+ *  The mp4 file structure is modified only when MP4AddItem(),
+ *  MP4SetItem() and MP4RemoveItem() are used. Simply free'ing
+ *  the item list does not modify the mp4 file.
+ *
+ *  <b>iTMF Generic read workflow:</b>
+ *
+ *      @li MP4ItmfGetItems()
+ *      @li inspect each item...
+ *      @li MP4ItmfItemListFree()
+ *
+ *  <b>iTMF Generic read/modify/remove workflow:</b>
+ *
+ *      @li MP4ItmfGetItems()
+ *      @li inspect/modify item...
+ *      @li MP4ItmfSetItem() each modified item...
+ *      @li MP4ItmfRemoveItem()...
+ *      @li MP4ItmfItemListFree()
+ *
+ *  <b>iTMF Generic add workflow:</b>
+ *
+ *      @li MP4ItmfItemAlloc()
+ *      @li MP4ItmfAddItem()
+ *      @li MP4ItmfItemFree()
+ *
+ *  @par Warning:
+ *  Care must be taken when using multiple mechanisms to modify an open mp4
+ *  file as it is not thread-safe, nor does it permit overlapping different
+ *  API workflows which have a begin/end to their workflow. That is to say
+ *  do not interleave an iTMF Generic workflow with an iTMF Tags workflow.
+ *
+ *****************************************************************************/
+
+/** Basic types of value data as enumerated in spec. */
+typedef enum MP4ItmfBasicType_e
+{
+    MP4_ITMF_BT_IMPLICIT  = 0,   /**< for use with tags for which no type needs to be indicated */
+    MP4_ITMF_BT_UTF8      = 1,   /**< without any count or null terminator */
+    MP4_ITMF_BT_UTF16     = 2,   /**< also known as UTF-16BE */
+    MP4_ITMF_BT_SJIS      = 3,   /**< deprecated unless it is needed for special Japanese characters */
+    MP4_ITMF_BT_HTML      = 6,   /**< the HTML file header specifies which HTML version */
+    MP4_ITMF_BT_XML       = 7,   /**< the XML header must identify the DTD or schemas */
+    MP4_ITMF_BT_UUID      = 8,   /**< also known as GUID; stored as 16 bytes in binary (valid as an ID) */
+    MP4_ITMF_BT_ISRC      = 9,   /**< stored as UTF-8 text (valid as an ID) */
+    MP4_ITMF_BT_MI3P      = 10,  /**< stored as UTF-8 text (valid as an ID) */
+    MP4_ITMF_BT_GIF       = 12,  /**< (deprecated) a GIF image */
+    MP4_ITMF_BT_JPEG      = 13,  /**< a JPEG image */
+    MP4_ITMF_BT_PNG       = 14,  /**< a PNG image */
+    MP4_ITMF_BT_URL       = 15,  /**< absolute, in UTF-8 characters */
+    MP4_ITMF_BT_DURATION  = 16,  /**< in milliseconds, 32-bit integer */
+    MP4_ITMF_BT_DATETIME  = 17,  /**< in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits */
+    MP4_ITMF_BT_GENRES    = 18,  /**< a list of enumerated values */
+    MP4_ITMF_BT_INTEGER   = 21,  /**< a signed big-endian integer with length one of { 1,2,3,4,8 } bytes */
+    MP4_ITMF_BT_RIAA_PA   = 24,  /**< RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger */
+    MP4_ITMF_BT_UPC       = 25,  /**< Universal Product Code, in text UTF-8 format (valid as an ID) */
+    MP4_ITMF_BT_BMP       = 27,  /**< Windows bitmap image */
+
+    MP4_ITMF_BT_UNDEFINED = 255  /**< undefined */
+} MP4ItmfBasicType;
+
+/** Data structure.
+ *  Models an iTMF data atom contained in an iTMF metadata item atom.
+ */
+typedef struct MP4ItmfData_s
+{
+    uint8_t          typeSetIdentifier; /**< always zero. */
+    MP4ItmfBasicType typeCode;          /**< iTMF basic type. */
+    uint32_t         locale;            /**< always zero. */
+    uint8_t*         value;             /**< may be NULL. */
+    uint32_t         valueSize;         /**< value size in bytes. */
+} MP4ItmfData;
+
+/** List of data. */
+typedef struct MP4ItmfDataList_s
+{
+    MP4ItmfData* elements; /**< flat array. NULL when size is zero. */
+    uint32_t     size;     /**< number of elements. */
+} MP4ItmfDataList;
+
+/** Item structure.
+ *  Models an iTMF metadata item atom contained in an ilst atom.
+ */
+typedef struct MP4ItmfItem_s
+{
+    void* __handle; /**< internal use only. */
+
+    char*           code;     /**< four-char code identifing atom type. NULL-terminated. */
+    char*           mean;     /**< may be NULL. UTF-8 meaning. NULL-terminated. */
+    char*           name;     /**< may be NULL. UTF-8 name. NULL-terminated. */
+    MP4ItmfDataList dataList; /**< list of data. can be zero length. */
+} MP4ItmfItem;
+
+/** List of items. */
+typedef struct MP4ItmfItemList_s
+{
+    MP4ItmfItem* elements; /**< flat array. NULL when size is zero. */
+    uint32_t     size;     /**< number of elements. */
+} MP4ItmfItemList;
+
+/** Allocate an item on the heap.
+ *  @param code four-char code identifying atom type. NULL-terminated.
+ *  @param numData number of data elements to allocate. Must be >= 1.
+ *  @return newly allocated item.
+ */
+MP4V2_EXPORT MP4ItmfItem*
+MP4ItmfItemAlloc( const char* code, uint32_t numData );
+
+/** Free an item (deep free).
+ *  @param item to be free'd.
+ */
+MP4V2_EXPORT void
+MP4ItmfItemFree( MP4ItmfItem* item );
+
+/** Free an item list (deep free).
+ *  @param itemList to be free'd.
+ */
+MP4V2_EXPORT void
+MP4ItmfItemListFree( MP4ItmfItemList* itemList );
+
+/** Get list of all items from file.
+ *  @param hFile handle of file to operate on.
+ *  @return On succes, list of items, which must be free'd. On failure, NULL.
+ */
+MP4V2_EXPORT MP4ItmfItemList*
+MP4ItmfGetItems( MP4FileHandle hFile );
+
+/** Get list of items by code from file.
+ *  @param hFile handle of file to operate on.
+ *  @param code four-char code identifying atom type. NULL-terminated.
+ *  @return On succes, list of items, which must be free'd. On failure, NULL.
+ */
+MP4V2_EXPORT MP4ItmfItemList*
+MP4ItmfGetItemsByCode( MP4FileHandle hFile, const char* code );
+
+/** Get list of items by meaning from file.
+ *  Implicitly only returns atoms of code @b{----}.
+ *  @param hFile handle of file to operate on.
+ *  @param meaning UTF-8 meaning. NULL-terminated.
+ *  @param name may be NULL. UTF-8 name. NULL-terminated.
+ *  @return On succes, list of items, which must be free'd. On failure, NULL.
+ */
+MP4V2_EXPORT MP4ItmfItemList*
+MP4ItmfGetItemsByMeaning( MP4FileHandle hFile, const char* meaning, const char* name );
+
+/** Add an item to file.
+ *  @param hFile handle of file to operate on.
+ *  @param item object to add.
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT bool
+MP4ItmfAddItem( MP4FileHandle hFile, const MP4ItmfItem* item );
+
+/** Overwrite an existing item in file.
+ *  @param hFile handle of file to operate on.
+ *  @param item object to overwrite. Must have a valid index obtained from prior get.
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT bool
+MP4ItmfSetItem( MP4FileHandle hFile, const MP4ItmfItem* item );
+
+/** Remove an existing item from file.
+ *  @param hFile handle of file to operate on.
+ *  @param item object to remove. Must have a valid index obtained from prior get.
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT bool
+MP4ItmfRemoveItem( MP4FileHandle hFile, const MP4ItmfItem* item );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_ITMF_GENERIC_H */
diff --git a/include/mp4v2/itmf_tags.h b/include/mp4v2/itmf_tags.h
new file mode 100644
index 0000000..3115a3c
--- /dev/null
+++ b/include/mp4v2/itmf_tags.h
@@ -0,0 +1,289 @@
+#ifndef MP4V2_ITMF_TAGS_H
+#define MP4V2_ITMF_TAGS_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_itmf_tags MP4v2 iTMF (iTunes Metadata Format) Tags
+ *  @{
+ *
+ *  This is a high-level API used to manage iTMF metadata.
+ *
+ *  It provides more type-safety and simplified memory management as compared
+ *  to iTMF Generic API.
+ *
+ *  At the heart of this API is a read-only structure that holds all known
+ *  items and their current values. The value is always a pointer which if
+ *  NULL indicates its corresponding atom does not exist. Thus, one must
+ *  always check if the pointer is non-NULL before attempting to extract
+ *  its value.
+ *
+ *  The structure may not be directly modified. Instead, <b>set</b> functions
+ *  corresponding to each item are used to modify the backing-store of
+ *  the read-only structure. Setting the value ptr to NULL will effectively
+ *  remove it. Setting the value ptr to real data will immediately make a
+ *  copy of the value in the backing-store and the read-only structure
+ *  will correctly reflect the change.
+ *
+ *  The hidden data cache memory is automatically managed. Thus the user need
+ *  only guarantee the data is available during the lifetime of the set-function
+ *  call.
+ *
+ *  <b>iTMF Tags read workflow:</b>
+ *
+ *      @li MP4TagsAlloc()
+ *      @li MP4TagsFetch()
+ *      @li inspect each tag of interest...
+ *      @li MP4TagsStore() (if modified)
+ *      @li MP4TagsFree()
+ *
+ *  <b>iTMF Tags read/modify/add/remove workflow:</b>
+ *
+ *      @li MP4TagsAlloc()
+ *      @li MP4TagsFetch()
+ *      @li inspect each tag of interest...
+ *      @li MP4TagsSetName(), MP4TagsSetArtist()...
+ *      @li MP4TagsStore()
+ *      @li MP4TagsFree()
+ *
+ *  @par Warning:
+ *  Care must be taken when using multiple mechanisms to modify an open mp4
+ *  file as it is not thread-safe, nor does it permit overlapping different
+ *  API workflows which have a begin/end to their workflow. That is to say
+ *  do not interleave an iTMF Generic workflow with an iTMF Tags workflow.
+ *
+ *****************************************************************************/
+
+/** Enumeration of possible MP4TagArtwork::type values. */
+typedef enum MP4TagArtworkType_e
+{
+    MP4_ART_UNDEFINED = 0,
+    MP4_ART_BMP       = 1,
+    MP4_ART_GIF       = 2,
+    MP4_ART_JPEG      = 3,
+    MP4_ART_PNG       = 4
+} MP4TagArtworkType;
+
+/** Data object representing a single piece of artwork. */
+typedef struct MP4TagArtwork_s
+{
+    void*             data; /**< raw picture data */
+    uint32_t          size; /**< data size in bytes */
+    MP4TagArtworkType type; /**< data type */
+} MP4TagArtwork;
+
+typedef struct MP4TagTrack_s
+{
+    uint16_t index;
+    uint16_t total;
+} MP4TagTrack;
+
+typedef struct MP4TagDisk_s
+{
+    uint16_t index;
+    uint16_t total;
+} MP4TagDisk;
+
+/** Tags <b>convenience</b> structure.
+ *
+ *  This structure is used in the tags convenience API which allows for
+ *  simplified retrieval and modification of the majority of known tags.
+ *
+ *  This is a read-only structure and each tag is present if and only if the
+ *  pointer is a <b>non-NULL</b> value. The actual data is backed by a hidden
+ *  data cache which is only updated when the appropriate metadata <b>set</b>
+ *  function is used, or if MP4TagsFetch() is invoked. Thus, if other API
+ *  is used to manipulate relevent atom structure of the MP4 file, the user
+ *  is responsible for re-fetching the data in this structure.
+ */
+typedef struct MP4Tags_s
+{
+    void* __handle; /* internal use only */
+
+    const char*        name;
+    const char*        artist;
+    const char*        albumArtist; 
+    const char*        album;
+    const char*        grouping;
+    const char*        composer;
+    const char*        comments;
+    const char*        genre;
+    const uint16_t*    genreType;
+    const char*        releaseDate;
+    const MP4TagTrack* track;
+    const MP4TagDisk*  disk;
+    const uint16_t*    tempo;
+    const uint8_t*     compilation;
+
+    const char*     tvShow;
+    const char*     tvNetwork;
+    const char*     tvEpisodeID;
+    const uint32_t* tvSeason;
+    const uint32_t* tvEpisode;
+
+    const char* description;
+    const char* longDescription;
+    const char* lyrics;
+
+    const char* sortName;
+    const char* sortArtist;
+    const char* sortAlbumArtist;
+    const char* sortAlbum;
+    const char* sortComposer;
+    const char* sortTVShow;
+
+    const MP4TagArtwork* artwork;
+    uint32_t             artworkCount;
+
+    const char* copyright;
+    const char* encodingTool;
+    const char* encodedBy;
+    const char* purchaseDate;
+
+    const uint8_t* podcast;
+    const char*    keywords;  /* TODO: Needs testing */
+    const char*    category;    
+
+    const uint8_t* hdVideo;
+    const uint8_t* mediaType;
+    const uint8_t* contentRating;
+    const uint8_t* gapless;
+
+    const char*     iTunesAccount;
+    const uint8_t*  iTunesAccountType;
+    const uint32_t* iTunesCountry;
+    const uint32_t* contentID;
+    const uint32_t* artistID;
+    const uint64_t* playlistID;
+    const uint32_t* genreID;
+    const uint32_t* composerID;
+    const char*     xid;
+} MP4Tags;
+
+/** Allocate tags convenience structure for reading and settings tags.
+ *
+ *  This function allocates a new structure which represents a snapshot
+ *  of all the tags therein, tracking if the tag is missing,
+ *  or present and with value. It is the caller's responsibility to free
+ *  the structure with MP4TagsFree().
+ *
+ *  @return structure with all tags missing.
+ */
+MP4V2_EXPORT
+const MP4Tags* MP4TagsAlloc( void );
+
+/** Fetch data from mp4 file and populate structure.
+ *
+ *  The tags structure and its hidden data-cache is updated to
+ *  reflect the actual tags values found in the <b>hFile</b>.
+ *
+ *  @param tags structure to fetch (write) into.
+ *  @param hFile handle of file to fetch data from.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4TagsFetch( const MP4Tags* tags, MP4FileHandle hFile );
+
+/** Store data to mp4 file from structure.
+ *
+ *  The tags structure is pushed out to the mp4 file,
+ *  adding tags if needed, removing tags if needed, and updating
+ *  the values to modified tags.
+ *
+ *  @param tags structure to store (read) from.
+ *  @param hFile handle of file to store data to.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4TagsStore( const MP4Tags* tags, MP4FileHandle hFile );
+
+/** Free tags convenience structure.
+ *
+ *  This function frees memory associated with the structure.
+ *
+ *  @param tags structure to destroy.
+ */
+MP4V2_EXPORT
+void MP4TagsFree( const MP4Tags* tags );
+
+/** Accessor that indicates whether a tags structure
+ * contains any metadata
+ *
+ * @param tags the structure to inspect
+ *
+ * @param hasMetadata populated with false if @p tags
+ * contains no metadata, true if @p tags contains metadata
+ *
+ * @retval false error determining if @p tags contains
+ * metadata
+ *
+ * @retval true successfully determined if @p tags contains
+ * metadata
+ */
+MP4V2_EXPORT
+bool MP4TagsHasMetadata ( const MP4Tags* tags, bool *hasMetadata );
+
+MP4V2_EXPORT bool MP4TagsSetName            ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetArtist          ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetAlbumArtist     ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetAlbum           ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetGrouping        ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetComposer        ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetComments        ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetGenre           ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetGenreType       ( const MP4Tags*, const uint16_t* );
+MP4V2_EXPORT bool MP4TagsSetReleaseDate     ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetTrack           ( const MP4Tags*, const MP4TagTrack* );
+MP4V2_EXPORT bool MP4TagsSetDisk            ( const MP4Tags*, const MP4TagDisk* );
+MP4V2_EXPORT bool MP4TagsSetTempo           ( const MP4Tags*, const uint16_t* );
+MP4V2_EXPORT bool MP4TagsSetCompilation     ( const MP4Tags*, const uint8_t* );
+
+MP4V2_EXPORT bool MP4TagsSetTVShow          ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetTVNetwork       ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetTVEpisodeID     ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetTVSeason        ( const MP4Tags*, const uint32_t* );
+MP4V2_EXPORT bool MP4TagsSetTVEpisode       ( const MP4Tags*, const uint32_t* );
+
+MP4V2_EXPORT bool MP4TagsSetDescription     ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetLongDescription ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetLyrics          ( const MP4Tags*, const char* );
+
+MP4V2_EXPORT bool MP4TagsSetSortName        ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetSortArtist      ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetSortAlbumArtist ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetSortAlbum       ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetSortComposer    ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetSortTVShow      ( const MP4Tags*, const char* );
+
+MP4V2_EXPORT bool MP4TagsAddArtwork         ( const MP4Tags*, MP4TagArtwork* );
+MP4V2_EXPORT bool MP4TagsSetArtwork         ( const MP4Tags*, uint32_t, MP4TagArtwork* );
+MP4V2_EXPORT bool MP4TagsRemoveArtwork      ( const MP4Tags*, uint32_t );
+
+MP4V2_EXPORT bool MP4TagsSetCopyright       ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetEncodingTool    ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetEncodedBy       ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetPurchaseDate    ( const MP4Tags*, const char* );
+
+MP4V2_EXPORT bool MP4TagsSetPodcast         ( const MP4Tags*, const uint8_t* );
+MP4V2_EXPORT bool MP4TagsSetKeywords        ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetCategory        ( const MP4Tags*, const char* );
+
+MP4V2_EXPORT bool MP4TagsSetHDVideo         ( const MP4Tags*, const uint8_t* );
+MP4V2_EXPORT bool MP4TagsSetMediaType       ( const MP4Tags*, const uint8_t* );
+MP4V2_EXPORT bool MP4TagsSetContentRating   ( const MP4Tags*, const uint8_t* );
+MP4V2_EXPORT bool MP4TagsSetGapless         ( const MP4Tags*, const uint8_t* );
+
+MP4V2_EXPORT bool MP4TagsSetITunesAccount     ( const MP4Tags*, const char* );
+MP4V2_EXPORT bool MP4TagsSetITunesAccountType ( const MP4Tags*, const uint8_t* );
+MP4V2_EXPORT bool MP4TagsSetITunesCountry     ( const MP4Tags*, const uint32_t* );
+MP4V2_EXPORT bool MP4TagsSetContentID         ( const MP4Tags*, const uint32_t* );
+MP4V2_EXPORT bool MP4TagsSetArtistID          ( const MP4Tags*, const uint32_t* );
+MP4V2_EXPORT bool MP4TagsSetPlaylistID        ( const MP4Tags*, const uint64_t* );
+MP4V2_EXPORT bool MP4TagsSetGenreID           ( const MP4Tags*, const uint32_t* );
+MP4V2_EXPORT bool MP4TagsSetComposerID        ( const MP4Tags*, const uint32_t* );
+MP4V2_EXPORT bool MP4TagsSetXID               ( const MP4Tags*, const char* );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_ITMF_TAGS_H */
diff --git a/include/mp4v2/mp4v2.h b/include/mp4v2/mp4v2.h
new file mode 100644
index 0000000..4d8e807
--- /dev/null
+++ b/include/mp4v2/mp4v2.h
@@ -0,0 +1,83 @@
+/*
+ * 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 - 2005.  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.
+ *
+ * Contributor(s):
+ *      Dave Mackie                 dmackie@cisco.com
+ *      Alix Marchandise-Franquet   alix@cisco.com
+ *      Ximpo Group Ltd.            mp4v2@ximpo.com
+ *      Bill May                    wmay@cisco.com
+ */
+#ifndef MP4V2_MP4V2_H
+#define MP4V2_MP4V2_H
+
+/*****************************************************************************/
+
+#include <mp4v2/platform.h>
+#include <mp4v2/project.h>
+
+/*****************************************************************************/
+
+/* exploit C++ ability of default values for function parameters */
+#if defined( DEFAULT )
+#   define __MP4V2_SAVE_DEFAULT DEFAULT
+#endif
+#undef DEFAULT
+#if defined( __cplusplus )
+#   define DEFAULT(x) =x
+#else
+#   define DEFAULT(x)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+#include <mp4v2/general.h>
+#include <mp4v2/file.h>
+#include <mp4v2/file_prop.h>
+#include <mp4v2/track.h>
+#include <mp4v2/track_prop.h>
+#include <mp4v2/sample.h>
+#include <mp4v2/chapter.h>
+#include <mp4v2/itmf_generic.h>
+#include <mp4v2/itmf_tags.h>
+#include <mp4v2/streaming.h>
+#include <mp4v2/isma.h>
+
+/*****************************************************************************/
+
+/* restore macro DEFAULT to state prior to mp4v2 headers */
+#undef DEFAULT
+#if defined( __MP4V2_SAVE_DEFAULT )
+#   define DEFAULT __MP4V2_SAVE_DEFAULT
+#endif
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+/*****************************************************************************/
+
+#endif /* MP4V2_MP4V2_H */
diff --git a/include/mp4v2/platform.h b/include/mp4v2/platform.h
new file mode 100644
index 0000000..aea948a
--- /dev/null
+++ b/include/mp4v2/platform.h
@@ -0,0 +1,95 @@
+#ifndef MP4V2_PLATFORM_H
+#define MP4V2_PLATFORM_H
+
+/*****************************************************************************/
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+// Thanks, MSFT, for making C99 a total PITA.  Declare this not to define any stdint stuff; this is useful
+// if you're going to be using mp4v2 on windows with some other library that defines its own stdint.
+// The 1600 version check is for Visual Studio 2010 which has stdint once again.
+#ifndef MP4V2_NO_STDINT_DEFS
+    #if defined( _WIN32 ) && !defined( __MINGW32__ ) && !(defined(_MSC_VER) && _MSC_VER >= 1600)
+        typedef char      int8_t;
+        typedef short     int16_t;
+        typedef int       int32_t;
+        typedef long long int64_t;
+
+        typedef unsigned char      uint8_t;
+        typedef unsigned short     uint16_t;
+        typedef unsigned int       uint32_t;
+        typedef unsigned long long uint64_t;
+    #else
+        #include <stdint.h>
+    #endif
+#endif
+
+#if defined( _WIN32 ) || defined( __MINGW32__ )
+#   if defined( MP4V2_EXPORTS )
+#       define MP4V2_EXPORT __declspec(dllexport)
+#   elif defined( MP4V2_USE_DLL_IMPORT ) || !defined( MP4V2_USE_STATIC_LIB )
+#       define MP4V2_EXPORT __declspec(dllimport)
+#   else
+#       define MP4V2_EXPORT
+#   endif
+#else
+#   define MP4V2_EXPORT __attribute__((visibility("default")))
+#endif
+
+#if defined( __GNUC__ )
+#   define MP4V2_DEPRECATED __attribute__((deprecated))
+#else
+#   define MP4V2_DEPRECATED
+#endif
+
+/******************************************************************************
+ *
+ * TODO-KB: cleanup -- absolutely no need for a C-API to fuss with reserved
+ * C++ keywords. This will involve changing the public interface and current
+ * plan of action:
+ *
+ *      typdef enum {
+ *          mp4_false,
+ *          mp4_true,
+ *      } mp4_bool_t;
+ *
+ * followed by updating all public signatures and implementation.
+ */
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#if !defined( __cplusplus )
+#ifndef bool
+#if SIZEOF_BOOL == 8
+typedef uint64_t bool;
+#else
+#if SIZEOF_BOOL == 4
+typedef uint32_t bool;
+#else
+#if SIZEOF_BOOL == 2
+typedef uint16_t bool;
+#else
+typedef unsigned char bool;
+#endif
+#endif
+#endif
+#ifndef false
+#define false FALSE
+#endif
+#ifndef true
+#define true TRUE
+#endif
+#endif
+#endif
+
+/*****************************************************************************/
+
+#endif /* MP4V2_PLATFORM_H */
diff --git a/include/mp4v2/project.h b/include/mp4v2/project.h
new file mode 100644
index 0000000..07ff7b9
--- /dev/null
+++ b/include/mp4v2/project.h
@@ -0,0 +1,31 @@
+#ifndef MP4V2_PROJECT_H
+#define MP4V2_PROJECT_H
+
+/*****************************************************************************/
+
+#define MP4V2_PROJECT_name            "MP4v2"
+#define MP4V2_PROJECT_name_lower      "mp4v2"
+#define MP4V2_PROJECT_name_upper      "MP4V2"
+#define MP4V2_PROJECT_name_formal     "MP4v2 -r"
+#define MP4V2_PROJECT_url_website     "http://code.google.com/p/mp4v2"
+#define MP4V2_PROJECT_url_downloads   "http://code.google.com/p/mp4v2/downloads/list"
+#define MP4V2_PROJECT_url_discussion  "http://groups.google.com/group/mp4v2"
+#define MP4V2_PROJECT_irc             "irc://irc.freenode.net/handbrake"
+#define MP4V2_PROJECT_bugreport       "<kidjan@gmail.com>"
+#define MP4V2_PROJECT_version         "-r"
+#define MP4V2_PROJECT_version_hex     0x00020100
+#define MP4V2_PROJECT_version_major   2
+#define MP4V2_PROJECT_version_minor   1
+#define MP4V2_PROJECT_version_point   0
+#define MP4V2_PROJECT_repo_url        "svn://nowhere.com/project/unknown"
+#define MP4V2_PROJECT_repo_branch     "unknown"
+#define MP4V2_PROJECT_repo_root       "svn://nowhere.com/project"
+#define MP4V2_PROJECT_repo_uuid       "00000000-0000-0000-0000-000000000000"
+#define MP4V2_PROJECT_repo_rev        0
+#define MP4V2_PROJECT_repo_date       "unknown"
+#define MP4V2_PROJECT_repo_type       "developer"
+#define MP4V2_PROJECT_build           "Tue Nov 15 12:09:50 PST 2016"
+
+/*****************************************************************************/
+
+#endif /* MP4V2_PROJECT_H */
diff --git a/include/mp4v2/project.h.in b/include/mp4v2/project.h.in
new file mode 100644
index 0000000..59777c6
--- /dev/null
+++ b/include/mp4v2/project.h.in
@@ -0,0 +1,31 @@
+#ifndef MP4V2_PROJECT_H
+#define MP4V2_PROJECT_H
+
+/*****************************************************************************/
+
+#define MP4V2_PROJECT_name            "@PROJECT_name@"
+#define MP4V2_PROJECT_name_lower      "@PROJECT_name_lower@"
+#define MP4V2_PROJECT_name_upper      "@PROJECT_name_upper@"
+#define MP4V2_PROJECT_name_formal     "@PROJECT_name_formal@"
+#define MP4V2_PROJECT_url_website     "@PROJECT_url_website@"
+#define MP4V2_PROJECT_url_downloads   "@PROJECT_url_downloads@"
+#define MP4V2_PROJECT_url_discussion  "@PROJECT_url_discussion@"
+#define MP4V2_PROJECT_irc             "@PROJECT_irc@"
+#define MP4V2_PROJECT_bugreport       "@PROJECT_bugreport@"
+#define MP4V2_PROJECT_version         "@PROJECT_version@"
+#define MP4V2_PROJECT_version_hex     @PROJECT_version_hex@
+#define MP4V2_PROJECT_version_major   @PROJECT_version_major@
+#define MP4V2_PROJECT_version_minor   @PROJECT_version_minor@
+#define MP4V2_PROJECT_version_point   @PROJECT_version_point@
+#define MP4V2_PROJECT_repo_url        "@PROJECT_repo_url@"
+#define MP4V2_PROJECT_repo_branch     "@PROJECT_repo_branch@"
+#define MP4V2_PROJECT_repo_root       "@PROJECT_repo_root@"
+#define MP4V2_PROJECT_repo_uuid       "@PROJECT_repo_uuid@"
+#define MP4V2_PROJECT_repo_rev        @PROJECT_repo_rev@
+#define MP4V2_PROJECT_repo_date       "@PROJECT_repo_date@"
+#define MP4V2_PROJECT_repo_type       "@PROJECT_repo_type@"
+#define MP4V2_PROJECT_build           "@PROJECT_build@"
+
+/*****************************************************************************/
+
+#endif /* MP4V2_PROJECT_H */
diff --git a/include/mp4v2/sample.h b/include/mp4v2/sample.h
new file mode 100644
index 0000000..ba35a57
--- /dev/null
+++ b/include/mp4v2/sample.h
@@ -0,0 +1,557 @@
+#ifndef MP4V2_SAMPLE_H
+#define MP4V2_SAMPLE_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_sample MP4v2 Sample
+ *  @{
+ *
+ *****************************************************************************/
+
+/** Sample dependency types.
+ *
+ *  Bit combinations 0x03, 0x30, 0xc0 are reserved.
+ */
+typedef enum MP4SampleDependencyType_e {
+    MP4_SDT_UNKNOWN                       = 0x00, /**< unknown */
+    MP4_SDT_HAS_REDUNDANT_CODING          = 0x01, /**< contains redundant coding */
+    MP4_SDT_HAS_NO_REDUNDANT_CODING       = 0x02, /**< does not contain redundant coding */
+    MP4_SDT_HAS_DEPENDENTS                = 0x04, /**< referenced by other samples */
+    MP4_SDT_HAS_NO_DEPENDENTS             = 0x08, /**< not referenced by other samples */
+    MP4_SDT_IS_DEPENDENT                  = 0x10, /**< references other samples */
+    MP4_SDT_IS_INDEPENDENT                = 0x20, /**< does not reference other samples */
+    MP4_SDT_EARLIER_DISPLAY_TIMES_ALLOWED = 0x40, /**< subequent samples in GOP may display earlier */
+    _MP4_SDT_RESERVED                     = 0x80 /**< reserved */
+} MP4SampleDependencyType;
+
+/** Read a track sample.
+ *
+ *  MP4ReadSample reads the specified sample from the specified track.
+ *  Typically this sample is then decoded in a codec dependent fashion and
+ *  rendered in an appropriate fashion.
+ *
+ *  The argument <b>ppBytes</b> allows for two possible approaches for
+ *  buffering:
+ *
+ *  If the calling application wishes to handle its own buffering it can set
+ *  *ppBytes to the buffer it wishes to use. The calling application is
+ *  responsible for ensuring that the buffer is large enough to hold the
+ *  sample. This can be done by using either MP4GetSampleSize() or
+ *  MP4GetTrackMaxSampleSize() to determine before-hand how large the
+ *  receiving buffer must be.
+ *
+ *  If the value of *ppBytes is NULL, then an appropriately sized buffer is
+ *  automatically malloc'ed for the sample data and *ppBytes set to this
+ *  pointer. The calling application is responsible for free'ing this
+ *  memory.
+ *
+ *  The last four arguments are pointers to variables that can receive
+ *  optional sample information.
+ *
+ *  Typically for audio none of these are needed. MPEG audio such as MP3 or
+ *  AAC has a fixed sample duration and every sample can be accessed at
+ *  random.
+ *
+ *  For video, all of these optional values could be needed. MPEG video can
+ *  be encoded at a variable frame rate, with only occasional random access
+ *  points, and with "B frames" which cause the rendering  (display)  order
+ *  of the video frames to differ from the storage/decoding order.
+ *
+ *  Other media types fall between these two extremes.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId specifies which sample is to be read.
+ *      Caveat: the first sample has id <b>1</b> not <b>0</b>.
+ *  @param ppBytes pointer to the pointer to the sample data.
+ *  @param pNumBytes pointer to variable that will be hold the size in bytes
+ *      of the sample.
+ *  @param pStartTime if non-NULL, pointer to variable that will receive the
+ *      starting timestamp for this sample. Caveat: The timestamp is in
+ *      <b>trackId</b>'s timescale.
+ *  @param pDuration if non-NULL, pointer to variable that will receive the
+ *      duration for this sample. Caveat: The duration is in
+ *      <b>trackId</b>'s timescale.
+ *  @param pRenderingOffset if non-NULL, pointer to variable that will
+ *      receive the rendering offset for this sample. Currently the only
+ *      media type that needs this feature is MPEG video. Caveat: The offset
+ *      is in <b>trackId</b>'s timescale.
+ *  @param pIsSyncSample if non-NULL, pointer to variable that will receive
+ *      the state of the sync/random access flag for this sample.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ *
+ *  @see MP4GetSampleSize().
+ *  @see MP4GetTrackMaxSampleSize().
+ */
+MP4V2_EXPORT
+bool MP4ReadSample(
+    /* input parameters */
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId,
+    /* input/output parameters */
+    uint8_t** ppBytes,
+    uint32_t* pNumBytes,
+    /* output parameters */
+    MP4Timestamp* pStartTime DEFAULT(NULL),
+    MP4Duration*  pDuration DEFAULT(NULL),
+    MP4Duration*  pRenderingOffset DEFAULT(NULL),
+    bool*         pIsSyncSample DEFAULT(NULL) );
+
+/** Read a track sample based on a specified time.
+ *
+ *  MP4ReadSampleFromTime is similar to MP4ReadSample() except the sample
+ *  is specified by using a timestamp instead of sampleId.
+ *  Typically this sample is then decoded in a codec dependent fashion and
+ *  rendered in an appropriate fashion.
+ *
+ *  The argument <b>ppBytes</b> allows for two possible approaches for
+ *  buffering:
+ *
+ *  If the calling application wishes to handle its own buffering it can set
+ *  *ppBytes to the buffer it wishes to use. The calling application is
+ *  responsible for ensuring that the buffer is large enough to hold the
+ *  sample. This can be done by using either MP4GetSampleSize() or
+ *  MP4GetTrackMaxSampleSize() to determine before-hand how large the
+ *  receiving buffer must be.
+ *
+ *  If the value of *ppBytes is NULL, then an appropriately sized buffer is
+ *  automatically malloc'ed for the sample data and *ppBytes set to this
+ *  pointer. The calling application is responsible for free'ing this
+ *  memory.
+ *
+ *  The last four arguments are pointers to variables that can receive
+ *  optional sample information.
+ *
+ *  Typically for audio none of these are needed. MPEG audio such as MP3 or
+ *  AAC has a fixed sample duration and every sample can be accessed at
+ *  random.
+ *
+ *  For video, all of these optional values could be needed. MPEG video can
+ *  be encoded at a variable frame rate, with only occasional random access
+ *  points, and with "B frames" which cause the rendering  (display)  order
+ *  of the video frames to differ from the storage/decoding order.
+ *
+ *  Other media types fall between these two extremes.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param when specifies which sample is to be read based on a time in the
+ *      track timeline. See MP4GetSampleIdFromTime() for details.
+ *  @param ppBytes pointer to the pointer to the sample data.
+ *  @param pNumBytes pointer to variable that will be hold the size in bytes
+ *      of the sample.
+ *  @param pStartTime if non-NULL, pointer to variable that will receive the
+ *      starting timestamp for this sample. Caveat: The timestamp is in
+ *      <b>trackId</b>'s timescale.
+ *  @param pDuration if non-NULL, pointer to variable that will receive the
+ *      duration for this sample. Caveat: The duration is in
+ *      <b>trackId</b>'s timescale.
+ *  @param pRenderingOffset if non-NULL, pointer to variable that will
+ *      receive the rendering offset for this sample. Currently the only
+ *      media type that needs this feature is MPEG video. Caveat: The offset
+ *      is in <b>trackId</b>'s timescale.
+ *  @param pIsSyncSample if non-NULL, pointer to variable that will receive
+ *      the state of the sync/random access flag for this sample.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ *
+ *  @see MP4ReadSample().
+ *  @see MP4GetSampleIdFromTime().
+ *  @see MP4GetSampleSize().
+ *  @see MP4GetTrackMaxSampleSize().
+ */
+MP4V2_EXPORT
+bool MP4ReadSampleFromTime(
+    /* input parameters */
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Timestamp  when,
+    /* input/output parameters */
+    uint8_t** ppBytes,
+    uint32_t* pNumBytes,
+    /* output parameters */
+    MP4Timestamp* pStartTime DEFAULT(NULL),
+    MP4Duration*  pDuration DEFAULT(NULL),
+    MP4Duration*  pRenderingOffset DEFAULT(NULL),
+    bool*         pIsSyncSample DEFAULT(NULL) );
+
+/** Write a track sample.
+ *
+ *  MP4WriteSample writes the given sample at the end of the specified track.
+ *  Currently the library does not support random insertion of samples into
+ *  the track timeline. Note that with mp4 there cannot be any holes or
+ *  overlapping samples in the track timeline. The last three arguments give
+ *  optional sample information.
+ *
+ *  The value of duration can be given as #MP4_INVALID_DURATION if all samples
+ *  in the track have the same duration. This can be specified with
+ *  MP4AddTrack() and related functions.
+ *
+ *  Typically for audio none of the optional arguments are needed. MPEG audio
+ *  such as MP3 or AAC has a fixed sample duration and every sample can be
+ *  accessed at random.
+ *
+ *  For video, all of the optional arguments could be  needed. MPEG video
+ *  can be encoded at a variable frame rate, with only occasional random
+ *  access points, and with "B frames" which cause the rendering  (display)
+ *  order of the video frames to differ from the storage/decoding order.
+ *
+ *  Other media types fall between these two extremes.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param pBytes pointer to sample data.
+ *  @param numBytes length of sample data in bytes.
+ *  @param duration sample duration. Caveat: should be in track timescale.
+ *  @param renderingOffset the rendering offset for this sample.
+ *      Currently the only media type that needs this feature is MPEG
+ *      video. Caveat: The offset should be in the track timescale.
+ *  @param isSyncSample the sync/random access flag for this sample.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ *
+ *  @see MP4AddTrack().
+ */
+MP4V2_EXPORT
+bool MP4WriteSample(
+    MP4FileHandle  hFile,
+    MP4TrackId     trackId,
+    const uint8_t* pBytes,
+    uint32_t       numBytes,
+    MP4Duration    duration DEFAULT(MP4_INVALID_DURATION),
+    MP4Duration    renderingOffset DEFAULT(0),
+    bool           isSyncSample DEFAULT(true) );
+
+/** Write a track sample and supply dependency information.
+ *
+ *  MP4WriteSampleDependency writes the given sample at the end of the specified track.
+ *  Currently the library does not support random insertion of samples into
+ *  the track timeline. Note that with mp4 there cannot be any holes or
+ *  overlapping samples in the track timeline. The last three arguments give
+ *  optional sample information.
+ *
+ *  The value of duration can be given as #MP4_INVALID_DURATION if all samples
+ *  in the track have the same duration. This can be specified with
+ *  MP4AddTrack() and related functions.
+ *
+ *  When this method is used instead of MP4WriteSample() it enables <b>sdtp</b>
+ *  atom to be written out. This atom may be used by advanced players to
+ *  optimize trick-operations such as fast-fwd, reverse or scrubbing.
+ *
+ *  An <b>sdtp</b> atom will always be written out if this method is used.
+ *  To avoid writing the atom, use MP4WriteSample() instead.
+ *
+ *  Intermixing use of MP4WriteSampleDependency() and MP4WriteSample() on the
+ *  same track is not permitted.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param pBytes pointer to sample data.
+ *  @param numBytes length of sample data in bytes.
+ *  @param duration sample duration. Caveat: should be in track timescale.
+ *  @param renderingOffset the rendering offset for this sample.
+ *      Currently the only media type that needs this feature is MPEG
+ *      video. Caveat: The offset should be in the track timescale.
+ *  @param isSyncSample the sync/random access flag for this sample.
+ *  @param dependencyFlags bitmask specifying sample dependency characteristics.
+ *      See #MP4SampleDependencyType for bit constants.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ *
+ *  @see MP4AddTrack().
+ */
+MP4V2_EXPORT
+bool MP4WriteSampleDependency(
+    MP4FileHandle  hFile,
+    MP4TrackId     trackId,
+    const uint8_t* pBytes,
+    uint32_t       numBytes,
+    MP4Duration    duration,
+    MP4Duration    renderingOffset,
+    bool           isSyncSample,
+    uint32_t       dependencyFlags );
+
+/** Make a copy of a sample.
+ *
+ *  MP4CopySample creates a new sample based on an existing sample. Note that
+ *  another copy of the media sample data is created in the file using this
+ *  function. ie. this call is equivalent to MP4ReadSample() followed by
+ *  MP4WriteSample().
+ *
+ *  Note that is the responsibility of the caller to ensure that the copied
+ *  media sample makes sense in the destination track. eg. copying a video
+ *  sample to an audio track is unlikely to result in anything good happening,
+ *  even copying a sample from video track to another requires that the tracks
+ *  use the same encoding and that issues such as image size are addressed.
+ *
+ *  @param srcFile source sample file handle.
+ *  @param srcTrackId source sample track id.
+ *  @param srcSampleId source sample id.
+ *  @param dstFile destination file handle for new (copied) sample.
+ *      If the value is #MP4_INVALID_FILE_HANDLE, the copy is created in
+ *      the same file as <b>srcFile</b>.
+ *  @param dstTrackId destination track id for new sample.
+ *      If the value is #MP4_INVALID_TRACK_ID, the the copy is created in
+ *      the same track as the <b>srcTrackId</b>.
+ *  @param dstSampleDuration duration in track timescale for new sample.
+ *      If the value is #MP4_INVALID_DURATION, then the duration of
+ *      the source sample is used.
+ *
+ *  @return On success, thew id of the new sample.
+ *      On error, #MP4_INVALID_SAMPLE_ID.
+ *
+ *  @see MP4ReadSample().
+ *  @see MP4WriteSample().
+ */
+MP4V2_EXPORT
+bool MP4CopySample(
+    MP4FileHandle srcFile,
+    MP4TrackId    srcTrackId,
+    MP4SampleId   srcSampleId,
+    MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE),
+    MP4TrackId    dstTrackId DEFAULT(MP4_INVALID_TRACK_ID),
+    MP4Duration   dstSampleDuration DEFAULT(MP4_INVALID_DURATION) );
+
+/** Make a copy of a sample.
+ *
+ *  MP4EncAndCopySample is similar to MP4CopySample() except that it
+ *  offers an encryption hook to the caller.
+ *
+ *  @param srcFile source sample file handle.
+ *  @param srcTrackId source sample track id.
+ *  @param srcSampleId source sample id.
+ *  @param encfcnp undocumented.
+ *  @param encfcnparam1 undocumented.
+ *  @param dstFile destination file handle for new (copied) sample.
+ *      If the value is #MP4_INVALID_FILE_HANDLE, the copy is created in
+ *      the same file as <b>srcFile</b>.
+ *  @param dstTrackId destination track id for new sample.
+ *      If the value is #MP4_INVALID_TRACK_ID, the the copy is created in
+ *      the same track as the <b>srcTrackId</b>.
+ *  @param dstSampleDuration duration in track timescale for new sample.
+ *      If the value is #MP4_INVALID_DURATION, then the duration of
+ *      the source sample is used.
+ *
+ *  @return On success, thew id of the new sample.
+ *      On error, #MP4_INVALID_SAMPLE_ID.
+ *
+ *  @see MP4CopySample().
+ */
+MP4V2_EXPORT
+bool MP4EncAndCopySample(
+    MP4FileHandle srcFile,
+    MP4TrackId    srcTrackId,
+    MP4SampleId   srcSampleId,
+    encryptFunc_t encfcnp,
+    uint32_t      encfcnparam1,
+    MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE),
+    MP4TrackId    dstTrackId DEFAULT(MP4_INVALID_TRACK_ID),
+    MP4Duration   dstSampleDuration DEFAULT(MP4_INVALID_DURATION) );
+
+/** Not implemented.
+ */
+MP4V2_EXPORT
+bool MP4ReferenceSample(
+    MP4FileHandle srcFile,
+    MP4TrackId    srcTrackId,
+    MP4SampleId   srcSampleId,
+    MP4FileHandle dstFile,
+    MP4TrackId    dstTrackId,
+    MP4Duration   dstSampleDuration DEFAULT(MP4_INVALID_DURATION) );
+
+/** Get size of a track sample.
+ *
+ *  MP4GetSampleSize returns the size in bytes of the specified sample from the
+ *  the specified track.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId id of sample for operation. Caveat: the first sample has
+ *      id <b>1</b>, not <b>0</b>.
+ *
+ *  @return On success the sample size in bytes. On error, <b>0</b>.
+ */
+MP4V2_EXPORT
+uint32_t MP4GetSampleSize(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId);
+
+/** Get the maximum sample size of a track.
+ *
+ *  MP4GetTrackMaxSampleSize returns the maximum size in bytes of all the
+ *  samples in the specified track.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *
+ *  @return On success, the maximum sample size in bytes. On error, <b>0</b>.
+ *
+ *  @see MP4GetSampleSize().
+ */
+MP4V2_EXPORT
+uint32_t MP4GetTrackMaxSampleSize(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/** Get sample id of a specified time.
+ *
+ *  MP4GetSampleIdFromTime returns the sample id of the track sample in which
+ *  the specified time occurs.
+ *
+ *  The specified time should be in the track timescale.
+ *
+ *  It is wise to use MP4GetSampleTime() with the returned sample id so one
+ *  can adjust for any difference between the specified time and the actual
+ *  start time of the sample.
+ *
+ *  If the calling application needs a sample that can be accessed randomly
+ *  then the <b>wantSyncSample</b> argument should be set to true. This could
+ *  be the case for a player that is implementing a positioning function and
+ *  needs to be able to start decoding a track from the returned sample id.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param when time in track timescale.
+ *  @param wantSyncSample specifies if the result sample id must correspond
+ *      to a sample whose sync/random access flag is true.
+ *
+ *  @return On success, the sample id that occurs at the specified time.
+ *      On error, #MP4_INVALID_SAMPLE_ID.
+ *
+ *  @see MP4ConvertToTrackTimestamp() for how to map a time value to this
+ *      timescale.
+ */
+MP4V2_EXPORT
+MP4SampleId MP4GetSampleIdFromTime(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Timestamp  when,
+    bool          wantSyncSample DEFAULT(false) );
+
+/** Get start time of track sample.
+ *
+ *  MP4GetSampleTime returns the start time of the specified sample from
+ *  the specified track in the track timescale units.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId id of sample for operation. Caveat: the first sample has
+ *      id <b>1</b>, not <b>0</b>.
+ *
+ *  @return On success, sample start time in track timescale units.
+ *      On error, #MP4_INVALID_TIMESTAMP.
+ *
+ *  @see MP4ConvertFromTrackTimestamp() for how to map this value to another
+ *      timescale.
+ */
+MP4V2_EXPORT
+MP4Timestamp MP4GetSampleTime(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId );
+
+/** Get the duration of a track sample.
+ *
+ *  MP4GetSampleDuration returns the duration of the specified sample from
+ *  the specified track in the track timescale units.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId id of sample for operation. Caveat: the first sample has
+ *      id <b>1</b>, not <b>0</b>.
+ *
+ *  @return On success, the sample duration in track timescale units.
+ *      On error, #MP4_INVALID_DURATION.
+ *
+ *  @see MP4ConvertFromTrackDuration() for how to map this value to another
+ *      timescale.
+ */
+MP4V2_EXPORT
+MP4Duration MP4GetSampleDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId );
+
+/** Get the rendering offset of a track sample.
+ *
+ *  MP4GetSampleRenderingOffset returns the rendering offset of the specified
+ *  sample from the specified track in the track timescale units.
+ *
+ *  The sample rendering offset is typically zero for all media types other
+ *  than video. For video, encodings such as those  defined by MPEG have
+ *  three  types  of  frames: I, P, and B. To increase coding efficiency B
+ *  frames can depend on I or P frames that should be rendered after the B
+ *  frame. However to decode the B frame the I or P frame must already have
+ *  been decoded. This situation is addressed  by placing the frames in
+ *  decoding order in the video track, and then setting the rendering offset
+ *  property to indicate when the video frame should actually be rendered to
+ *  the screen. Hence the start time of a sample indicates when it should be
+ *  decoded, the start time plus the rendering offset indicates when it
+ *  should be rendered.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId id of sample for operation. Caveat: the first sample has
+ *      id <b>1</b>, not <b>0</b>.
+ *
+ *  @return On success, the rendering offset in track timescale units.
+ *      On error, #MP4_INVALID_DURATION.
+ *
+ *  @see MP4ConvertFromTrackDuration() for how to map this value to another
+ *      timescale.
+ */
+MP4V2_EXPORT
+MP4Duration MP4GetSampleRenderingOffset(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId );
+
+/** Set the rendering offset of a track sample.
+ *
+ *  MP4SetSampleRenderingOffset sets the rendering offset of the specified
+ *  sample from the specified track in the track timescale units.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId id of sample for operation. Caveat: the first sample has
+ *      id <b>1</b>, not <b>0</b>.
+ *  @param renderingOffset new offset value in timescale units.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ *
+ *  @see MP4ConvertToTrackDuration() for how to map this value from another
+ *      timescale.
+ *  @see MP4GetSampleRenderingOffset() for a description of this sample
+ *      property.
+ */
+MP4V2_EXPORT
+bool MP4SetSampleRenderingOffset(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId,
+    MP4Duration   renderingOffset );
+
+/** Get sync/random access state of sample.
+ *
+ *  MP4GetSampleSync returns the state of the sync/random access flag of
+ *  the specified sample from the specified track.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param sampleId id of sample for operation. Caveat: the first sample has
+ *      id <b>1</b>, not <b>0</b>.
+ *
+ *  @return <b>1</b> when true, <b>0</b> when false. On error, <b>-1</b>.
+ */
+MP4V2_EXPORT
+int8_t MP4GetSampleSync(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId );
+
+/* @} ***********************************************************************/
+
+#endif /* MP4V2_SAMPLE_H */
diff --git a/include/mp4v2/streaming.h b/include/mp4v2/streaming.h
new file mode 100644
index 0000000..36389d6
--- /dev/null
+++ b/include/mp4v2/streaming.h
@@ -0,0 +1,163 @@
+#ifndef MP4V2_STREAMING_H
+#define MP4V2_STREAMING_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_hint MP4v2 Streaming
+ *  @{
+ *
+ *****************************************************************************/
+
+MP4V2_EXPORT
+bool MP4GetHintTrackRtpPayload(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    char**        ppPayloadName DEFAULT(NULL),
+    uint8_t*      pPayloadNumber DEFAULT(NULL),
+    uint16_t*     pMaxPayloadSize DEFAULT(NULL),
+    char**        ppEncodingParams DEFAULT(NULL) );
+
+#define MP4_SET_DYNAMIC_PAYLOAD 0xff
+
+MP4V2_EXPORT
+bool MP4SetHintTrackRtpPayload(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    const char*   pPayloadName,
+    uint8_t*      pPayloadNumber,
+    uint16_t      maxPayloadSize DEFAULT(0),
+    const char *  encode_params DEFAULT(NULL),
+    bool          include_rtp_map DEFAULT(true),
+    bool          include_mpeg4_esid DEFAULT(true) );
+
+MP4V2_EXPORT
+const char* MP4GetSessionSdp(
+    MP4FileHandle hFile );
+
+MP4V2_EXPORT
+bool MP4SetSessionSdp(
+    MP4FileHandle hFile,
+    const char*   sdpString );
+
+MP4V2_EXPORT
+bool MP4AppendSessionSdp(
+    MP4FileHandle hFile,
+    const char*   sdpString );
+
+MP4V2_EXPORT
+const char* MP4GetHintTrackSdp(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId );
+
+MP4V2_EXPORT
+bool MP4SetHintTrackSdp(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    const char*   sdpString );
+
+MP4V2_EXPORT
+bool MP4AppendHintTrackSdp(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    const char*   sdpString );
+
+MP4V2_EXPORT
+MP4TrackId MP4GetHintTrackReferenceTrackId(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId );
+
+MP4V2_EXPORT
+bool MP4ReadRtpHint(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    MP4SampleId   hintSampleId,
+    uint16_t*     pNumPackets DEFAULT(NULL) );
+
+MP4V2_EXPORT
+uint16_t MP4GetRtpHintNumberOfPackets(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId );
+
+MP4V2_EXPORT
+int8_t MP4GetRtpPacketBFrame(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    uint16_t      packetIndex );
+
+MP4V2_EXPORT
+int32_t MP4GetRtpPacketTransmitOffset(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    uint16_t      packetIndex );
+
+MP4V2_EXPORT
+bool MP4ReadRtpPacket(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    uint16_t      packetIndex,
+    uint8_t**     ppBytes,
+    uint32_t*     pNumBytes,
+    uint32_t      ssrc DEFAULT(0),
+    bool          includeHeader DEFAULT(true),
+    bool          includePayload DEFAULT(true) );
+
+MP4V2_EXPORT
+MP4Timestamp MP4GetRtpTimestampStart(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId );
+
+MP4V2_EXPORT
+bool MP4SetRtpTimestampStart(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    MP4Timestamp  rtpStart );
+
+MP4V2_EXPORT
+bool MP4AddRtpHint(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId );
+
+MP4V2_EXPORT
+bool MP4AddRtpVideoHint(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    bool          isBframe DEFAULT(false),
+    uint32_t      timestampOffset DEFAULT(0) );
+
+MP4V2_EXPORT
+bool MP4AddRtpPacket(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    bool          setMbit DEFAULT(false),
+    int32_t       transmitOffset DEFAULT(0) );
+
+MP4V2_EXPORT
+bool MP4AddRtpImmediateData(
+    MP4FileHandle  hFile,
+    MP4TrackId     hintTrackId,
+    const uint8_t* pBytes,
+    uint32_t       numBytes );
+
+MP4V2_EXPORT
+bool MP4AddRtpSampleData(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    MP4SampleId   sampleId,
+    uint32_t      dataOffset,
+    uint32_t      dataLength );
+
+MP4V2_EXPORT
+bool MP4AddRtpESConfigurationPacket(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId );
+
+MP4V2_EXPORT
+bool MP4WriteRtpHint(
+    MP4FileHandle hFile,
+    MP4TrackId    hintTrackId,
+    MP4Duration   duration,
+    bool          isSyncSample DEFAULT(true) );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_STREAMING_H */
diff --git a/include/mp4v2/track.h b/include/mp4v2/track.h
new file mode 100644
index 0000000..90eb7bc
--- /dev/null
+++ b/include/mp4v2/track.h
@@ -0,0 +1,451 @@
+#ifndef MP4V2_TRACK_H
+#define MP4V2_TRACK_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_track MP4v2 Track
+ *  @{
+ *
+ *****************************************************************************/
+
+/** Add a user defined track.
+ *
+ *  MP4AddTrack adds a user defined track to the mp4 file. Care should be
+ *  taken to avoid any of the standardized track type names. A useful
+ *  convention is use only uppercase characters for user defined track types.
+ *  The string should be exactly four characters in length, e.g. "MINE".
+ *
+ *  Note this should not be used to add any of the known track types defined
+ *  in the MP4 standard (ISO/IEC 14496-1:2001).
+ *
+ *  @param hFile handle of file for operation.
+ *  @param type specifies the type of track to be added.
+ *  @param timeScale the time scale in ticks per second of the track.  Default is 1000.
+ *
+ *  @return On success, the track-id of new track.
+ *      On failure, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddTrack(
+    MP4FileHandle hFile,
+    const char*   type,
+    uint32_t      timeScale DEFAULT(MP4_MSECS_TIME_SCALE) );
+
+/** Add an MPEG-4 systems track.
+ *
+ *  MP4AddSystemsTrack adds an MPEG-4 Systems track to the mp4 file. Note
+ *  this should not be used to add OD or scene tracks, MP4AddODTrack() and
+ *  MP4AddSceneTrack() should be used for those purposes. Other known
+ *  MPEG-4 System track types are:
+ *      @li #MP4_CLOCK_TRACK_TYPE
+ *      @li #MP4_MPEG7_TRACK_TYPE
+ *      @li #MP4_OCI_TRACK_TYPE
+ *      @li #MP4_IPMP_TRACK_TYPE
+ *      @li #MP4_MPEGJ_TRACK_TYPE
+ *
+ *  @param hFile handle of file for operation.
+ *  @param type specifies the type of track to be added.
+ *
+ *  @return On success, the track-id of new track.
+ *      On failure, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddSystemsTrack(
+    MP4FileHandle hFile,
+    const char*   type );
+
+/** Add a object descriptor (OD) track.
+ *
+ *  MP4AddODTrack adds an object descriptor (aka OD) track to the mp4 file.
+ *  MP4WriteSample() can then be used to add the desired OD commands to the
+ *  track. The burden is currently on the calling application to understand
+ *  OD.
+ *
+ *  Those wishing to have a simple audio/video scene without understanding
+ *  OD may wish to use MP4MakeIsmaCompliant() to create the minimal OD and
+ *  BIFS information.
+ *
+ *  @param hFile handle of file for operation.
+ * 
+ *  @return On success, the track-id of new track.
+ *      On failure, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddODTrack(
+    MP4FileHandle hFile );
+
+/** Add a scene (BIFS) track.
+ *
+ *  MP4AddSceneTrack adds a scene (aka BIFS) track to the mp4 file.
+ *  MP4WriteSample() can then be used to add the desired BIFS commands to
+ *  the track. The burden is currently on the calling application to
+ *  understand BIFS.
+ *
+ *  Those  wishing to have a simple audio/video scene without understanding
+ *  BIFS may wish to use MP4MakeIsmaCompliant() to create the minimal OD
+ *  and BIFS information.
+ *
+ *  @param hFile handle of file for operation.
+ * 
+ *  @return On success, the track-id of new track.
+ *      On failure, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddSceneTrack(
+    MP4FileHandle hFile );
+
+/** Add audio track to mp4 file.
+ *
+ *  MP4AddAudioTrack adds an audio track to the mp4 file. MP4WriteSample()
+ *  can then be used to add the desired audio samples.
+ *
+ *  It is recommended that the time scale be set to the sampling frequency
+ *  (eg. 44100 Hz) of the audio so as to preserve the timing information
+ *  accurately.
+ *
+ *  If the audio encoding uses a fixed duration for each sample that should
+ *  be specified here. If not then the value #MP4_INVALID_DURATION
+ *  should be given for the sampleDuration argument.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param timeScale the time scale in ticks per second of the track.
+ *  @param sampleDuration the fixed  duration for all track samples.
+ *      Caveat: the value should be in track-timescale units.
+ *  @param audioType the audio encoding type.
+ *      See MP4GetTrackEsdsObjectTypeId() for known values.
+ *
+ *  @return On success, the track-id of the new track.
+ *      On error, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddAudioTrack(
+    MP4FileHandle hFile,
+    uint32_t      timeScale,
+    MP4Duration   sampleDuration,
+    uint8_t       audioType DEFAULT(MP4_MPEG4_AUDIO_TYPE) );
+
+/** Add ulaw track to mp4 file.
+ *
+ *  MP4AddULawAudioTrack adds a ulaw track to the mp4 file. MP4WriteSample()
+ *  can then be used to add the desired audio samples.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param timeScale the time scale in ticks per second of the track.
+ *
+ *  @return On success, the track-id of the new track.
+ *      On error, #MP4_INVALID_TRACK_ID.
+*/
+MP4V2_EXPORT
+MP4TrackId MP4AddULawAudioTrack(
+    MP4FileHandle hFile,
+    uint32_t timeScale);
+
+/** Add alaw track to mp4 file.
+ *
+ *  MP4AddALawAudioTrack adds a alaw track to the mp4 file. MP4WriteSample()
+ *  can then be used to add the desired audio samples.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param timeScale the time scale in ticks per second of the track.
+ *
+ *  @return On success, the track-id of the new track.
+ *      On error, #MP4_INVALID_TRACK_ID.
+*/
+MP4V2_EXPORT
+MP4TrackId MP4AddALawAudioTrack(
+    MP4FileHandle hFile,
+    uint32_t timeScale);
+
+MP4V2_EXPORT
+MP4TrackId MP4AddAC3AudioTrack(
+    MP4FileHandle hFile,
+    uint32_t      samplingRate,
+    uint8_t       fscod,
+    uint8_t       bsid,
+    uint8_t       bsmod,
+    uint8_t       acmod,
+    uint8_t       lfeon,
+    uint8_t       bit_rate_code );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddAmrAudioTrack(
+    MP4FileHandle hFile,
+    uint32_t      timeScale,
+    uint16_t      modeSet,
+    uint8_t       modeChangePeriod,
+    uint8_t       framesPerSample,
+    bool          isAmrWB );
+
+MP4V2_EXPORT
+void MP4SetAmrVendor(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint32_t      vendor );
+
+MP4V2_EXPORT
+void MP4SetAmrDecoderVersion(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint8_t       decoderVersion );
+
+MP4V2_EXPORT
+void MP4SetAmrModeSet(
+    MP4FileHandle hFile,
+    MP4TrackId    trakId,
+    uint16_t      modeSet );
+
+MP4V2_EXPORT
+uint16_t MP4GetAmrModeSet(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddHrefTrack(
+    MP4FileHandle hFile,
+    uint32_t      timeScale,
+    MP4Duration   sampleDuration,
+    const char*   base_url DEFAULT(NULL) );
+
+MP4V2_EXPORT
+const char* MP4GetHrefTrackBaseUrl(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/** Add a video track.
+ *
+ *  MP4AddVideoTrack adds a video track to the mp4 file. MP4WriteSample()
+ *  can then be used to add the desired video samples.
+ *
+ *  It is recommended that the time scale be set to 90000 so as to preserve
+ *  the timing information accurately for the range of video frame rates
+ *  commonly in use.
+ *
+ *  If the video frame rate is to be fixed then the sampleDuration argument
+ *  should be give the appropriate fixed value. If the video frame rate is
+ *  to be variable then the value #MP4_INVALID_DURATION should be
+ *  given for the sampleDuration argument.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param timeScale the timescale in ticks per second of the track.
+ *  @param sampleDuration specifies fixed sample duration for all track
+ *      samples. Caveat: the value should be in track timescale units.
+ *  @param width specifies the video frame width in pixels.
+ *  @param height specifies the video frame height in pixels.
+ *  @param videoType specifies the video encoding type.
+ *      See MP4GetTrackVideoType() for known values.
+ *
+ *  @return On success, the track-id of the new track.
+ *      On error, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddVideoTrack(
+    MP4FileHandle hFile,
+    uint32_t      timeScale,
+    MP4Duration   sampleDuration,
+    uint16_t      width,
+    uint16_t      height,
+    uint8_t       videoType DEFAULT(MP4_MPEG4_VIDEO_TYPE) );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddH264VideoTrack(
+    MP4FileHandle hFile,
+    uint32_t      timeScale,
+    MP4Duration   sampleDuration,
+    uint16_t      width,
+    uint16_t      height,
+    uint8_t       AVCProfileIndication,
+    uint8_t       profile_compat,
+    uint8_t       AVCLevelIndication,
+    uint8_t       sampleLenFieldSizeMinusOne );
+
+MP4V2_EXPORT
+void MP4AddH264SequenceParameterSet(
+    MP4FileHandle  hFile,
+    MP4TrackId     trackId,
+    const uint8_t* pSequence,
+    uint16_t       sequenceLen );
+
+MP4V2_EXPORT
+void MP4AddH264PictureParameterSet(
+    MP4FileHandle  hFile,
+    MP4TrackId     trackId,
+    const uint8_t* pPict,
+    uint16_t       pictLen );
+
+MP4V2_EXPORT
+void MP4SetH263Vendor(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint32_t      vendor );
+
+MP4V2_EXPORT
+void MP4SetH263DecoderVersion(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint8_t       decoderVersion );
+
+MP4V2_EXPORT
+void MP4SetH263Bitrates(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint32_t      avgBitrate,
+    uint32_t      maxBitrate );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddH263VideoTrack(
+    MP4FileHandle hFile,
+    uint32_t      timeScale,
+    MP4Duration   sampleDuration,
+    uint16_t      width,
+    uint16_t      height,
+    uint8_t       h263Level,
+    uint8_t       h263Profile,
+    uint32_t      avgBitrate,
+    uint32_t      maxBitrate );
+
+/** Add a hint track.
+ *
+ *  MP4AddHintTrack adds a hint track to the mp4 file. A hint track is used
+ *  to describe how to send the reference media track over a particular
+ *  network transport. In the case of the IETF RTP protocol, the hint track
+ *  describes how the media data should be placed into packets and any
+ *  media specific protocol headers that should be added.
+ *
+ *  Typically there is a one to one correspondence between reference  media
+ *  track  samples  and  hint track samples. The start time, duration, and
+ *  sync flags are typically the same, however provisions are made for
+ *  deviations from this rule.
+ *
+ *  The MP4 library provides extensive support for RTP hint tracks. This
+ *  includes a easy to use API to create RTP hint tracks, and read out
+ *  fully constructed RTP packets based on the hint track.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param refTrackId specifies the reference media track for this hint track.
+ *
+ *  @return On success, the track-id of the new track.
+ *      On error, #MP4_INVALID_TRACK_ID.
+ */
+MP4V2_EXPORT
+MP4TrackId MP4AddHintTrack(
+    MP4FileHandle hFile,
+    MP4TrackId    refTrackId );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddTextTrack(
+    MP4FileHandle hFile,
+    MP4TrackId    refTrackId );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddSubtitleTrack(
+    MP4FileHandle hFile,
+    uint32_t      timescale,
+    uint16_t      width,
+    uint16_t      height );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddSubpicTrack(
+    MP4FileHandle hFile,
+    uint32_t      timescale,
+    uint16_t      width,
+    uint16_t      height );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddPixelAspectRatio(
+    MP4FileHandle hFile,
+    MP4TrackId    refTrackId,
+    uint32_t      hSpacing,
+    uint32_t      vSpacing );
+
+MP4V2_EXPORT
+MP4TrackId MP4AddColr(
+    MP4FileHandle hFile,
+    MP4TrackId    refTrackId,
+    uint16_t      primary,
+    uint16_t      transfer,
+    uint16_t      matrix );
+
+MP4V2_EXPORT
+MP4TrackId MP4CloneTrack(
+    MP4FileHandle srcFile,
+    MP4TrackId    srcTrackId,
+    MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE),
+    MP4TrackId    dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) );
+
+MP4V2_EXPORT
+MP4TrackId MP4CopyTrack(
+    MP4FileHandle srcFile,
+    MP4TrackId    srcTrackId,
+    MP4FileHandle dstFile DEFAULT(MP4_INVALID_FILE_HANDLE),
+    bool          applyEdits DEFAULT(false),
+    MP4TrackId    dstHintTrackReferenceTrack DEFAULT(MP4_INVALID_TRACK_ID) );
+
+MP4V2_EXPORT
+bool MP4DeleteTrack(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+uint32_t MP4GetNumberOfTracks(
+    MP4FileHandle hFile,
+    const char*   type DEFAULT(NULL),
+    uint8_t       subType DEFAULT(0) );
+
+MP4V2_EXPORT
+MP4TrackId MP4FindTrackId(
+    MP4FileHandle hFile,
+    uint16_t      index,
+    const char*   type DEFAULT(NULL),
+    uint8_t       subType DEFAULT(0) );
+
+MP4V2_EXPORT
+uint16_t MP4FindTrackIndex(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/** Get maximum duration of chunk.
+ *
+ *  MP4GetTrackDurationPerChunk gets the maximum duration for each chunk.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param duration out value of duration in track timescale units.
+ *
+ *  return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4GetTrackDurationPerChunk(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Duration*  duration );
+
+/** Set maximum duration of chunk.
+ *
+ *  MP4SetTrackDurationPerChunk sets the maximum duration for each chunk.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param duration in timescale units.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4SetTrackDurationPerChunk(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Duration   duration );
+
+/**
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4AddIPodUUID(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_TRACK_H */
diff --git a/include/mp4v2/track_prop.h b/include/mp4v2/track_prop.h
new file mode 100644
index 0000000..4b3adf4
--- /dev/null
+++ b/include/mp4v2/track_prop.h
@@ -0,0 +1,366 @@
+#ifndef MP4V2_TRACK_PROP_H
+#define MP4V2_TRACK_PROP_H
+
+/**************************************************************************//**
+ *
+ *  @defgroup mp4_track_prop MP4v2 Track Property
+ *  @{
+ *
+ *****************************************************************************/
+
+/* specific track properties */
+
+MP4V2_EXPORT
+bool MP4HaveTrackAtom(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   atomname );
+
+/** Get the track type.
+ *
+ *  MP4GetTrackType gets the type of the track with the specified track id.
+ *
+ *  Note: the library does not provide a MP4SetTrackType function, the
+ *  track type needs to be specified when the track is created, e.g.
+ *  MP4AddSystemsTrack(MP4_OCI_TRACK_TYPE).
+ *
+ *  Known track types are:
+ *      @li #MP4_OD_TRACK_TYPE
+ *      @li #MP4_SCENE_TRACK_TYPE
+ *      @li #MP4_AUDIO_TRACK_TYPE
+ *      @li #MP4_VIDEO_TRACK_TYPE
+ *      @li #MP4_HINT_TRACK_TYPE
+ *      @li #MP4_CNTL_TRACK_TYPE
+ *      @li #MP4_TEXT_TRACK_TYPE
+ *      @li #MP4_CLOCK_TRACK_TYPE
+ *      @li #MP4_MPEG7_TRACK_TYPE
+ *      @li #MP4_OCI_TRACK_TYPE
+ *      @li #MP4_IPMP_TRACK_TYPE
+ *      @li #MP4_MPEGJ_TRACK_TYPE
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *
+ *  @return On success, a string indicating track type. On failure, NULL.
+ */
+MP4V2_EXPORT
+const char* MP4GetTrackType(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+const char* MP4GetTrackMediaDataName(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/*
+ * MP4GetTrackMediaDataOriginalFormat is to be used to get the original
+ * MediaDataName if a track has been encrypted.
+ */
+
+MP4V2_EXPORT
+bool MP4GetTrackMediaDataOriginalFormat(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    char*         originalFormat,
+    uint32_t      buflen );
+
+MP4V2_EXPORT
+MP4Duration MP4GetTrackDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/** Get the time scale of a track.
+ *
+ *  MP4GetTrackTimeScale returns the time scale of the specified track in
+ *  the mp4 file. The time scale determines the number of clock ticks per
+ *  second for this track.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *
+ *  @return timescale (ticks per second) of the track in the mp4 file.
+ */
+MP4V2_EXPORT
+uint32_t MP4GetTrackTimeScale(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/** Set the time scale of a track.
+ *
+ *  MP4SetTrackTimeScale sets the time scale of the specified track in the
+ *  mp4 file. The time scale determines the number of clock ticks per
+ *  second for this track.
+ *
+ *  Typically this value is set once when the track is created. However
+ *  this call can be used to modify the value if that is desired. Since
+ *  track sample durations are expressed in units of the track time scale,
+ *  any change to the time scale value will effect the real time duration
+ *  of the samples.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param timeScale desired time scale for the track.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4SetTrackTimeScale(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint32_t      value );
+
+/** Get ISO-639-2/T language code of a track.
+ *  The language code is a 3-char alpha code consisting of lower-case letters.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param code buffer to hold 3-char+null (4-bytes total).
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4GetTrackLanguage(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    char*         code );
+
+/** Set ISO-639-2/T language code of a track.
+ *  The language code is a 3-char alpha code consisting of lower-case letters.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *  @param code 3-char language code.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4SetTrackLanguage(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   code );
+
+/** Get track name.
+ *
+ *  MP4GetTrackName gets the name of the track via udta.name property.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4GetTrackName(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    char**        name );
+
+/** Set track name.
+ *
+ *  MP4SetTrackName sets the name of the track via udta.name property.
+ *  The udta atom is created if needed.
+ *
+ *  @param hFile handle of file for operation.
+ *  @param trackId id of track for operation.
+ *
+ *  @return <b>true</b> on success, <b>false</b> on failure.
+ */
+MP4V2_EXPORT
+bool MP4SetTrackName(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   name );
+
+MP4V2_EXPORT
+uint8_t MP4GetTrackAudioMpeg4Type(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+uint8_t MP4GetTrackEsdsObjectTypeId(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/* returns MP4_INVALID_DURATION if track samples do not have a fixed duration */
+MP4V2_EXPORT
+MP4Duration MP4GetTrackFixedSampleDuration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+uint32_t MP4GetTrackBitRate(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+bool MP4GetTrackVideoMetadata(
+    MP4FileHandle hFile,
+    MP4TrackId trackId,
+    uint8_t**  ppConfig,
+    uint32_t*  pConfigSize );
+
+MP4V2_EXPORT
+bool MP4GetTrackESConfiguration(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint8_t**     ppConfig,
+    uint32_t*     pConfigSize );
+
+MP4V2_EXPORT
+bool MP4SetTrackESConfiguration(
+    MP4FileHandle  hFile,
+    MP4TrackId     trackId,
+    const uint8_t* pConfig,
+    uint32_t       configSize );
+
+/* h264 information routines */
+
+/** Frees the memory allocated by MP4GetTrackH264SeqPictHeaders.
+ *
+ *  MP4FreeH264SeqPictHeaders frees the memory that was allocated by a
+ *  call to the MP4GetTrackH264SeqPictHeaders function.
+ *
+ *  When a client application wants to extract the H.264 video data from
+ *  an MP4 file it will call MP4GetTrackH264SeqPictHeaders to obtain the
+ *  sequence and picture parameter sets.  These parameter sets are
+ *  required for decoding a sequence of one, or more, coded slices.  When
+ *  the client application is done with the data it must free it.  On the
+ *  Windows platform this cannot be done directly by the client
+ *  application because the C runtime of the client application and the C
+ *  runtime of the mp4v2 DLL may be different, which will result in an
+ *  error at runtime.  This function allows the client application to let
+ *  the mp4v2 DLL free the memory with the appropriate CRT heap manager.
+ *
+ *  @param pSeqHeaders pointer to an array of SPS pointers.
+ *  @param pSeqHeaderSize pointer to array of SPS sizes.
+ *  @param pPictHeader pointer to an array of PPS pointers.
+ *  @param pPictHeaderSize pointer to array of PPS sizes.
+ */
+MP4V2_EXPORT
+void MP4FreeH264SeqPictHeaders(
+    uint8_t** pSeqHeaders,
+    uint32_t* pSeqHeaderSize,
+    uint8_t** pPictHeader,
+    uint32_t* pPictHeaderSize );
+
+MP4V2_EXPORT
+bool MP4GetTrackH264ProfileLevel(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint8_t*      pProfile,
+    uint8_t*      pLevel );
+
+MP4V2_EXPORT
+bool MP4GetTrackH264SeqPictHeaders(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint8_t***    pSeqHeaders,
+    uint32_t**    pSeqHeaderSize,
+    uint8_t***    pPictHeader,
+    uint32_t**    pPictHeaderSize );
+
+MP4V2_EXPORT
+bool MP4GetTrackH264LengthSize(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    uint32_t*     pLength );
+
+MP4V2_EXPORT
+MP4SampleId MP4GetTrackNumberOfSamples(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+uint16_t MP4GetTrackVideoWidth(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+uint16_t MP4GetTrackVideoHeight(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+double MP4GetTrackVideoFrameRate(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+int MP4GetTrackAudioChannels(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+MP4V2_EXPORT
+bool MP4IsIsmaCrypMediaTrack(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId );
+
+/* generic track properties */
+
+MP4V2_EXPORT
+bool MP4HaveTrackAtom(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   atomName );
+
+MP4V2_EXPORT
+bool MP4GetTrackIntegerProperty(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   propName,
+    uint64_t*     retvalue );
+
+MP4V2_EXPORT
+bool MP4GetTrackFloatProperty(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   propName,
+    float*        ret_value );
+
+MP4V2_EXPORT
+bool MP4GetTrackStringProperty(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   propName,
+    const char**  retvalue );
+
+MP4V2_EXPORT
+bool MP4GetTrackBytesProperty(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   propName,
+    uint8_t**     ppValue,
+    uint32_t*     pValueSize );
+
+MP4V2_EXPORT
+bool MP4SetTrackIntegerProperty(
+    MP4FileHandle hFile,
+    MP4TrackId   trackId,
+    const char*  propName,
+    int64_t      value );
+
+MP4V2_EXPORT
+bool MP4SetTrackFloatProperty(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   propName,
+    float         value );
+
+MP4V2_EXPORT
+bool MP4SetTrackStringProperty(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   propName,
+    const char*   value );
+
+MP4V2_EXPORT
+bool MP4SetTrackBytesProperty(
+    MP4FileHandle  hFile,
+    MP4TrackId     trackId,
+    const char*    propName,
+    const uint8_t* pValue,
+    uint32_t       valueSize);
+
+/** @} ***********************************************************************/
+
+#endif /* MP4V2_TRACK_PROP_H */
diff --git a/libplatform/config.h b/libplatform/config.h
new file mode 100644
index 0000000..e23dea0
--- /dev/null
+++ b/libplatform/config.h
@@ -0,0 +1,66 @@
+/* libplatform/config.h.  Generated from config.h.in by configure.  */
+/* libplatform/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if LFS should be activated */
+/* #undef NEED_LFS_ACTIVATION */
+
+/* Name of package */
+#define PACKAGE "mp4v2"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "kidjan@gmail.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "MP4v2"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "MP4v2 -r"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "mp4v2"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "-r"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "-r"
diff --git a/libplatform/config.h.in b/libplatform/config.h.in
new file mode 100644
index 0000000..47a5a04
--- /dev/null
+++ b/libplatform/config.h.in
@@ -0,0 +1,65 @@
+/* libplatform/config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#undef LT_OBJDIR
+
+/* Define to 1 if LFS should be activated */
+#undef NEED_LFS_ACTIVATION
+
+/* Name of package */
+#undef PACKAGE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
diff --git a/libplatform/endian.h b/libplatform/endian.h
new file mode 100644
index 0000000..83fe69e
--- /dev/null
+++ b/libplatform/endian.h
@@ -0,0 +1,42 @@
+#ifndef MP4V2_PLATFORM_ENDIAN_H
+#define MP4V2_PLATFORM_ENDIAN_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define MP4V2_BYTESWAP_16(x) \
+    mp4v2::platform::uint16_t( ((mp4v2::platform::uint16_t(x) & 0xff00U) >> 8) \
+                             | ((mp4v2::platform::uint16_t(x) & 0x00ffU) << 8) )
+                           
+#define MP4V2_BYTESWAP_32(x) \
+    mp4v2::platform::uint32_t( ((mp4v2::platform::uint32_t(x) & 0xff000000U) >> 24) \
+                             | ((mp4v2::platform::uint32_t(x) & 0x00ff0000U) >>  8) \
+                             | ((mp4v2::platform::uint32_t(x) & 0x0000ff00U) <<  8) \
+                             | ((mp4v2::platform::uint32_t(x) & 0x000000ffU) << 24) )
+
+#define MP4V2_BYTESWAP_64(x) \
+    mp4v2::platform::uint64_t( ((mp4v2::platform::uint64_t(x) & 0xff00000000000000ULL) >> 56) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x00ff000000000000ULL) >> 40) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x0000ff0000000000ULL) >> 24) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x000000ff00000000ULL) >>  8) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x00000000ff000000ULL) <<  8) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x0000000000ff0000ULL) << 24) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x000000000000ff00ULL) << 40) \
+                             | ((mp4v2::platform::uint64_t(x) & 0x00000000000000ffULL) << 56) )
+                           
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined( __BIG_ENDIAN__ )
+#   define MP4V2_HTONS(x)  (x)
+#   define MP4V2_HTONL(x)  (x)
+#   define MP4V2_NTOHS(x)  (x)
+#   define MP4V2_NTOHL(x)  (x)
+#else
+#   define MP4V2_HTONS(x)  MP4V2_BYTESWAP_16(x)
+#   define MP4V2_HTONL(x)  MP4V2_BYTESWAP_32(x)
+#   define MP4V2_NTOHS(x)  MP4V2_BYTESWAP_16(x)
+#   define MP4V2_NTOHL(x)  MP4V2_BYTESWAP_32(x)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_PLATFORM_ENDIAN_H
diff --git a/libplatform/impl.h b/libplatform/impl.h
new file mode 100644
index 0000000..6c0068c
--- /dev/null
+++ b/libplatform/impl.h
@@ -0,0 +1,13 @@
+#ifndef MP4V2_PLATFORM_IMPL_H
+#define MP4V2_PLATFORM_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libplatform/platform.h"
+#if defined _WIN32
+#include "libplatform/platform_win32_impl.h"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_PLATFORM_IMPL_H
diff --git a/libplatform/io/File.cpp b/libplatform/io/File.cpp
new file mode 100644
index 0000000..2ea0e8d
--- /dev/null
+++ b/libplatform/io/File.cpp
@@ -0,0 +1,201 @@
+#include "libplatform/impl.h"
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+    const File::Size __maxChunkSize = 1024*1024;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+File::File( std::string name_, Mode mode_, FileProvider* provider_ )
+    : _name     ( name_ )
+    , _isOpen   ( false )
+    , _mode     ( mode_ )
+    , _size     ( 0 )
+    , _position ( 0 )
+    , _provider ( provider_ ? *provider_ : standard() )
+    , name      ( _name )
+    , isOpen    ( _isOpen )
+    , mode      ( _mode )
+    , size      ( _size )
+    , position  ( _position )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+File::~File()
+{
+    close();
+    delete &_provider;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+File::setMode( Mode mode_ )
+{
+    _mode = mode_;
+}
+
+void
+File::setName( const std::string& name_ )
+{
+    _name = name_;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+File::open( std::string name_, Mode mode_ )
+{
+    if( _isOpen )
+        return true;
+
+    if( !name_.empty() )
+        setName( name_ );
+    if( mode_ != MODE_UNDEFINED )
+        setMode( mode_ );
+
+    if( _provider.open( _name, _mode ))
+        return true;
+
+    if( _provider.getSize( _size ))
+        return true;
+
+    _isOpen = true;
+    return false;
+}
+
+bool
+File::seek( Size pos )
+{
+    if( !_isOpen )
+        return true;
+
+    if( _provider.seek( pos ))
+        return true;
+    _position = pos;
+    return false;
+}
+
+bool
+File::read( void* buffer, Size size, Size& nin, Size maxChunkSize )
+{
+    nin = 0;
+
+    if( !_isOpen )
+        return true;
+
+    if( _provider.read( buffer, size, nin, maxChunkSize ))
+        return true;
+
+    _position += nin;
+    if( _position > _size )
+        _size = _position;
+
+    return false;
+}
+
+bool
+File::write( const void* buffer, Size size, Size& nout, Size maxChunkSize )
+{
+    nout = 0;
+
+    if( !_isOpen )
+        return true;
+
+    if( _provider.write( buffer, size, nout, maxChunkSize ))
+        return true;
+
+    _position += nout;
+    if( _position > _size )
+        _size = _position;
+
+    return false;
+}
+
+bool
+File::close()
+{
+    if( !_isOpen )
+        return false;
+    if( _provider.close() )
+        return true;
+
+    _isOpen = false;
+    return false;
+}
+
+bool
+File::getSize( Size& nout )
+{
+    if( !_isOpen )
+        return false;
+
+    return _provider.getSize( nout );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CustomFileProvider::CustomFileProvider( const MP4FileProvider& provider )
+    : _handle( NULL )
+{
+    memcpy( &_call, &provider, sizeof(MP4FileProvider) );
+}
+
+bool
+CustomFileProvider::open( std::string name, Mode mode )
+{
+    MP4FileMode fm;
+    switch( mode ) {
+        case MODE_READ:   fm = FILEMODE_READ;   break;
+        case MODE_MODIFY: fm = FILEMODE_MODIFY; break;
+        case MODE_CREATE: fm = FILEMODE_CREATE; break;
+
+        case MODE_UNDEFINED:
+        default:
+            fm = FILEMODE_UNDEFINED;
+            break;
+    }
+
+    _handle = _call.open( name.c_str(), fm );
+    return _handle == NULL;
+}
+
+bool
+CustomFileProvider::seek( Size pos )
+{
+    return _call.seek( _handle, pos );
+}
+
+bool
+CustomFileProvider::read( void* buffer, Size size, Size& nin, Size maxChunkSize )
+{
+    return _call.read( _handle, buffer, size, &nin, maxChunkSize );
+}
+
+bool
+CustomFileProvider::write( const void* buffer, Size size, Size& nout, Size maxChunkSize )
+{
+    return _call.write( _handle, buffer, size, &nout, maxChunkSize );
+}
+
+bool
+CustomFileProvider::close()
+{
+    return _call.close( _handle );
+}
+
+bool
+CustomFileProvider::getSize( Size& nout )
+{
+    return _call.getSize( _handle, &nout );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
diff --git a/libplatform/io/File.h b/libplatform/io/File.h
new file mode 100644
index 0000000..bf8536c
--- /dev/null
+++ b/libplatform/io/File.h
@@ -0,0 +1,222 @@
+#ifndef MP4V2_PLATFORM_IO_FILE_H
+#define MP4V2_PLATFORM_IO_FILE_H
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4V2_EXPORT FileProvider
+{
+public:
+    static FileProvider& standard();
+
+public:
+    //! file operation mode flags
+    enum Mode {
+        MODE_UNDEFINED, //!< undefined
+        MODE_READ,      //!< file may be read
+        MODE_MODIFY,    //!< file may be read/written
+        MODE_CREATE,    //!< file will be created/truncated for read/write
+    };
+
+    //! type used to represent all file sizes and offsets
+    typedef int64_t Size;
+
+public:
+    virtual ~FileProvider() { }
+
+    virtual bool open( std::string name, Mode mode ) = 0;
+    virtual bool seek( Size pos ) = 0;
+    virtual bool read( void* buffer, Size size, Size& nin, Size maxChunkSize ) = 0;
+    virtual bool write( const void* buffer, Size size, Size& nout, Size maxChunkSize ) = 0;
+    virtual bool close() = 0;
+    virtual bool getSize( Size& nout ) = 0;
+
+protected:
+    FileProvider() { }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// File implementation.
+///
+/// File objects model real filesystem files in a 1:1 releationship and always
+/// treated as binary; there are no translations of text content performed.
+///
+/// The interface represents all sizes with a signed 64-bit value, thus
+/// the limit to this interface is 63-bits of size, roughly 9.22 million TB.
+///
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4V2_EXPORT File : public FileProvider
+{
+public:
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Constructor.
+    //!
+    //! A new file object is constructed but not opened.
+    //!
+    //! @param name filename of file object, or empty-string.
+	//!  On Windows, this should be a UTF-8 encoded string.
+    //!  On other platforms, it should be an 8-bit encoding that is
+    //!  appropriate for the platform, locale, file system, etc.
+    //!  (prefer to use UTF-8 when possible).
+    //! @param mode bitmask specifying mode flags.
+    //!     See #Mode for bit constants.
+    //! @param provider a fileprovider instance. If NULL a standard file
+    //!     provider will be used otherwise the supplied provider must be
+    //!     new-allocated and will be delete'd via ~File().
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    explicit File( std::string name = "", Mode mode = MODE_UNDEFINED, FileProvider* = NULL );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Destructor.
+    //!
+    //! File object is destroyed. If the file is opened it is closed prior
+    //! to destruction.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    ~File();
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Open file.
+    //!
+    //! @param name filename of file object, or empty-string to use #name.
+	//!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool open( std::string name = "", Mode mode = MODE_UNDEFINED );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Closes file.
+    //!
+    //! If the file has not been opened or is not considered the
+    //! owner of a filehandle, no action is taken.
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool close();
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Set current file position in bytes.
+    //!
+    //! @param pos new file position in bytes.
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool seek( Size pos );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Binary stream read.
+    //!
+    //! The function reads up to a maximum <b>size</b> bytes from file,
+    //! storing them in <b>buffer</b>. The number of bytes actually read are
+    //! returned in <b>nin</b>.
+    //!
+    //! @param buffer storage for data read from file.
+    //! @param size maximum number of bytes to read from file.
+    //! @param nin output indicating number of bytes read from file.
+    //! @param maxChunkSize maximum chunk size for reads issued to operating
+    //!     system or 0 for default.
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool read( void* buffer, Size size, Size& nin, Size maxChunkSize = 0 );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Binary stream write.
+    //!
+    //! The function writes up to a maximum <b>size</b> bytes from
+    //! <b>buffer</b> to file. The number of bytes actually written are
+    //! returned in <b>nout</b>.
+    //!
+    //! @param buffer data to be written out to file.
+    //! @param size maximum number of bytes to read from file.
+    //! @param nout output indicating number of bytes written to file.
+    //! @param maxChunkSize maximum chunk size for writes issued to operating
+    //!     system or 0 for default.
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool write( const void* buffer, Size size, Size& nout, Size maxChunkSize = 0 );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Get size of file in bytes.
+    //!
+    //! @param nout output indicating the size of the file in bytes.
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    bool getSize( Size& nout );
+
+private:
+    std::string   _name;
+    bool          _isOpen;
+    Mode          _mode;
+    Size          _size;
+    Size          _position;
+    FileProvider& _provider;
+
+public:
+    const std::string& name;      //!< read-only: file pathname or empty-string if not applicable
+    const bool&        isOpen;    //!< read-only: true if file is open
+    const Mode&        mode;      //!< read-only: file mode
+    const Size&        size;      //!< read-only: file size
+    const Size&        position;  //!< read-only: file position
+
+public:
+    void setName( const std::string& name );
+    void setMode( Mode mode );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class CustomFileProvider : public FileProvider
+{
+public:
+    CustomFileProvider( const MP4FileProvider& );
+
+    bool open( std::string name, Mode mode );
+    bool seek( Size pos );
+    bool read( void* buffer, Size size, Size& nin, Size maxChunkSize );
+    bool write( const void* buffer, Size size, Size& nout, Size maxChunkSize );
+    bool close();
+    bool getSize( Size& nout );
+
+private:
+    MP4FileProvider _call;
+    void*           _handle;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
+
+#endif // MP4V2_PLATFORM_IO_FILE_H
diff --git a/libplatform/io/FileSystem.cpp b/libplatform/io/FileSystem.cpp
new file mode 100644
index 0000000..073b026
--- /dev/null
+++ b/libplatform/io/FileSystem.cpp
@@ -0,0 +1,103 @@
+#include "libplatform/impl.h"
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+FileSystem::pathnameCleanup( string& name )
+{
+    string bad;
+
+    // fold repeating directory separators
+    bad = DIR_SEPARATOR;
+    bad += DIR_SEPARATOR;
+    for( string::size_type pos = name.find( bad );
+         pos != string::npos;
+         pos = name.find( bad, pos ) )
+    {
+        name.replace( pos, bad.length(), DIR_SEPARATOR );
+    }
+
+    // replace occurances of /./ with /
+    bad = DIR_SEPARATOR;
+    bad += '.';
+    bad += DIR_SEPARATOR;
+    for( string::size_type pos = name.find( bad );
+         pos != string::npos;
+         pos = name.find( bad, pos ) )
+    {
+        name.replace( pos, bad.length(), DIR_SEPARATOR );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+FileSystem::pathnameOnlyExtension( string& name )
+{
+    // compute basename
+    string::size_type dot_pos = name.rfind( '.' );
+    string::size_type slash_pos = name.rfind( DIR_SEPARATOR );
+
+    // dot_pos must be after slash_pos
+    if( slash_pos != string::npos && dot_pos < slash_pos )
+        dot_pos = string::npos;
+
+    // return empty-string if no dot
+    if( dot_pos == string::npos ) {
+        name.resize( 0 );
+        return;
+    }
+
+    name = name.substr( dot_pos + 1 );
+    pathnameCleanup( name );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+FileSystem::pathnameStripExtension( string& name )
+{
+    pathnameCleanup( name );
+
+    // compute basename
+    string::size_type dot_pos = name.rfind( '.' );
+    string::size_type slash_pos = name.rfind( DIR_SEPARATOR );
+
+    // dot_pos must be after slash_pos
+    if( slash_pos != string::npos && dot_pos < slash_pos )
+        dot_pos = string::npos;
+
+    // chop extension
+    if( dot_pos != string::npos )
+        name.resize( dot_pos );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+FileSystem::pathnameTemp( string& name, string dir, string prefix, string suffix )
+{
+    ostringstream buf;
+
+    if( !dir.empty() ) {
+        buf << dir;
+
+        // add dir separator if needed
+        // TODO there's a platform specific bug here, if someone passes in a pathname ending
+        // in '\', which would be legitimate on Windows.
+        if( dir[dir.length()-1] != '/' )
+            buf << '/';
+    }
+
+    buf << prefix;
+    buf << setfill('0') << setw(8) << number::random32();
+    buf << suffix;
+
+    name = buf.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
diff --git a/libplatform/io/FileSystem.h b/libplatform/io/FileSystem.h
new file mode 100644
index 0000000..b348752
--- /dev/null
+++ b/libplatform/io/FileSystem.h
@@ -0,0 +1,215 @@
+#ifndef MP4V2_PLATFORM_IO_FILESYSTEM_H
+#define MP4V2_PLATFORM_IO_FILESYSTEM_H
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// General file-system abstraction.
+///
+/// FileSystem abstracts operations on files and directories.
+///
+///////////////////////////////////////////////////////////////////////////////
+class MP4V2_EXPORT FileSystem
+{
+public:
+    static string DIR_SEPARATOR;  //!< separator string used in file pathnames
+    static string PATH_SEPARATOR; //!< separator string used in search-paths
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Query file presence.
+    //! Check if <b>name</b> exists.
+    //! @param name filename to query.
+	//!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //! @return true if present, false otherwise.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static bool exists( std::string name );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Query directory type.
+    //! Check if <b>name</b> exists and is a directory.
+    //! @param name pathname to query.
+	//!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //! @return true if directory, false otherwise.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static bool isDirectory( std::string name );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Query file type.
+    //! Check if <b>name</b> exists and is a file.
+    //!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //! @param name filename to query.
+    //! @return true if file, false otherwise.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static bool isFile( std::string name );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Query file size.
+    //! Check if <b>name</b> exists and is a file.
+    //! @param name filename to query.
+    //!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //! @param size output indicating file size in bytes.
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static bool getFileSize( std::string name, File::Size& size );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Rename file or directory.
+    //!
+    //! Rename <b>oldname</b> to <b>newname</b>.
+    //! If <b>newname</b> exists, it is first removed.
+    //! Both <b>oldname</b> and <b>newname</b> must be of the same type;
+    //! that is, both must be either files or directories and must reside on
+    //! the same filesystem.
+    //!
+    //! @param oldname existing pathname to rename.
+    //!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //! @param newname new pathname.
+    //!     On Windows, this should be a UTF-8 encoded string.
+    //!     On other platforms, it should be an 8-bit encoding that is
+    //!     appropriate for the platform, locale, file system, etc.
+    //!     (prefer to use UTF-8 when possible).
+    //!
+    //! @return true on failure, false on success.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static bool rename( std::string oldname, std::string newname );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Generate temporary pathname.
+    //!
+    //! @param name output containing generated pathname.
+    //! @param dir relative or absolute directory of pathname.
+    //! @param prefix text prepended to base pathname.
+    //! @param suffix text appended to base pathname.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static void pathnameTemp( string& name, string dir = ".", string prefix = "tmp", string suffix = "" );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Cleanup pathname.
+    //!
+    //! Redundant (repeating) directory-separators are folded into a single
+    //! directory-separator.
+    //!
+    //! Redundant /./ are folded into a single directory-separator.
+    //!
+    //! @param name pathname to modify.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static void pathnameCleanup( string& name );
+
+#if 0
+TODO-KB: implement
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Remove everything after the last directory component.
+    //!
+    //! A pathname cleanup is always performed. See pathnameCleanup().
+    //! If no directory component is present then "." is assumed.
+    //!
+    //! @param name pathname to modify.
+    //! @param trailing when true all results are suffixed with exactly one
+    //!     directory-separator, otherwise the result is guaranteed to not
+    //!     end in a directory-separator.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+    static void pathnameOnlyDirectory( string& name, bool trailing = true );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Remove everything except the file component of pathname.
+    //!
+    //! A pathname cleanup is always performed. See pathnameCleanup().
+    //! If no file component exists then an empty-string is output.
+    //! A file component may include an extension.
+    //!
+    //! @param name pathname to modify.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+    static void pathnameOnlyFile( string& name );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Remove everything except file basename.
+    //!
+    //! A pathname cleanup is always performed. See pathnameCleanup().
+    //! A basename is considered to be everything before the last '.'
+    //! in the file component of a pathname.
+    //! If no file extension exists then an empty-string is output.
+    //!
+    //! @param name pathname to modify.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+    static void pathnameOnlyBasename( string& name );
+#endif
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Remove everything except file extension.
+    //!
+    //! A pathname cleanup is always performed. See pathnameCleanup().
+    //! A file extension is considered to everything <b>after</b>
+    //! the last '.' in the file component of a pathname.
+    //! If no file extension exists then an empty-string is output.
+    //!
+    //! @param name pathname to modify.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static void pathnameOnlyExtension( string& name );
+
+    ///////////////////////////////////////////////////////////////////////////
+    //!
+    //! Remove file extension from pathname.
+    //!
+    //! A pathname cleanup is always performed. See pathnameCleanup().
+    //! A file extension is considered to everything <b>after</b>
+    //! the last '.' in the file component of a pathname.
+    //! The last '.' is also removed.
+    //!
+    //! @param name pathname to modify.
+    //!
+    ///////////////////////////////////////////////////////////////////////////
+
+    static void pathnameStripExtension( string& name );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
+
+#endif // MP4V2_PLATFORM_IO_FILESYSTEM_H
diff --git a/libplatform/io/FileSystem_posix.cpp b/libplatform/io/FileSystem_posix.cpp
new file mode 100644
index 0000000..46cf933
--- /dev/null
+++ b/libplatform/io/FileSystem_posix.cpp
@@ -0,0 +1,65 @@
+#include "libplatform/impl.h"
+#include <sys/stat.h>
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::exists( string path_ )
+{
+    struct stat buf;
+    return stat( path_.c_str(), &buf ) == 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::isDirectory( string path_ )
+{
+    struct stat buf;
+    if( stat( path_.c_str(), &buf ))
+        return false;
+    return S_ISDIR( buf.st_mode );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::isFile( string path_ )
+{
+    struct stat buf;
+    if( stat( path_.c_str(), &buf ))
+        return false;
+    return S_ISREG( buf.st_mode );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::getFileSize( string path_, File::Size& size_ )
+{
+    size_ = 0;
+    struct stat buf;
+    if( stat( path_.c_str(), &buf ))
+        return true;
+    size_ = buf.st_size;
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::rename( string from, string to )
+{
+    return ::rename( from.c_str(), to.c_str() ) != 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string FileSystem::DIR_SEPARATOR  = "/";
+string FileSystem::PATH_SEPARATOR = ":";
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
diff --git a/libplatform/io/FileSystem_win32.cpp b/libplatform/io/FileSystem_win32.cpp
new file mode 100644
index 0000000..4a21381
--- /dev/null
+++ b/libplatform/io/FileSystem_win32.cpp
@@ -0,0 +1,146 @@
+#include "src/impl.h"
+#include "libplatform/impl.h" /* for platform_win32_impl.h which declares Utf8ToFilename */
+#include <windows.h>
+
+namespace mp4v2 {
+    using namespace impl;
+}
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+static DWORD    getAttributes ( string   path_ );
+
+/**
+ * Call GetFileAttributesW throw exceptions for errors
+ *
+ * @param path_ the path to get attributes for
+ *
+ * @retval INVALID_FILE_ATTRIBUTES @p path_ doesn't exist
+ * @retval anything else the attributes of @p path_
+ */
+static DWORD
+getAttributes ( string  path_ )
+{
+    win32::Utf8ToFilename filename(path_);
+
+    if (!filename.IsUTF16Valid())
+    {
+        // throw an exception to avoid changing the
+        // signature of this function and dealing with all
+        // the places it's called.
+        ostringstream msg;
+        msg << "can't convert file to UTF-16(" << filename.utf8 << ")";
+        throw new Exception(msg.str(),__FILE__,__LINE__,__FUNCTION__);
+    }
+
+    DWORD attributes = ::GetFileAttributesW(filename);
+    if( attributes == INVALID_FILE_ATTRIBUTES )
+    {
+        DWORD last_err = GetLastError();
+
+        // Distinguish between an error and the path not existing
+        if ((last_err == ERROR_FILE_NOT_FOUND) || (last_err == ERROR_PATH_NOT_FOUND))
+        {
+            return attributes;
+        }
+
+        // Anything else is an error
+        ostringstream msg;
+        msg << "GetFileAttributes(" << filename.utf8 << ") failed (" << last_err << ")";
+        throw new Exception(msg.str(),__FILE__,__LINE__,__FUNCTION__);
+    }
+
+    // path exists so return its attributes
+    return attributes;
+}
+
+bool
+FileSystem::exists( string path_ )
+{
+    return( getAttributes(path_) != INVALID_FILE_ATTRIBUTES );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::isDirectory( string path_ )
+{
+    DWORD attributes = getAttributes( path_ );
+    if( attributes == INVALID_FILE_ATTRIBUTES )
+        return false;
+
+    return ( ( attributes & FILE_ATTRIBUTE_DIRECTORY ) == FILE_ATTRIBUTE_DIRECTORY );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::isFile( string path_ )
+{
+    DWORD attributes = getAttributes( path_ );
+    if( attributes == INVALID_FILE_ATTRIBUTES )
+        return false;
+
+    return ( ( attributes & FILE_ATTRIBUTE_DIRECTORY ) != FILE_ATTRIBUTE_DIRECTORY );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::getFileSize( string path_, File::Size& size_ )
+{
+    win32::Utf8ToFilename filename(path_);
+
+    if (!filename.IsUTF16Valid())
+    {
+        // The logging is done
+        return true;
+    }
+
+    size_ = 0;
+    WIN32_FILE_ATTRIBUTE_DATA data = {0};
+    if( !GetFileAttributesExW( filename, GetFileExInfoStandard, (LPVOID)&data ) )
+    {
+        log.errorf("%s: GetFileAttributesExW(%s) failed (%d)",__FUNCTION__,filename.utf8.c_str(),
+                   GetLastError());
+        return true;
+    }
+
+    size_ = ( (File::Size)data.nFileSizeHigh << 32 ) | data.nFileSizeLow;
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileSystem::rename( string from, string to )
+{
+    win32::Utf8ToFilename from_file(from);
+    win32::Utf8ToFilename to_file(to);
+
+    if (!from_file.IsUTF16Valid() || !to_file.IsUTF16Valid())
+    {
+        return true;
+    }
+
+    if (!::MoveFileExW( from_file, to_file,
+                        MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH ) )
+    {
+        log.errorf("%s: MoveFileExW(%s,%s) failed (%d)",__FUNCTION__,from_file.utf8.c_str(),to_file.utf8.c_str(),
+                   GetLastError());
+        return true;
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string FileSystem::DIR_SEPARATOR  = "\\";
+string FileSystem::PATH_SEPARATOR = ";";
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
diff --git a/libplatform/io/File_posix.cpp b/libplatform/io/File_posix.cpp
new file mode 100644
index 0000000..1c961ab
--- /dev/null
+++ b/libplatform/io/File_posix.cpp
@@ -0,0 +1,123 @@
+#include "libplatform/impl.h"
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StandardFileProvider : public FileProvider
+{
+public:
+    StandardFileProvider();
+
+    bool open( std::string name, Mode mode );
+    bool seek( Size pos );
+    bool read( void* buffer, Size size, Size& nin, Size maxChunkSize );
+    bool write( const void* buffer, Size size, Size& nout, Size maxChunkSize );
+    bool close();
+    bool getSize( Size& nout );
+
+private:
+    bool         _seekg;
+    bool         _seekp;
+    std::fstream _fstream;
+    std::string  _name;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+StandardFileProvider::StandardFileProvider()
+    : _seekg ( false )
+    , _seekp ( false )
+{
+}
+
+bool
+StandardFileProvider::open( std::string name, Mode mode )
+{
+    ios::openmode om = ios::binary;
+    switch( mode ) {
+        case MODE_UNDEFINED:
+        case MODE_READ:
+        default:
+            om |= ios::in;
+            _seekg = true;
+            _seekp = false;
+            break;
+
+        case MODE_MODIFY:
+            om |= ios::in | ios::out;
+            _seekg = true;
+            _seekp = true;
+            break;
+
+        case MODE_CREATE:
+            om |= ios::in | ios::out | ios::trunc;
+            _seekg = true;
+            _seekp = true;
+            break;
+    }
+
+    _fstream.open( name.c_str(), om );
+    _name = name;
+    return _fstream.fail();
+}
+
+bool
+StandardFileProvider::seek( Size pos )
+{
+    if( _seekg )
+        _fstream.seekg( pos, ios::beg );
+    if( _seekp )
+        _fstream.seekp( pos, ios::beg );
+    return _fstream.fail();
+}
+
+bool
+StandardFileProvider::read( void* buffer, Size size, Size& nin, Size maxChunkSize )
+{
+    _fstream.read( (char*)buffer, size );
+    if( _fstream.fail() )
+        return true;
+    nin = _fstream.gcount();
+    return false;
+}
+
+bool
+StandardFileProvider::write( const void* buffer, Size size, Size& nout, Size maxChunkSize )
+{
+    _fstream.write( (const char*)buffer, size );
+    if( _fstream.fail() )
+        return true;
+    nout = size;
+    return false;
+}
+
+bool
+StandardFileProvider::close()
+{
+    _fstream.close();
+    return _fstream.fail();
+}
+
+bool
+StandardFileProvider::getSize( Size& nout )
+{
+    bool retval;
+
+    // getFileSize will log if it fails
+    retval = FileSystem::getFileSize( _name, nout );
+
+    return retval;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FileProvider&
+FileProvider::standard()
+{
+    return *new StandardFileProvider();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
diff --git a/libplatform/io/File_win32.cpp b/libplatform/io/File_win32.cpp
new file mode 100644
index 0000000..0b3ebe8
--- /dev/null
+++ b/libplatform/io/File_win32.cpp
@@ -0,0 +1,287 @@
+#include "src/impl.h"
+#include "libplatform/impl.h" /* for platform_win32_impl.h which declares Utf8ToFilename */
+#include <windows.h>
+
+namespace mp4v2 {
+    using namespace impl;
+}
+
+/**
+ * Set this to 1 to compile in extra debugging
+ */
+#define EXTRA_DEBUG 0
+
+/**
+ * @def LOG_PRINTF
+ *
+ * call log.printf if EXTRA_DEBUG is defined to 1.  Do
+ * nothing otherwise
+ */
+#if EXTRA_DEBUG
+#define LOG_PRINTF(X) log.printf X
+#else
+#define LOG_PRINTF(X)
+#endif
+
+namespace mp4v2 { namespace platform { namespace io {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StandardFileProvider : public FileProvider
+{
+public:
+    StandardFileProvider();
+
+    bool open( std::string name, Mode mode );
+    bool seek( Size pos );
+    bool read( void* buffer, Size size, Size& nin, Size maxChunkSize );
+    bool write( const void* buffer, Size size, Size& nout, Size maxChunkSize );
+    bool close();
+    bool getSize( Size& nout );
+
+private:
+    HANDLE _handle;
+
+    /**
+     * The UTF-8 encoded file name
+     */
+    std::string _name;
+
+    /**
+     * Argument for FileSystem::getFileSize()
+     */
+    std::string _orig_name;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+StandardFileProvider::StandardFileProvider()
+    : _handle( INVALID_HANDLE_VALUE )
+{
+}
+
+/**
+ * Open a file
+ *
+ * @param name the name of a file to open
+ * @param mode the mode to open @p name
+ *
+ * @retval false successfully opened @p name
+ * @retval true error opening @p name
+ */
+bool
+StandardFileProvider::open( std::string name, Mode mode )
+{
+    _orig_name = name;
+
+    DWORD access = 0;
+    DWORD share  = 0;
+    DWORD crdisp = 0;
+    DWORD flags  = FILE_ATTRIBUTE_NORMAL;
+
+    switch( mode ) {
+        case MODE_UNDEFINED:
+        case MODE_READ:
+        default:
+            access |= GENERIC_READ;
+            share  |= FILE_SHARE_READ;
+            crdisp |= OPEN_EXISTING;
+            break;
+
+        case MODE_MODIFY:
+            access |= GENERIC_READ | GENERIC_WRITE;
+            share  |= FILE_SHARE_READ;
+            crdisp |= OPEN_EXISTING;
+            break;
+
+        case MODE_CREATE:
+            access |= GENERIC_READ | GENERIC_WRITE;
+            share  |= FILE_SHARE_READ;
+            crdisp |= CREATE_ALWAYS;
+            break;
+    }
+
+    win32::Utf8ToFilename filename(name);
+
+    if (!filename.IsUTF16Valid())
+    {
+        // The logging is done
+        return true;
+    }
+
+    ASSERT(LPCWSTR(filename));
+    _handle = CreateFileW( filename, access, share, NULL, crdisp, flags, NULL );
+    if (_handle == INVALID_HANDLE_VALUE)
+    {
+        log.errorf("%s: CreateFileW(%s) failed (%d)",__FUNCTION__,filename.utf8.c_str(),GetLastError());
+        return true;
+    }
+
+    /*
+    ** Make a copy of the name for future log messages, etc.
+    */
+    log.verbose2f("%s: CreateFileW(%s) succeeded",__FUNCTION__,filename.utf8.c_str());
+
+    _name = filename.utf8;
+    return false;
+}
+
+/**
+ * Seek to an offset in the file
+ *
+ * @param pos the offset from the beginning of the file to
+ * seek to
+ *
+ * @retval false successfully seeked to @p pos
+ * @retval true error seeking to @p pos
+ */
+bool
+StandardFileProvider::seek( Size pos )
+{
+    LARGE_INTEGER n;
+
+    ASSERT(_handle != INVALID_HANDLE_VALUE);
+
+    n.QuadPart = pos;
+    if (!SetFilePointerEx( _handle, n, NULL, FILE_BEGIN ))
+    {
+        log.errorf("%s: SetFilePointerEx(%s,%" PRId64 ") failed (%d)",__FUNCTION__,_name.c_str(),
+                                pos,GetLastError());
+        return true;
+    }
+
+    return false;
+}
+
+/**
+ * Read from the file
+ *
+ * @param buffer populated with at most @p size bytes from
+ * the file
+ *
+ * @param size the maximum number of bytes to read
+ *
+ * @param nin the 
+ *
+ * @retval false successfully read from the file
+ * @retval true error reading from the file
+ */
+bool
+StandardFileProvider::read( void* buffer, Size size, Size& nin, Size maxChunkSize )
+{
+    DWORD nread = 0;
+
+    ASSERT(_handle != INVALID_HANDLE_VALUE);
+
+    // ReadFile takes a DWORD for number of bytes to read so
+    // make sure we're not asking for more than fits.
+    // MAXDWORD from WinNT.h.
+    ASSERT(size <= MAXDWORD);
+    if( ReadFile( _handle, buffer, (DWORD)(size & MAXDWORD), &nread, NULL ) == 0 )
+    {
+        log.errorf("%s: ReadFile(%s,%d) failed (%d)",__FUNCTION__,_name.c_str(),
+                   (DWORD)(size & MAXDWORD),GetLastError());
+        return true;
+    }
+    LOG_PRINTF((MP4_LOG_VERBOSE3,"%s: ReadFile(%s,%d) succeeded: read %d byte(s)",__FUNCTION__,
+               _name.c_str(),(DWORD)(size & MAXDWORD),nread));
+    nin = nread;
+    return false;
+}
+
+/**
+ * Write to the file
+ *
+ * @param buffer the data to write
+ *
+ * @param size the number of bytes of @p buffer to write
+ *
+ * @param nout populated with the number of bytes actually
+ * written if the function succeeds
+ *
+ * @retval false successfully wrote to the file
+ * @retval true error writing to the file
+ */
+bool
+StandardFileProvider::write( const void* buffer, Size size, Size& nout, Size maxChunkSize )
+{
+    DWORD nwrote = 0;
+
+    ASSERT(_handle != INVALID_HANDLE_VALUE);
+
+    // ReadFile takes a DWORD for number of bytes to read so
+    // make sure we're not asking for more than fits.
+    // MAXDWORD from WinNT.h.
+    ASSERT(size <= MAXDWORD);
+    if( WriteFile( _handle, buffer, (DWORD)(size & MAXDWORD), &nwrote, NULL ) == 0 )
+    {
+        log.errorf("%s: WriteFile(%s,%d) failed (%d)",__FUNCTION__,_name.c_str(),
+                   (DWORD)(size & MAXDWORD),GetLastError());
+        return true;
+    }
+    log.verbose2f("%s: WriteFile(%s,%d) succeeded: wrote %d byte(s)",__FUNCTION__,
+                  _name.c_str(),(DWORD)(size & MAXDWORD),nwrote);
+    nout = nwrote;
+    return false;
+}
+
+/**
+ * Close the file
+ *
+ * @retval false successfully closed the file
+ * @retval true error closing the file
+ */
+bool
+StandardFileProvider::close()
+{
+    BOOL retval;
+
+    retval = CloseHandle( _handle );
+    if (!retval)
+    {
+        log.errorf("%s: CloseHandle(%s) failed (%d)",__FUNCTION__,
+                   _name.c_str(),GetLastError());
+    }
+
+    // Whether we succeeded or not, clear the handle and
+    // forget the name
+    _handle = INVALID_HANDLE_VALUE;
+    _name.clear();
+
+    // CloseHandle return 0/false to indicate failure, but
+    // we return 0/false to indicate success, so negate.
+    return !retval;
+}
+
+/**
+ * Get the size of a the file in bytes
+ *
+ * @param nout populated with the size of the file in
+ * bytes if the function succeeds
+ *
+ * @retval false successfully got the file size
+ * @retval true error getting the file size
+ */
+bool
+StandardFileProvider::getSize( Size& nout )
+{
+    BOOL retval;
+
+    // getFileSize will log if it fails
+    // (cannot use _name because it may have UNC prefix)
+    retval = FileSystem::getFileSize( _orig_name, nout );
+
+    return retval;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+FileProvider&
+FileProvider::standard()
+{
+    return *new StandardFileProvider();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::io
diff --git a/libplatform/number/random.h b/libplatform/number/random.h
new file mode 100644
index 0000000..9d0f105
--- /dev/null
+++ b/libplatform/number/random.h
@@ -0,0 +1,18 @@
+#ifndef MP4V2_PLATFORM_NUMBER_RANDOM_H
+#define MP4V2_PLATFORM_NUMBER_RANDOM_H
+
+namespace mp4v2 { namespace platform { namespace number {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Generate 32-bit pseudo-random number.
+MP4V2_EXPORT uint32_t random32();
+
+/// Seed pseudo-random number generator.
+MP4V2_EXPORT void srandom( uint32_t );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::number
+
+#endif // MP4V2_PLATFORM_NUMBER_RANDOM_H
diff --git a/libplatform/number/random_posix.cpp b/libplatform/number/random_posix.cpp
new file mode 100644
index 0000000..0bdcd1d
--- /dev/null
+++ b/libplatform/number/random_posix.cpp
@@ -0,0 +1,24 @@
+#include "libplatform/impl.h"
+#include <stdlib.h>
+
+namespace mp4v2 { namespace platform { namespace number {
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t
+random32()
+{
+    return uint32_t( ::random() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+srandom( uint32_t seed )
+{
+    ::srandom( seed );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::time
diff --git a/libplatform/number/random_win32.cpp b/libplatform/number/random_win32.cpp
new file mode 100644
index 0000000..f884e5d
--- /dev/null
+++ b/libplatform/number/random_win32.cpp
@@ -0,0 +1,24 @@
+#include "libplatform/impl.h"
+#include <stdlib.h>
+
+namespace mp4v2 { namespace platform { namespace number {
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t
+random32()
+{
+    return uint32_t( ::rand() << 16 | ::rand() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+srandom( uint32_t seed )
+{
+    ::srand( seed );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::time
diff --git a/libplatform/platform.h b/libplatform/platform.h
new file mode 100644
index 0000000..2868f77
--- /dev/null
+++ b/libplatform/platform.h
@@ -0,0 +1,43 @@
+#ifndef MP4V2_PLATFORM_PLATFORM_H
+#define MP4V2_PLATFORM_PLATFORM_H
+
+/// @namespace mp4v2::platform (private) Platform abstraction.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+///
+/// This namespace implements platform abstractions that are useful for
+/// keeping the code base portable.
+
+/// @namespace mp4v2::platform::io (private) I/O.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+
+/// @namespace mp4v2::platform::number (private) Number.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+
+/// @namespace mp4v2::platform::sys (private) System.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if defined( _WIN32 )
+#   include "libplatform/platform_win32.h"
+#else
+#   include "libplatform/platform_posix.h"
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libplatform/warning.h"
+#include "libplatform/endian.h"
+
+#include "libplatform/io/File.h"
+#include "libplatform/io/FileSystem.h"
+
+#include "libplatform/number/random.h"
+#include "libplatform/process/process.h"
+#include "libplatform/prog/option.h"
+#include "libplatform/sys/error.h"
+#include "libplatform/time/time.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_PLATFORM_PLATFORM_H
diff --git a/libplatform/platform_base.h b/libplatform/platform_base.h
new file mode 100644
index 0000000..5b6a263
--- /dev/null
+++ b/libplatform/platform_base.h
@@ -0,0 +1,34 @@
+#ifndef MP4V2_PLATFORM_BASE_H
+#define MP4V2_PLATFORM_BASE_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <list>
+#include <locale>
+#include <map>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cassert>
+#include <cctype>
+#include <cerrno>
+#include <climits>
+#include <cmath>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+#include <cwchar>
+#include <cwctype>
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_PLATFORM_BASE_H
diff --git a/libplatform/platform_posix.h b/libplatform/platform_posix.h
new file mode 100644
index 0000000..af5901a
--- /dev/null
+++ b/libplatform/platform_posix.h
@@ -0,0 +1,77 @@
+#ifndef MP4V2_PLATFORM_POSIX_H
+#define MP4V2_PLATFORM_POSIX_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libplatform/config.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+// constant macros are not usually used with C++ so the standard mechanism to
+// activate it is to define before stdint.h
+#ifndef __STDC_CONSTANT_MACROS
+#   define __STDC_CONSTANT_MACROS
+#endif
+
+// format macros are not usually used with C++ so the standard mechanism to
+// activate it is to define before inttypes.h
+#ifndef __STDC_FORMAT_MACROS
+#   define __STDC_FORMAT_MACROS
+#endif
+
+#ifdef NEED_LFS_ACTIVATION
+#   ifndef _LARGEFILE_SOURCE
+#       define _LARGEFILE_SOURCE
+#       define _FILE_OFFSET_BITS 64
+#   endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libplatform/platform_base.h"
+#include <inttypes.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <mp4v2/mp4v2.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mp4v2 { namespace platform {
+    using namespace std;
+
+    using ::int8_t;
+    using ::int16_t;
+    using ::int32_t;
+    using ::int64_t;
+
+    using ::uint8_t;
+    using ::uint16_t;
+    using ::uint32_t;
+    using ::uint64_t;
+}} // namespace mp4v2::platform
+
+///////////////////////////////////////////////////////////////////////////////
+
+// win32 platform requires O_BINARY when using old open() calls so we add
+// this harmless bit-flag for posix to avoid .cpp platform conditionals
+#ifndef O_BINARY
+#   define O_BINARY  0
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// ARM seems to require integer instructions operands to have 4-byte alignment
+// so we set this macro to for some int<->string code to manually copy string
+// bytes into an int which aligns it. This is much easier than trying to
+// align pertinent string data (constants) from in text sections.
+#if defined( __arm__ )
+#   define MP4V2_INTSTRING_ALIGNMENT 1
+#else
+#   undef MP4V2_INTSTRING_ALIGNMENT
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_PLATFORM_POSIX_H
diff --git a/libplatform/platform_win32.cpp b/libplatform/platform_win32.cpp
new file mode 100644
index 0000000..5518150
--- /dev/null
+++ b/libplatform/platform_win32.cpp
@@ -0,0 +1,1091 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 David Byron.
+//  Portions created by David Byron are Copyright (C) 2010.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+#include "libplatform/impl.h" /* for platform_win32_impl.h which declares Utf8ToFilename */
+#include <algorithm> /* for replace */
+#include <windows.h>
+
+namespace mp4v2 {
+    using namespace impl;
+}
+
+/**
+ * Set this to 1 to compile in extra debugging
+ */
+#define EXTRA_DEBUG 0
+
+/**
+ * @def LOG_PRINTF
+ *
+ * call log.printf if EXTRA_DEBUG is defined to 1.  Do
+ * nothing otherwise
+ */
+#if EXTRA_DEBUG
+#define LOG_PRINTF(X) log.printf X
+#else
+#define LOG_PRINTF(X)
+#endif
+
+/**
+ * Section 2.13 "Special Characters and Noncharacters" of
+ * _The Unicode Standard, Version 5.0_
+ * (http://www.unicode.org/versions/Unicode5.0.0/bookmarks.html)
+ * defines "The Replacement Character" U+FFFD as the
+ * "general substitute character" that "can be substituted
+ * for any 'unknown' character in another encoding that can
+ * not be mapped in terms of known Unicode characters"
+ *
+ * See also section D.7 of 10646.
+ */
+#define REPLACEMENT_CHAR    0xFFFD
+
+namespace mp4v2 { namespace platform { namespace win32 {
+
+/**
+ * A structure to store the number of characters required to
+ * encode a particular UCS-4 character in UTF-8
+ */
+struct utf8_len_info
+{
+    /**
+     * This structure applies to a number >= @p range_min.
+     */
+    UINT32      range_min;
+
+    /**
+     * This structure applies to a number <= @p range_max.
+     */
+    UINT32      range_max;
+
+    /**
+     * The number of characters required to encode a number
+     * in [@p range_min,@p range_max] as UTF-8.
+     */
+    size_t      num_chars;
+};
+
+/**
+ * A structure to store the number of characters required to
+ * encode a particular UCS-4 character in UTF-8.  For now
+ * we're using wide characters (which according to
+ * http://msdn.microsoft.com/en-us/library/ms776414.aspx
+ * means UTF-16 since Windows 2000) so we're only using up
+ * to 4-byte UTF-8 sequences.  Parts of the range aren't
+ * valid (e.g. [U+D800,U+DFFF] but that's handled elsewhere.
+ */
+static struct utf8_len_info s_len_info[] =
+{
+    { 0x00000000, 0x0000007F, 1 },
+    { 0x00000080, 0x000007FF, 2 },
+    { 0x00000800, 0x0000FFFF, 3 },
+    { 0x00010000, 0x001FFFFF, 4 },
+    { 0x00200000, 0x03FFFFFF, 5 },
+    { 0x04000000, 0x7FFFFFFF, 6 }
+};
+
+/**
+ * Utf8ToFilename constructor
+ *
+ * @param utf8string a UTF-8 encoded string that does not
+ * begin with \\\?\\ nor \\\?\\UNC\\
+ *
+ * @see IsValidUTF16 to see whether the constructor
+ * succeeded
+ */
+Utf8ToFilename::Utf8ToFilename( const string &utf8string )
+    : _wideCharString( NULL )
+      , utf8( _utf8 )
+{
+    // See
+    // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
+    // for notes about path lengths, prefixes, etc.  The
+    // goal is to support the longest path possible.
+    // Relative paths are limited to 260 characters but
+    // absolute paths can be up to about 32767
+    // characters if properly prefixed.
+
+    // If utf8string is a relative path, convert it to
+    // UTF-16 and be done.
+    if (!IsAbsolute(utf8string))
+    {
+        _wideCharString = ConvertToUTF16(utf8string);
+        return;
+    }
+
+    // Since the prefix has backslashes, convert any forward
+    // slashes in utf8string to backslashes to keep Windows
+    // happy
+    const string *utf8ToUse = &utf8string;
+    string forwardSlash;
+
+    if (utf8string.find('/') != std::string::npos)
+    {
+        forwardSlash = utf8string;
+        std::replace(forwardSlash.begin(),forwardSlash.end(),'/','\\');
+        utf8ToUse = &forwardSlash;
+    }
+    ASSERT(utf8ToUse);
+    ASSERT((*utf8ToUse).length() > 0);
+
+    // utf8string is an absolute path.  It could be a
+    // UNC path (\\host\path).  The prefix is different
+    // for UNC paths than it is for non-UNC paths.
+    string prefixedPath;
+
+    if (IsUncPath(*utf8ToUse))
+    {
+        // utf8string begins with two backslashes, but
+        // with a prefix we only need one so we can't
+        // just prepend a prefix.
+        prefixedPath = "\\\\?\\UNC" + (*utf8ToUse).substr(1);
+    }
+    else
+    {
+        prefixedPath = "\\\\?\\" + *utf8ToUse;
+    }
+
+    // Transform prefixedPath to UTF-16 so it's
+    // appropriate for CreateFileW
+    _wideCharString = ConvertToUTF16(prefixedPath);
+}
+
+Utf8ToFilename::~Utf8ToFilename( )
+{
+    if( _wideCharString != NULL )
+    {
+        free(_wideCharString);
+        _wideCharString = NULL;
+    }
+}
+
+/**
+ * Convert a UTF-8 encoded string to a UTF-16 string
+ *
+ * @param utf8 the NUL-terminated UTF-8 string to decode
+ *
+ * @retval NULL error allocating memory for UTF-16 string
+ *
+ * @retval non-NULL NUL-terminated UTF-16 version of @p
+ * utf8.  Invalid portions of UTF-8 are represented by a
+ * replacement character U+FFFD.  The caller is
+ * responsible for freeing this memory.
+ */
+wchar_t *
+Utf8ToFilename::ConvertToUTF16 ( const string &utf8string )
+{
+    int         num_bytes;
+    size_t      num_chars;
+    wchar_t     *retval;
+
+    ASSERT(sizeof(wchar_t) == 2);
+
+    // Store the utf8 string in our member variable so it's
+    // available
+    _utf8 = utf8string;
+
+    // We need to find out how many characters we're dealing
+    // with so we know how much memory to allocate.  At the
+    // same time, it's possible that the string we've been
+    // given isn't valid UTF-8.  So, just use the length of
+    // the string we've been given as the number of
+    // characters to allocate.  The decoded string can't be
+    // longer than this, even taking into account surrogate
+    // pairs since they require 4 UTF-8 characters but only
+    // two UTF-16 character elements.
+    num_chars = utf8string.length();
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: entry point (%d character string)",
+                __FUNCTION__,num_chars));
+
+    /*
+    ** Allocate space for the decoded string.  Add one
+    ** for the NUL terminator.
+    */
+    num_bytes = (num_chars + 1) * sizeof(wchar_t);
+    retval = (wchar_t *)malloc(num_bytes);
+    if (!retval)
+    {
+        log.errorf("%s: error allocating memory for %d byte(s)",__FUNCTION__,num_bytes);
+        return NULL;
+    }
+
+    /*
+    ** ConvertToUTF16Buf zeroes out the memory so don't
+    ** do it here
+    */
+
+    // ConvertToUTF16Buf shouldn't fail if we allocated
+    // enough memory for the entire string.  Check
+    // anyway just to be safe.
+    if (!ConvertToUTF16Buf(utf8string.c_str(),retval,num_bytes))
+    {
+        // But ASSERT so we can find the problem and fix
+        // it.
+        ASSERT(0);
+        free(retval);
+        retval = NULL;
+        return NULL;
+    }
+
+    return retval;
+}
+
+/**
+ * Convert a UTF-8 encoded string to a UTF-16 string in
+ * a previously allocated buffer.
+ *
+ * @param utf8 the NUL-terminated UTF-8 string to decode
+ *
+ * @param utf16_buf the buffer in which to place the
+ * UTF-16 version of @p utf8.  If there's enough space
+ * to hold a NUL terminator, @p utf16_buf contains one.
+ * If not, @p utf16_buf is not NUL terminated.
+ *
+ * @param num_bytes the number of bytes that @p
+ * utf16_str points to
+ *
+ * @retval 0 error converting @p name to UTF-16,
+ * including when @p utf8 requires more space to encode
+ * in UTF-16 than indicated by @p num_bytes.  In that
+ * case, @p utf16_buf contains the UTF-16 encoding of as
+ * much of @p utf8 as possible.
+ *
+ * @retval 1 successfully converted @p name to @p UTF-16
+ * in @p utf16_buf. wide character (UTF-16) version of
+ * @p Invalid portions of UTF-8 are represented by a
+ * replacement character U+FFFD.
+ */
+int
+Utf8ToFilename::ConvertToUTF16Buf ( const char      *utf8,
+                                    wchar_t         *utf16_buf,
+                                    size_t          num_bytes )
+{
+    size_t      i;
+    const UINT8 *next_char;
+    size_t      num_chars;
+    size_t      num_utf16_chars;
+    size_t      num_input_bytes;
+    const UINT8 *p;
+    wchar_t     this_utf16[2];
+
+    ASSERT(utf8);
+    ASSERT(utf16_buf || (num_bytes == 0));
+    ASSERT(sizeof(wchar_t) == 2);
+
+    ASSERT(num_bytes % sizeof(wchar_t) == 0);
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: converting \"%s\"",__FUNCTION__,utf8));
+
+    num_chars = strlen(utf8);
+
+    // If the input is NUL-terminated (which it better
+    // be), the NUL-terminator is a valid input byte as
+    // well
+    num_input_bytes = num_chars + 1;
+
+    // Make sure the buffer we've been given is long
+    // enough.  We might need one UTF-16 character for
+    // every UTF-8 character.  And one more for the NUL
+    // terminator.
+    //
+    // Here, check that there's room for a NUL
+    // terminator in the output string.  This makes it
+    // safe to dereference p in the while loop below.
+    // It's probably enough to check num_bytes == 0 here
+    // but if we did that we'd have to change the error
+    // message after the while loop to be less specific.
+    // This way we give the caller more info about the
+    // input string.
+    if (num_bytes < sizeof(wchar_t))
+    {
+        log.errorf("%s: %u byte(s) is not enough to transform a %u byte UTF-8 string "
+                   "to NUL-terminated UTF-16",__FUNCTION__,num_bytes,num_input_bytes);
+        return 0;
+    }
+
+    ASSERT(num_bytes > 0);
+    ASSERT(utf16_buf);
+    memset(utf16_buf,0,num_bytes);
+
+    // The number of UTF-16 characters we've got space for
+    // in utf16_buf
+    num_utf16_chars = num_bytes / sizeof(wchar_t);
+
+    p = (const UINT8 *)utf8;
+    i = 0;
+    while (*p && (i < num_utf16_chars))
+    {
+        LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: decoding first UTF-8 byte 0x%02X (UTF-16 "
+                    "character %d of at most %d)",__FUNCTION__,*p,(i + 1),
+                    num_utf16_chars));
+
+        memset(this_utf16,0,sizeof(this_utf16));
+
+        // This function decodes illegal bytes/sequences
+        // with a replacement character and returns the
+        // pointer to the next character to decode.  Pass
+        // NULL since we don't care about detecting invalid
+        // characters here.
+        next_char = Utf8DecodeChar(p,num_input_bytes,this_utf16,NULL);
+
+        // We've always got one character to assign
+        utf16_buf[i++] = this_utf16[0];
+
+        // If we're dealing with a surrogate pair,
+        // assign the low half too
+        if (this_utf16[1])
+        {
+            // We may not have any more room in the
+            // UTF-16 buffer.  Check to make sure we
+            // don't step on someone else's memory.  We
+            // need to return failure here instead of
+            // depending on our other logic to do it for
+            // us.  We'll get out of the while loop with
+            // no extra code, but if we're dealing with
+            // the UTF-16 encoding of the last character
+            // in the input string, there won't appear
+            // to be anything wrong.
+            if (i >= num_utf16_chars)
+            {
+                log.errorf("%s: out of space in %u  byte output string to store surrogate "
+                           "pair low half (0x%04X)",__FUNCTION__,num_bytes,this_utf16[1]);
+                return 0;
+            }
+             
+            utf16_buf[i++] = this_utf16[1];
+        }
+
+        // Put this here to make it brutally clear that
+        // the cast is safe
+        ASSERT(next_char >= p);
+        num_input_bytes -= (size_t)(next_char - p);
+        p = next_char;
+    }
+
+    if (*p)
+    {
+        // Since num_input_bytes includes 1 for the
+        // NUL-terminator, it's got to be bigger than
+        // one here.
+        ASSERT(num_input_bytes > 1);
+        log.errorf("%s: %u byte(s) of input string remain(s) undecoded (%s): out of space in "
+                   "%u byte output string",__FUNCTION__,(num_input_bytes - 1),p,num_bytes);
+        return 0;
+    }
+
+    return 1;
+}
+
+/**
+ * Accessor for the length of a prefix (i.e. \\\?\\ or
+ * \\\?\\UNC\\) that begins a filename
+ *
+ * @param utf8string the UTF-8 encoded filename to
+ * examine
+ *
+ * @return the length of the prefix of @p utf8string in
+ * characters
+ */
+int
+Utf8ToFilename::GetPrefixLen ( const string &utf8string )
+{
+    if (utf8string.find("\\\\?\\") == 0)
+    {
+        return strlen("\\\\?\\");
+    }
+
+    if (utf8string.find("\\\\?\\UNC\\") == 0)
+    {
+        return strlen("\\\\?\\UNC\\");
+    }
+
+    return 0;
+}
+
+/**
+ * Determine if a path is absolute or not
+ *
+ * @param utf8string the UTF-8 encoded path to examine
+ * that does not begin with \\\?\\ nor \\\?\\UNC\\
+ *
+ * @retval 0 @p utf8string is not an absolute path
+ * @retval 1 @p utf8string is an absolute path
+ */       
+int
+Utf8ToFilename::IsAbsolute ( const string &utf8string )
+{
+    // Assume utf8string doesn't already start with a
+    // long filename prefix (i.e. \\?\ or \\?\UNC\)
+    // since the logic here depends on that.
+    ASSERT(GetPrefixLen(utf8string) == 0);
+
+    // Is an empty string absolute or relative?  It's
+    // not absolute since we can't tell what
+    // drive/volume it's for so say it's relative.
+    if (utf8string.length() == 0)
+    {
+        return 0;
+    }
+        
+    // Here we're looking for:
+    //  x:   drive relative
+    //  x:\  absolute path
+    if (utf8string[1] == ':')
+    {
+        // It starts with x:, but is it x:/ ?
+        if ((utf8string.length() >= 2) && IsPathSeparator(utf8string[2]))
+        {
+            // Yup -- it's absolute
+            return 1;
+        }
+
+        // Nope, not x:/, just x:something
+        return 0;
+    }
+
+    // UNC paths are absolute paths too
+    return IsUncPath(utf8string);
+}
+
+/**
+ * Determine if a character is a valid path separator
+ *
+ * @param c the character to check
+ *
+ * @retval 0 @p c is not a valid path separator
+ * @retval 1 @p c is a valid path separator
+ */
+int
+Utf8ToFilename::IsPathSeparator ( char c )
+{
+    return ((c == '\\') || (c == '/'));
+}
+
+/**
+ * Determine if a path is a UNC path
+ *
+ * @param utf8string the UTF-8 encoded path to examine
+ * that does not begin with \\\?\\ nor \\\?\\UNC\\
+ *
+ * @retval 0 @p utf8string is not a UNC path
+ * @retval 1 @p utf8string is a UNC path
+ */       
+int
+Utf8ToFilename::IsUncPath ( const string &utf8string )
+{
+    const char  *host;
+    int         num_slashes;
+    const char  *p;
+
+    // Assume utf8string doesn't already start with a
+    // long filename prefix (i.e. \\?\ or \\?\UNC\)
+    // since the logic here depends on that.
+    ASSERT(GetPrefixLen(utf8string) == 0);
+
+    // Is an empty string a UNC path?  No.
+    if (utf8string.length() == 0)
+    {
+        return 0;
+    }
+
+    //  Recognize:
+    //    //volume/path
+    //    \\volume\path
+    if (!IsPathSeparator(utf8string[0]))
+    {
+        // If it doesn't start with a path separator, it's
+        // not a UNC path.
+        return 0;
+    }
+
+    // The path starts with a slash, so it could be a UNC
+    // path.  See if it starts with two slashes...Be careful
+    // though, it might have more than 2 slashes.
+    p = utf8string.c_str();
+    num_slashes = 0;
+    while (*p && IsPathSeparator(*p))
+    {
+        num_slashes++;
+        p++;
+    }
+
+    // We found a slash at the beginning so we better have
+    // at least one here
+    ASSERT(num_slashes >= 1);
+    if ((num_slashes > 2) || !(*p))
+    {
+        // If we've got more than two slashes or we've
+        // run off the end of the string (///foo or
+        // //)...who knows how the OS will handle it,
+        // but it's not a UNC path.
+        log.errorf("%s: don't understand path(%s)",__FUNCTION__,utf8string.c_str());
+        return 0;
+    }
+
+    // If we've only got one slash, it looks like a
+    // drive relative path.  If it's something like
+    // /foo//bar it's not clear how the OS handles it,
+    // but that's someone else's problem.  It's not a
+    // UNC path.
+    if (num_slashes == 1)
+    {
+        return 0;
+    }
+    
+    // If we're here, we've got two slashes followed by
+    // a non-slash.  Something like //foo.  To be a
+    // proper UNC path, we need to see a hostname
+    // (e.g. foo), and then another slash.  If not, it's
+    // not a UNC path.
+    ASSERT(num_slashes == 2);
+
+    // Tempting to use STRTOK_R here, but that modifies
+    // the original string.  Instead of making a copy,
+    // search manually.
+    host = p;
+    while (*p && !IsPathSeparator(*p))
+    {
+        p++;
+    }
+
+    // We checked for separators above, so we better
+    // have moved on at least a bit
+    ASSERT(host != p);
+    if (!(*p))
+    {
+        // We ran off the end of the string without finding
+        // another separator.  So, we've got something like
+        // 
+        //  //foobar
+        // 
+        // which isn't a UNC path.
+        log.warningf("%s: incomplete UNC path: host only(%s)",__FUNCTION__,
+                     utf8string.c_str());
+        return 0;
+    }
+
+    // p points to a separator, so...we've got one of:
+    //  //host//
+    //  //host//blah
+    //  //host/bar
+    //
+    // Of these, only the last is a proper UNC path.  See
+    // what we've got after p.
+    num_slashes = 0;
+    while (*p && IsPathSeparator(*p))
+    {
+        num_slashes++;
+        p++;
+    }
+
+    // We better have at least one slash or our logic is
+    // broken
+    ASSERT(num_slashes >= 1);
+    if (!(*p))
+    {
+        // //host// (or maybe //host///), but no path
+        // part after the host
+        log.warningf("%s: incomplete UNC path: no path after host(%s)",
+                     __FUNCTION__,utf8string.c_str());
+        return 0;
+    }
+
+    if (num_slashes > 1)
+    {
+        // Another busted case //host//blah or
+        // //host///blah, etc.
+        log.warningf("%s: invalid UNC path: too many slashes after host(%s)",
+                     __FUNCTION__,utf8string.c_str());
+        return 0;
+    }
+    
+    // If we're here it means num_slashes is exactly 1
+    // so we've got //host/something so we're calling
+    // that a UNC path.
+    return 1;
+}
+
+/**
+ * Accessor for whether the UTF-16 encoded string is valid
+ *
+ * @retval false the UTF-16 encoded string is not valid
+ * @retval true the UTF-16 encoded string is valid
+ */
+bool
+Utf8ToFilename::IsUTF16Valid( ) const
+{
+    return (_wideCharString ? true : false);
+}
+
+/**
+ * Decode one UTF-8 encoded character into a UTF-16
+ * character.  The trouble here is that UTF-16 is really a
+ * variable length encoding to handle surrogate pairs
+ * (0xD800 --> 0xDFFF).  This way UTF-16 can handle more
+ * than 2^16 characters.  So we need to be careful.  UCS-2
+ * is a fixed width (16-bit) encoding that we could use, but
+ * then we can only handle 2^16 characters (the BMP).  To
+ * handle all 2^21 characters, we need UTF-16.
+ *
+ * What does Windows really use?  UTF-16.  See
+ * http://unicode.org/iuc/iuc17/b2/slides.ppt for a
+ * discussion.
+ * http://discuss.fogcreek.com/joelonsoftware5/default.asp?cmd=show&ixPost=168543
+ * also has some info.
+ *
+ * @param utf8_char the UTF-8 character to decode, possibly
+ * occupying multiple bytes, not necessarily NUL terminated
+ *
+ * @param num_bytes the number of bytes that @p utf8_char
+ * points to (must be > 0)
+ *
+ * @param utf16 populated with the UTF-16 equivalent of @p
+ * utf8_char.  Note that this must point to at least 2
+ * wchar_t's of memory so there's room to hold a surrogate
+ * pair.
+ *
+ * @param invalid populated with 1 if @p utf8_char doesn't
+ * point to a valid UTF-8 encoded character, 0 if @p
+ * utf8_char is valid.
+ *
+ * @return the next byte to examine for subsequent decoding
+ * (some number of bytes after @p utf8_char).  This may not
+ * be valid to dereference depending on the value of @p
+ * num_bytes.
+ */
+const UINT8 *
+Utf8ToFilename::Utf8DecodeChar ( const UINT8    *utf8_char,
+                                 size_t         num_bytes,
+                                 wchar_t        *utf16,
+                                 int            *invalid )
+
+{
+    wchar_t     high_half;
+    int         i;
+    UINT8       len;
+    wchar_t     low_half;
+    UINT8       mask;
+    const UINT8 *p;
+    UINT32      ucs4;
+    int         valid_len;
+
+    ASSERT(utf8_char);
+    ASSERT(num_bytes > 0);
+    ASSERT(utf16);
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: decoding UTF-8 string at address 0x%p",
+                __FUNCTION__,utf8_char));
+
+    /*
+    ** Assume utf8_char is invalid until we learn otherwise
+    */
+    if (invalid)
+    {
+        *invalid = 1;
+    }
+
+    /*
+    ** Traverse the UTF-8 encoding and figure out what we've
+    ** got.
+    */
+    p = (const UINT8 *)(utf8_char);
+
+    /*
+    ** This is the number of bytes we expect based on the
+    ** first octet.  If subsequent bytes are NUL or invalid,
+    ** then it may not the same as the actual len.
+    */
+    len = Utf8NumOctets(*p);
+    if (len == 0)
+    {
+        log.errorf("%s: 0x%02X is not a valid first byte of a UTF-8 encoded character",__FUNCTION__,*p);
+
+        /*
+        ** Use the replacement character and advance past
+        ** the invalid byte
+        */
+        *utf16 = REPLACEMENT_CHAR;
+        return p + 1;
+    }
+
+    /*
+    ** Handle one byte encodings in a special case.  See
+    ** below for an explanation of how we mask successive
+    ** bytes of an encoding to see why.  We're depending on
+    ** the validation in Utf8NumOctets here to make this OK.
+    */
+    if (len == 1)
+    {
+        /*
+        ** There's no intermediate UCS-4 step here.  We go
+        ** straight to UTF-16 since they're the same.
+        */
+        LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: one byte UTF-16 encoding: 0x%02X",
+                    __FUNCTION__,*p));
+        *utf16 = *p;
+        if (invalid)
+        {
+            *invalid = 0;
+        }
+        return p + 1;
+    }
+
+    /*
+    ** Make sure we've got enough bytes in our input string
+    ** to form a valid UTF-8 character
+    */
+    if (len > num_bytes)
+    {
+        log.errorf("%s: first byte 0x%02X indicates a %d byte "
+                   "UTF-8 character, but we only have %u valid byte(s)",
+                   __FUNCTION__,*p,len,num_bytes);
+        *utf16 = REPLACEMENT_CHAR;
+        return p + 1;
+    }
+
+    /*
+    ** Traverse the bytes that should be part of this UTF-8
+    ** encoded character and make sure we don't have an
+    ** overlength encoding, and make sure that each
+    ** character is valid.
+    */
+
+    /*
+    ** As we traverse each character, we mask off the
+    ** appropriate number of bits and include them in the
+    ** overall result.
+    **
+    ** 1 byte encoding [U+00000000,U+0000007F]: 7 bits (7 bits total) (handled above)
+    ** 2 byte encoding [U+00000080,U+000007FF]: 5 bits, 6 bits (11 bits total)
+    ** 3 byte encoding [U+00000800,U+0000FFFF]: 4 bits, 6 bits, 6 bits (16 bits total)
+    ** 4 byte encoding [U+00010000,U+001FFFFF]: 3 bits, 6 bits, 6 bits, 6 bits (21 bits total)
+    ** 5 byte encoding [U+00200000,U+03FFFFFF]: 2 bits, 6 bits, 6 bits, 6 bits, 6 bits (26 bits total)
+    ** 6 byte encoding [U+04000000,U+7FFFFFFF]: 1 bit, 6 bits, 6 bits, 6 bits, 6 bits, 6 bits (31 bits total)
+    **
+    ** So, mask the initial byte appropriately, then take
+    ** the bottom 6 bits from the remaining bytes.  To be
+    ** brutally explicit, the first byte mask is:
+    **
+    ** 1 byte encoding: 0x7F (or 0x80 - 1) (or (1 << 7) - 1)
+    ** 2 byte encoding: 0x1F (or 0x20 - 1) (or (1 << 5) - 1)
+    ** 3 byte encoding: 0x0F (or 0x10 - 1) (or (1 << 4) - 1)
+    ** 4 byte encoding: 0x07 (or 0x08 - 1) (or (1 << 3) - 1)
+    ** 5 byte encoding: 0x03 (or 0x04 - 1) (or (1 << 2) - 1)
+    ** 6 byte encoding: 0x01 (or 0x02 - 1) (or (1 << 1) - 1)
+    **    
+    ** So, the one byte encoding is a special case (again,
+    ** handled above), but for the other lengths, the mask
+    ** is (1 << (7 - len)) - 1.
+    */
+
+    /*
+    ** Handle the first byte of multi-byte encodings since
+    ** it's special
+    */
+    ASSERT(len > 1);
+    ASSERT(len <= 6);
+    mask = (1 << (7 - len)) - 1;
+    ucs4 = *p & mask;
+    p++;
+
+    /*
+    ** Now handle the remaining bytes
+    */
+    for (i = 1;(i < len);i++)
+    {
+        if ((*p < 0x80) || (*p > 0xBF))
+        {
+            log.errorf("%s: 0x%02X is not a valid continuation character in a UTF-8 encoding",
+                       __FUNCTION__,*p);
+
+            /*
+            ** Use the replacement character and return the
+            ** next byte after the invalid sequence as the
+            ** place for subsequent decoding operations.  In
+            ** this case the invalid continuation character
+            ** could be the beginning of the next valid
+            ** sequence, so return that.
+            */
+            *utf16 = REPLACEMENT_CHAR;
+            return p;
+        }
+            
+        /*
+        ** For the remainder of the bytes, shift over what
+        ** we've already got by 6 bits, and then OR in the
+        ** bottom 6 bits of the current byte.
+        */
+        ucs4 = (ucs4 << 6) | (*p & 0x3F);
+        p++;
+    }
+
+    /*
+    ** p is now pointing to the beginning of the next UTF-8
+    ** sequence to decode...
+    */
+
+    /*
+    ** Finally, detect overlong encodings.  For example, a
+    ** line feed (U+000A) should be encoded as 0x0A
+    ** (0b00001010) but could in theory be encoded in UTF-8
+    ** as 0xC0 0x8A (0b10001010).
+    **
+    ** Another example is the forward slash (/) (U+002F).
+    ** It should be encoded as 0x2F, but could in theory be
+    ** encoded in UTF-8 as 0xC0 0xAF (which we'll catch
+    ** because 0xC0 is an invalid first byte of a UTF-8
+    ** encoding), but could also be 0xE0 0x80 0xAF.
+    **
+    ** I can't see any reasonable way to do this other than
+    ** to check the decoded character against its expected
+    ** length
+    */
+    valid_len = Utf8LenFromUcs4(ucs4);
+    if (valid_len == 0)
+    {
+        /*
+        ** This should never happen
+        */
+        log.errorf("%s: decoded a character that we can't encode again (0x%08X)",__FUNCTION__,ucs4);
+        ASSERT(0);
+
+        /*
+        ** If it does, use the replacement character
+        */
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    if (len != valid_len)
+    {
+        ASSERT(len > valid_len);
+        log.errorf("%s: overlong encoding(%s)...should be %d byte(s), not %d",__FUNCTION__,
+                   utf8_char,valid_len,len);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** UTF-16 can only hold 21 bits.  As of now (21-dec-10),
+    ** there's no Unicode code point bigger than 2^21.  To
+    ** be safe, check...
+    */
+    if (ucs4 > 0x0010FFFF)
+    {
+        log.errorf("%s: code point 0x%08X is too big",__FUNCTION__,ucs4);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** Check to make sure we're not working with a "code
+    ** point" that is in the range used to indicate
+    ** surrogate pairs.
+    */
+    if ((ucs4 >= 0x0000D800) && (ucs4 <= 0x0000DFFF))
+    {
+        log.errorf("%s: code point 0x%08X is in the range used to indicate surrogate pairs",
+                   __FUNCTION__,ucs4);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** To (try to) be complete, check for a couple more
+    ** invalid code points
+    */
+    if ((ucs4 == 0x0000FFFF) || (ucs4 == 0x0000FFFE))
+    {
+        log.errorf("%s: invalid code point (0x%08X)",__FUNCTION__,ucs4);
+        *utf16 = REPLACEMENT_CHAR;
+        return p;
+    }
+
+    /*
+    ** Finally, convert from UCS-4 to UTF-16.  This may be a
+    ** straightforward assignment, but we have to deal with
+    ** surrogate pairs
+    */
+    if (ucs4 <= 0x0000FFFF)
+    {
+        *utf16 = ucs4 & 0xFFFF;
+        LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: UTF-16 encoding of 0x%08X is 0x%04X",
+                    __FUNCTION__,ucs4,*utf16));
+        if (invalid)
+        {
+            *invalid = 0;
+        }
+        return p;
+    }
+
+    /*
+    ** Transform UCS-4 into a UTF-16 surrogate pair
+    */
+
+    /*
+    ** Grab bits [10,20] (where bit 0 is the LSB) and shift
+    ** them down
+    */
+    high_half = 0xD800 + ((ucs4 - 0x00010000) >> 10);
+
+    /*
+    ** And the bottom 10 bits [0,9]
+    */
+    low_half = 0xDC00 + (ucs4 & 0x03FF);
+
+    utf16[0] = high_half;
+    utf16[1] = low_half;
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: UTF-16 encoding of 0x%08X is 0x%04X:0x%04X",
+                __FUNCTION__,ucs4,utf16[0],utf16[1]));
+
+    if (invalid)
+    {
+        *invalid = 0;
+    }
+
+    return p;
+}
+
+/**
+ * Determine the number of bytes required to hold the UTF-8
+ * encoding of a UCS-4 code point
+ *
+ * @param ucs4 the code point
+ *
+ * @param use_syslog 1 to use syslog, 0 otherwise
+ *
+ * @retval 0 @p ucs4 is not a valid code point
+ *
+ * @retval [1,6] the number of bytes required to hold the
+ * UTF-8 encoding of @p ucs4
+ */
+size_t
+Utf8ToFilename::Utf8LenFromUcs4 ( UINT32 ucs4 )
+{
+    size_t      table_idx;
+
+    LOG_PRINTF((MP4_LOG_VERBOSE4,"%s: processing UCS-4 code point 0x%08X",
+                __FUNCTION__,ucs4));
+
+    for (table_idx = 0;(table_idx < (sizeof(s_len_info) /
+                                     sizeof(struct utf8_len_info)));
+         table_idx++)
+    {
+        if ((s_len_info[table_idx].range_min <= ucs4) &&
+            (ucs4 <= s_len_info[table_idx].range_max))
+        {
+            return s_len_info[table_idx].num_chars;
+        }
+    }
+
+    log.errorf("%s: 0x%08X is an invalid code point",__FUNCTION__,ucs4);
+
+    return 0;
+}
+
+/**
+ * Determine the number of octets that a UTF-8 encoded
+ * character should occupy based on its first byte
+ *
+ * @param utf8_first_byte the byte to examine
+ *
+ * @retval 0 @p utf8_first_byte is not a valid first byte of
+ * a UTF-8 encoded character
+ *
+ * @retval [1,6] the number of octets that @p
+ * utf8_first_byte should occupy
+ */
+UINT8
+Utf8ToFilename::Utf8NumOctets ( UINT8 utf8_first_byte )
+{
+    /**
+     * Here's a mapping from the first byte of a UTF-8
+     * character to the number of bytes it should contain
+     * based on information from
+     * http://www.unicode.org/versions/corrigendum1.html as
+     * well as
+     * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+     *
+     * [0x00,0x7F]: 1       (0-127)     (128 possible values)
+     * [0x80,0xBF]: invalid (128-191)   (64 possible values)
+     * [0xC0,0xDF]: 2       (192-223)   (32 possible values) (see below)
+     * [0xE0,0xEF]: 3       (224-239)   (16 possible values)
+     * [0xF0,0xF7]: 4       (240 - 247) (8 possible values)
+     * [0xF8,0xFB]: 5       (248 - 251) (4 possible values)
+     * [0xFC,0xFD]: 6       (252 - 253) (2 possible values)
+     * [0xFE,0xFF]: invalid (254 - 255) (2 possible values)
+     *
+     * There's some gray area about 0xC0 and 0xC1.  It's
+     * clear they are invalid first bytes but the question
+     * is how to handle it.  If I reject them here, they'll
+     * get replaced with the REPLACEMENT character.  But, if
+     * I allow them here, it's likely that both this byte
+     * and the subsequent one will get replaced with only
+     * one replacement character.  This is what
+     * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+     * assumes in sections 4.1.1, 4.2.1 and 4.3.1.
+     */
+    if (utf8_first_byte <= 0x7F)
+    {
+        return 1;
+    }
+
+    if ((utf8_first_byte >= 0x80) && (utf8_first_byte <= 0xBF))
+    {
+        return 0;
+    }
+
+    if ((utf8_first_byte >= 0xC0) && (utf8_first_byte <= 0xDF))
+    {
+        return 2;
+    }
+
+    if ((utf8_first_byte >= 0xE0) && (utf8_first_byte <= 0xEF))
+    {
+        return 3;
+    }
+
+    if ((utf8_first_byte >= 0xF0) && (utf8_first_byte <= 0xF7))
+    {
+        return 4;
+    }
+
+    if ((utf8_first_byte >= 0xF8) && (utf8_first_byte <= 0xFB))
+    {
+        return 5;
+    }
+
+    if ((utf8_first_byte >= 0xFC) && (utf8_first_byte <= 0xFD))
+    {
+        return 6;
+    }
+
+    ASSERT((utf8_first_byte == 0xFE) || (utf8_first_byte == 0xFF));
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::win32
diff --git a/libplatform/platform_win32.h b/libplatform/platform_win32.h
new file mode 100644
index 0000000..28fe800
--- /dev/null
+++ b/libplatform/platform_win32.h
@@ -0,0 +1,98 @@
+#ifndef MP4V2_PLATFORM_WIN32_H
+#define MP4V2_PLATFORM_WIN32_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+// mingw needs this to enable some newer 64-bit functions
+#ifdef __MINGW32__
+#   undef  __MSVCRT_VERSION__
+#   define __MSVCRT_VERSION__ 0x800
+// JAN: see http://code.google.com/p/mp4v2/issues/detail?id=132
+#   define _USE_32BIT_TIME_T
+#endif
+
+// set minimum win32 API requirement to Windows 2000 or higher
+#ifndef _WIN32_WINNT
+#   define _WIN32_WINNT 0x0500
+#endif 
+#ifndef WINVER
+#   define WINVER 0x0500
+#endif 
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libplatform/platform_base.h"
+#include <mp4v2/mp4v2.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mp4v2 { namespace platform {
+    using namespace std;
+
+    using ::int8_t;
+    using ::int16_t;
+    using ::int32_t;
+    using ::int64_t;
+
+    using ::uint8_t;
+    using ::uint16_t;
+    using ::uint32_t;
+    using ::uint64_t;
+}} // namespace mp4v2::platform
+
+///////////////////////////////////////////////////////////////////////////////
+
+// fprintf macros for unsigned types - mingw32 is a good source if more needed
+#define PRId8   "d"
+#define PRId16  "d"
+#define PRId32  "d"
+#define PRId64  "I64d"
+
+#define PRIu8   "u"
+#define PRIu16  "u"
+#define PRIu32  "u"
+#define PRIu64  "I64u"
+
+#define PRIx8   "x"
+#define PRIx16  "x"
+#define PRIx32  "x"
+#define PRIx64  "I64x"
+
+///////////////////////////////////////////////////////////////////////////////
+
+// If using Visual Studio 2010 or newer these aren't needed since stdint.h is available.
+#if !(defined(_MSC_VER) && _MSC_VER >= 1600)
+// some macros for constant expressions
+#define INT8_C(x)    x
+#define INT16_C(x)   x
+#define INT32_C(x)   x ## L
+#define INT64_C(x)   x ## LL
+
+#define UINT8_C(x)   x
+#define UINT16_C(x)  x
+#define UINT32_C(x)  x ## UL
+#define UINT64_C(x)  x ## ULL
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef min
+#   undef min
+#endif
+
+#ifdef max
+#   undef max
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define snprintf(s,n,...)  _snprintf(s,n,__VA_ARGS__)
+#define strcasecmp(s1,s2)  _stricmp(s1,s2)
+#define strdup(s)          _strdup(s)
+
+///////////////////////////////////////////////////////////////////////////////
+
+// macro clashes with symbol
+#undef LC_NONE
+
+#endif // MP4V2_PLATFORM_WIN32_H
diff --git a/libplatform/platform_win32_impl.h b/libplatform/platform_win32_impl.h
new file mode 100644
index 0000000..65a872b
--- /dev/null
+++ b/libplatform/platform_win32_impl.h
@@ -0,0 +1,68 @@
+// Note that we have a separate platform_win32_impl.h to deal with the fact that windows.h defines a macro
+// called FindAtom, which mp4v2 also defines.  In older versions of visual studio, this actually causes
+// some pretty seriously issues with naming collisions and the defined macros (think infamous min/max macro
+// of windows.h vs stdc++'s min/max template functions)
+#include <windows.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mp4v2 { namespace platform { namespace win32 {
+
+class Utf8ToFilename
+{
+    public:
+    Utf8ToFilename( const string &utf8string );
+    ~Utf8ToFilename( );
+
+    bool                IsUTF16Valid( ) const;
+
+    operator LPCWSTR( ) const { return _wideCharString; }
+    operator LPWSTR( ) const { return _wideCharString; }
+
+    private:
+    Utf8ToFilename ( const Utf8ToFilename &src );
+    Utf8ToFilename &operator= ( const Utf8ToFilename &src );
+    
+    wchar_t             *ConvertToUTF16 ( const string &utf8 );
+
+    static int          ConvertToUTF16Buf ( const char      *utf8,
+                                            wchar_t         *utf16_buf,
+                                            size_t          num_bytes );
+    static int          GetPrefixLen ( const string &utf8string );
+
+    static int          IsAbsolute ( const string &utf8string );
+
+    static int          IsPathSeparator ( char c );
+
+    static int          IsUncPath ( const string &utf8string );
+
+    static const UINT8  *Utf8DecodeChar (
+        const UINT8     *utf8_char,
+        size_t          num_bytes,
+        wchar_t         *utf16,
+        int             *invalid
+        );
+
+    static size_t       Utf8LenFromUcs4 ( UINT32 ucs4 );
+
+    static UINT8        Utf8NumOctets ( UINT8 utf8_first_byte );
+
+    /**
+     * The UTF-8 encoding of the filename actually used
+     */
+    string      _utf8;
+
+    /**
+     * The UTF-16 encoding of the filename actually used
+     */
+    wchar_t*    _wideCharString;
+
+    public:
+
+    /**
+     * Accessor for @p _utf8
+     */
+    const string&       utf8;
+};
+
+}}} // namespace mp4v2::platform::win32
diff --git a/libplatform/process/process.h b/libplatform/process/process.h
new file mode 100644
index 0000000..446b100
--- /dev/null
+++ b/libplatform/process/process.h
@@ -0,0 +1,16 @@
+#ifndef MP4V2_PLATFORM_PROCESS_PROCESS_H
+#define MP4V2_PLATFORM_PROCESS_PROCESS_H
+
+/// @namespace mp4v2::platform::process (private) Process.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+namespace mp4v2 { namespace platform { namespace process {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4V2_EXPORT int32_t getpid();
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::process
+
+#endif // MP4V2_PLATFORM_PROCESS_PROCESS_H
diff --git a/libplatform/process/process_posix.cpp b/libplatform/process/process_posix.cpp
new file mode 100644
index 0000000..74b4e17
--- /dev/null
+++ b/libplatform/process/process_posix.cpp
@@ -0,0 +1,15 @@
+#include "libplatform/impl.h"
+
+namespace mp4v2 { namespace platform { namespace process {
+
+///////////////////////////////////////////////////////////////////////////////
+
+int32_t
+getpid()
+{
+    return ::getpid();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::process
diff --git a/libplatform/process/process_win32.cpp b/libplatform/process/process_win32.cpp
new file mode 100644
index 0000000..b784680
--- /dev/null
+++ b/libplatform/process/process_win32.cpp
@@ -0,0 +1,16 @@
+#include "libplatform/impl.h"
+#include <process.h>
+
+namespace mp4v2 { namespace platform { namespace process {
+
+///////////////////////////////////////////////////////////////////////////////
+
+int32_t
+getpid()
+{
+    return ::_getpid();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::process
diff --git a/libplatform/prog/option.cpp b/libplatform/prog/option.cpp
new file mode 100644
index 0000000..645dccf
--- /dev/null
+++ b/libplatform/prog/option.cpp
@@ -0,0 +1,637 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// branched from:
+//      FreeBSD 7.0-RELEASE
+//      $FreeBSD: src/lib/libc/stdlib/getopt_long.c,v 1.15 2006/09/23 14:48:31 ache Exp $
+//
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *        This product includes software developed by the NetBSD
+ *        Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "libplatform/impl.h"
+
+namespace mp4v2 {
+namespace platform {
+namespace prog {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define GNU_COMPATIBLE
+#define REPLACE_GETOPT
+
+namespace {
+    void warnx( const char* format, ... ) {
+        va_list ap;
+        va_start( ap, format );
+        vfprintf( stderr, format, ap );
+        va_end( ap );
+        fprintf( stderr, "\n" );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef REPLACE_GETOPT
+int   opterr = 1;    /* if error message should be printed */
+int   optind = 1;    /* index into parent argv vector */
+int   optopt = '?';  /* character checked for validity */
+int   optreset;      /* reset getopt */
+const char* optarg;  /* argument associated with option */
+#endif
+
+#define PRINT_ERROR ((opterr) && (*options != ':'))
+
+#define FLAG_PERMUTE    0x01    // permute non-options to the end of argv
+#define FLAG_ALLARGS    0x02    // treat non-options as args to option "-1"
+#define FLAG_LONGONLY   0x04    // operate as getopt_long_only
+
+// return values
+#define BADCH       (int)'?'
+#define BADARG      ((*options == ':') ? (int)':' : (int)'?')
+#define INORDER     (int)1
+
+#define EMSG        ""
+
+#ifdef GNU_COMPATIBLE
+#define NO_PREFIX   (-1)
+#define D_PREFIX    0
+#define DD_PREFIX   1
+#define W_PREFIX    2
+#endif
+
+static int getopt_internal(int, char * const *, const char *, const Option*, int*, int);
+static int parse_long_options(char * const *, const char *, const Option*, int *, int, int);
+static int gcd(int, int);
+static void permute_args(int, int, int, char * const *);
+
+static const char *place = EMSG; /* option letter processing */
+
+/* XXX: set optreset to 1 rather than these two */
+static int nonopt_start = -1; /* first non option argument (for permute) */
+static int nonopt_end = -1;   /* first option after non options (for permute) */
+
+/* Error messages */
+static const char recargchar[] = "option requires an argument -- %c";
+static const char illoptchar[] = "illegal option -- %c"; /* From P1003.2 */
+#ifdef GNU_COMPATIBLE
+static int dash_prefix = NO_PREFIX;
+static const char gnuoptchar[] = "invalid option -- %c";
+
+static const char recargstring[] = "option `%s%s' requires an argument";
+static const char ambig[] = "option `%s%.*s' is ambiguous";
+static const char noarg[] = "option `%s%.*s' doesn't allow an argument";
+static const char illoptstring[] = "unrecognized option `%s%s'";
+#else
+static const char recargstring[] = "option requires an argument -- %s";
+static const char ambig[] = "ambiguous option -- %.*s";
+static const char noarg[] = "option doesn't take an argument -- %.*s";
+static const char illoptstring[] = "unknown option -- %s";
+#endif
+
+/*
+ * Compute the greatest common divisor of a and b.
+ */
+static int
+gcd(int a, int b) {
+    int c;
+
+    c = a % b;
+    while (c != 0) {
+        a = b;
+        b = c;
+        c = a % b;
+    }
+
+    return (b);
+}
+
+/*
+ * Exchange the block from nonopt_start to nonopt_end with the block
+ * from nonopt_end to opt_end (keeping the same order of arguments
+ * in each block).
+ */
+static void
+permute_args(int panonopt_start, int panonopt_end, int opt_end,
+             char * const *nargv) {
+    int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
+    char *swap;
+
+    /*
+     * compute lengths of blocks and number and size of cycles
+     */
+    nnonopts = panonopt_end - panonopt_start;
+    nopts = opt_end - panonopt_end;
+    ncycle = gcd(nnonopts, nopts);
+    cyclelen = (opt_end - panonopt_start) / ncycle;
+
+    for (i = 0; i < ncycle; i++) {
+        cstart = panonopt_end+i;
+        pos = cstart;
+        for (j = 0; j < cyclelen; j++) {
+            if (pos >= panonopt_end)
+                pos -= nnonopts;
+            else
+                pos += nopts;
+            swap = nargv[pos];
+            /* LINTED const cast */
+            ((char **) nargv)[pos] = nargv[cstart];
+            /* LINTED const cast */
+            ((char **)nargv)[cstart] = swap;
+        }
+    }
+}
+
+/*
+ * parse_long_options --
+ *	Parse long options in argc/argv argument vector.
+ * Returns -1 if short_too is set and the option does not match long_options.
+ */
+static int
+parse_long_options(char * const *nargv, const char *options,
+                   const Option* long_options, int* idx, int short_too, int flags) {
+    const char *current_argv;
+    char *has_equal;
+#ifdef GNU_COMPATIBLE
+    const char *current_dash;
+#endif
+    size_t current_argv_len;
+    int i, match, exact_match, second_partial_match;
+
+    current_argv = place;
+#ifdef GNU_COMPATIBLE
+    switch (dash_prefix) {
+    case D_PREFIX:
+        current_dash = "-";
+        break;
+    case DD_PREFIX:
+        current_dash = "--";
+        break;
+    case W_PREFIX:
+        current_dash = "-W ";
+        break;
+    default:
+        current_dash = "";
+        break;
+    }
+#endif
+    match = -1;
+    exact_match = 0;
+    second_partial_match = 0;
+
+    optind++;
+
+    if ((has_equal = (char*)strchr(current_argv, '=')) != NULL) {
+        /* argument found (--option=arg) */
+        current_argv_len = has_equal - current_argv;
+        has_equal++;
+    }
+    else
+        current_argv_len = strlen(current_argv);
+
+    for (i = 0; long_options[i].name; i++) {
+        /* find matching long option */
+        if (strncmp(current_argv, long_options[i].name,
+                    current_argv_len))
+            continue;
+
+        if (strlen(long_options[i].name) == current_argv_len) {
+            /* exact match */
+            match = i;
+            exact_match = 1;
+            break;
+        }
+        /*
+         * If this is a known short option, don't allow
+         * a partial match of a single character.
+         */
+        if (short_too && current_argv_len == 1)
+            continue;
+
+        if (match == -1)        /* first partial match */
+            match = i;
+        else if ((flags & FLAG_LONGONLY) ||
+                 long_options[i].type !=
+                 long_options[match].type ||
+                 long_options[i].flag != long_options[match].flag ||
+                 long_options[i].val != long_options[match].val)
+            second_partial_match = 1;
+    }
+    if (!exact_match && second_partial_match) {
+        /* ambiguous abbreviation */
+        if (PRINT_ERROR)
+            warnx(ambig,
+#ifdef GNU_COMPATIBLE
+                  current_dash,
+#endif
+                  (int)current_argv_len,
+                  current_argv);
+        optopt = 0;
+        return (BADCH);
+    }
+    if (match != -1) {		/* option found */
+        if (long_options[match].type == Option::NO_ARG
+                && has_equal) {
+            if (PRINT_ERROR)
+                warnx(noarg,
+#ifdef GNU_COMPATIBLE
+                      current_dash,
+#endif
+                      (int)current_argv_len,
+                      current_argv);
+            /*
+             * XXX: GNU sets optopt to val regardless of flag
+             */
+            if (long_options[match].flag == NULL)
+                optopt = long_options[match].val;
+            else
+                optopt = 0;
+#ifdef GNU_COMPATIBLE
+            return (BADCH);
+#else
+            return (BADARG);
+#endif
+        }
+        if (long_options[match].type == Option::REQUIRED_ARG ||
+                long_options[match].type == Option::OPTIONAL_ARG) {
+            if (has_equal)
+                optarg = has_equal;
+            else if (long_options[match].type == Option::REQUIRED_ARG) {
+                /*
+                 * optional argument doesn't use next nargv
+                 */
+                optarg = nargv[optind++];
+            }
+        }
+        if ((long_options[match].type == Option::REQUIRED_ARG)
+                && (optarg == NULL)) {
+            /*
+             * Missing argument; leading ':' indicates no error
+             * should be generated.
+             */
+            if (PRINT_ERROR)
+                warnx(recargstring,
+#ifdef GNU_COMPATIBLE
+                      current_dash,
+#endif
+                      current_argv);
+            /*
+             * XXX: GNU sets optopt to val regardless of flag
+             */
+            if (long_options[match].flag == NULL)
+                optopt = long_options[match].val;
+            else
+                optopt = 0;
+            --optind;
+            return (BADARG);
+        }
+    }
+    else {			/* unknown option */
+        if (short_too) {
+            --optind;
+            return (-1);
+        }
+        if (PRINT_ERROR)
+            warnx(illoptstring,
+#ifdef GNU_COMPATIBLE
+                  current_dash,
+#endif
+                  current_argv);
+        optopt = 0;
+        return (BADCH);
+    }
+    if (idx)
+        *idx = match;
+    if (long_options[match].flag) {
+        *long_options[match].flag = long_options[match].val;
+        return (0);
+    }
+    else
+        return (long_options[match].val);
+}
+
+/*
+ * getopt_internal --
+ *	Parse argc/argv argument vector.  Called by user level routines.
+ */
+static int
+getopt_internal(int nargc, char * const *nargv, const char *options,
+                const Option* long_options, int* idx, int flags) {
+    const char *oli;				/* option letter list index */
+    int optchar, short_too;
+    int posixly_correct;	/* no static, can be changed on the fly */
+
+    if (options == NULL)
+        return (-1);
+
+    /*
+     * Disable GNU extensions if POSIXLY_CORRECT is set or options
+     * string begins with a '+'.
+     */
+    posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
+#ifdef GNU_COMPATIBLE
+    if (*options == '-')
+        flags |= FLAG_ALLARGS;
+    else if (posixly_correct || *options == '+')
+        flags &= ~FLAG_PERMUTE;
+#else
+    if (posixly_correct || *options == '+')
+        flags &= ~FLAG_PERMUTE;
+    else if (*options == '-')
+        flags |= FLAG_ALLARGS;
+#endif
+    if (*options == '+' || *options == '-')
+        options++;
+
+    /*
+     * XXX Some GNU programs (like cvs) set optind to 0 instead of
+     * XXX using optreset.  Work around this braindamage.
+     */
+    if (optind == 0)
+        optind = optreset = 1;
+
+    optarg = NULL;
+    if (optreset)
+        nonopt_start = nonopt_end = -1;
+start:
+    if (optreset || !*place) {		/* update scanning pointer */
+        optreset = 0;
+        if (optind >= nargc) {          /* end of argument vector */
+            place = EMSG;
+            if (nonopt_end != -1) {
+                /* do permutation, if we have to */
+                permute_args(nonopt_start, nonopt_end,
+                             optind, nargv);
+                optind -= nonopt_end - nonopt_start;
+            }
+            else if (nonopt_start != -1) {
+                /*
+                 * If we skipped non-options, set optind
+                 * to the first of them.
+                 */
+                optind = nonopt_start;
+            }
+            nonopt_start = nonopt_end = -1;
+            return (-1);
+        }
+        if (*(place = nargv[optind]) != '-' ||
+#ifdef GNU_COMPATIBLE
+                place[1] == '\0') {
+#else
+                (place[1] == '\0' && strchr(options, '-') == NULL)) {
+#endif
+            place = EMSG;		/* found non-option */
+            if (flags & FLAG_ALLARGS) {
+                /*
+                 * GNU extension:
+                 * return non-option as argument to option 1
+                 */
+                optarg = nargv[optind++];
+                return (INORDER);
+            }
+            if (!(flags & FLAG_PERMUTE)) {
+                /*
+                 * If no permutation wanted, stop parsing
+                 * at first non-option.
+                 */
+                return (-1);
+            }
+            /* do permutation */
+            if (nonopt_start == -1)
+                nonopt_start = optind;
+            else if (nonopt_end != -1) {
+                permute_args(nonopt_start, nonopt_end,
+                             optind, nargv);
+                nonopt_start = optind -
+                               (nonopt_end - nonopt_start);
+                nonopt_end = -1;
+            }
+            optind++;
+            /* process next argument */
+            goto start;
+        }
+        if (nonopt_start != -1 && nonopt_end == -1)
+            nonopt_end = optind;
+
+        /*
+         * If we have "-" do nothing, if "--" we are done.
+         */
+        if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {
+            optind++;
+            place = EMSG;
+            /*
+             * We found an option (--), so if we skipped
+             * non-options, we have to permute.
+             */
+            if (nonopt_end != -1) {
+                permute_args(nonopt_start, nonopt_end,
+                             optind, nargv);
+                optind -= nonopt_end - nonopt_start;
+            }
+            nonopt_start = nonopt_end = -1;
+            return (-1);
+        }
+    }
+
+    /*
+     * Check long options if:
+     *  1) we were passed some
+     *  2) the arg is not just "-"
+     *  3) either the arg starts with -- we are getopt_long_only()
+     */
+    if (long_options != NULL && place != nargv[optind] &&
+            (*place == '-' || (flags & FLAG_LONGONLY))) {
+        short_too = 0;
+#ifdef GNU_COMPATIBLE
+        dash_prefix = D_PREFIX;
+#endif
+        if (*place == '-') {
+            place++;		/* --foo long option */
+#ifdef GNU_COMPATIBLE
+            dash_prefix = DD_PREFIX;
+#endif
+        }
+        else if (*place != ':' && strchr(options, *place) != NULL)
+            short_too = 1;		/* could be short option too */
+
+        optchar = parse_long_options(nargv, options, long_options,
+                                     idx, short_too, flags);
+        if (optchar != -1) {
+            place = EMSG;
+            return (optchar);
+        }
+    }
+
+    if ((optchar = (int)*place++) == (int)':' ||
+            (optchar == (int)'-' && *place != '\0') ||
+            (oli = (const char*)strchr(options, optchar)) == NULL) {
+        /*
+         * If the user specified "-" and  '-' isn't listed in
+         * options, return -1 (non-option) as per POSIX.
+         * Otherwise, it is an unknown option character (or ':').
+         */
+        if (optchar == (int)'-' && *place == '\0')
+            return (-1);
+        if (!*place)
+            ++optind;
+#ifdef GNU_COMPATIBLE
+        if (PRINT_ERROR)
+            warnx(posixly_correct ? illoptchar : gnuoptchar,
+                  optchar);
+#else
+        if (PRINT_ERROR)
+            warnx(illoptchar, optchar);
+#endif
+        optopt = optchar;
+        return (BADCH);
+    }
+    if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
+        /* -W long-option */
+        if (*place)			/* no space */
+            /* NOTHING */;
+        else if (++optind >= nargc) {	/* no arg */
+            place = EMSG;
+            if (PRINT_ERROR)
+                warnx(recargchar, optchar);
+            optopt = optchar;
+            return (BADARG);
+        }
+        else				/* white space */
+            place = nargv[optind];
+#ifdef GNU_COMPATIBLE
+        dash_prefix = W_PREFIX;
+#endif
+        optchar = parse_long_options(nargv, options, long_options,
+                                     idx, 0, flags);
+        place = EMSG;
+        return (optchar);
+    }
+    if (*++oli != ':') {			/* doesn't take argument */
+        if (!*place)
+            ++optind;
+    }
+    else {				/* takes (optional) argument */
+        optarg = NULL;
+        if (*place)			/* no white space */
+            optarg = place;
+        else if (oli[1] != ':') {	/* arg not optional */
+            if (++optind >= nargc) {	/* no arg */
+                place = EMSG;
+                if (PRINT_ERROR)
+                    warnx(recargchar, optchar);
+                optopt = optchar;
+                return (BADARG);
+            }
+            else
+                optarg = nargv[optind];
+        }
+        place = EMSG;
+        ++optind;
+    }
+    /* dump back option letter */
+    return (optchar);
+}
+
+#if 0
+#ifdef REPLACE_GETOPT
+/*
+ * getopt --
+ *	Parse argc/argv argument vector.
+ *
+ * [eventually this will replace the BSD getopt]
+ */
+int
+getopt(int nargc, char * const *nargv, const char *options) {
+
+    /*
+     * We don't pass FLAG_PERMUTE to getopt_internal() since
+     * the BSD getopt(3) (unlike GNU) has never done this.
+     *
+     * Furthermore, since many privileged programs call getopt()
+     * before dropping privileges it makes sense to keep things
+     * as simple (and bug-free) as possible.
+     */
+    return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));
+}
+#endif /* REPLACE_GETOPT */
+#endif
+
+/*
+ * getopt_long --
+ *	Parse argc/argv argument vector.
+ */
+int
+getOption(int nargc, char * const *nargv, const char *options,
+          const Option* long_options, int* idx) {
+
+    return (getopt_internal(nargc, nargv, options, long_options, idx,
+                            FLAG_PERMUTE));
+}
+
+/*
+ * getopt_long_only --
+ *	Parse argc/argv argument vector.
+ */
+int
+getOptionSingle(int nargc, char * const *nargv, const char *options,
+                 const Option* long_options, int* idx) {
+
+    return (getopt_internal(nargc, nargv, options, long_options, idx,
+                            FLAG_PERMUTE|FLAG_LONGONLY));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::prog
diff --git a/libplatform/prog/option.h b/libplatform/prog/option.h
new file mode 100644
index 0000000..9f7a82a
--- /dev/null
+++ b/libplatform/prog/option.h
@@ -0,0 +1,174 @@
+#ifndef MP4V2_PLATFORM_PROG_OPTION_H
+#define MP4V2_PLATFORM_PROG_OPTION_H
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// @namespace mp4v2::platform::prog Command-line argument parsing.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+///
+/// This namespace provides a mechanism to parse command-line arguments and
+/// options for executables.
+/// It is identical in behavior to <b>getopt_long</b> functions available
+/// with many popular posix-platforms such as Darwin, FreeBSD and Linux.
+/// Virtually any OS which has getopt_long will adequately document this
+/// functionality. However, to avoid symbol ambiguity with the popular
+/// posix implementation, the following identifiers have been renamed:
+///     @li getopt_long()      -> getOption()
+///     @li getopt_long_only() -> getOptionSingle()
+///     @li option             -> Option
+///     @li option.has_arg     -> Option.type
+//!
+///////////////////////////////////////////////////////////////////////////////
+namespace mp4v2 { namespace platform { namespace prog {
+
+//! On return from getOption() or getOptionSingle(),
+//! points to an option argument, if it is anticipated.
+MP4V2_EXPORT extern const char* optarg;
+
+//! On return from getOption() or getOptionSingle(),
+//! contains the index to the next argv argument for a subsequent call to
+//! getOption() or getOptionSingle().
+//! Initialized to 1 and must be set manually to 1 prior to invoking
+//! getOption() or getOptionSingle() to evaluate multiple sets of arguments.
+MP4V2_EXPORT extern int optind;
+
+//! On return from getOption() or getOptionSingle(),
+//! saves the last known option character returned by
+//! getOption() or getOptionSingle().
+//! On error, contains the character/code of option which caused error.
+MP4V2_EXPORT extern int optopt;
+
+//! Initialized to 1 and may be set to 0 to disable error messages.
+MP4V2_EXPORT extern int opterr;
+
+//! Must be set to 1 before evaluating the 2nd or each additional set
+//! of arguments.
+MP4V2_EXPORT extern int optreset;
+
+//! Structure describing a single option.
+//! An instance of Option is required for each program option and is
+//! initialized before use with getOption() or getOptionWord().
+struct MP4V2_EXPORT Option
+{
+    //! expectation-type indicating number of arguments expected
+    //! on the command-line following the option-argument itself
+    enum Type {
+        //! indicates exactly 0 arguments follow option
+        NO_ARG,
+        //! indicates exactly 1 argument follow option
+        REQUIRED_ARG,
+        //! indicates 0 or 1 arguments follow option
+        OPTIONAL_ARG,
+    };
+
+    //! contains the option name without leading double-dash
+    const char* name;
+
+    //! option expectation-type
+    Type type;
+
+    //! If not NULL, then the integer pointed to by it will be set to
+    //! the value in the val field. If the flag field is NULL, then the
+    //! <b>val</b> field will be returned.
+    int* flag;
+
+    //! Constant value representing option. This is usually a single-char
+    //! ASCII value but in case of long-options without a corresponding
+    //! single-char value it can be a unique integer (beyond ASCII range)
+    //! which represents the long-option.
+    int val;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//!
+//! Get option character from command line argument list.
+//!
+//! getOption() is similar to posix getopt() but it accepts options in two
+//! forms: words and characters. The getOption() function provides a
+//! superset of the functionality of getopt(). The getOption() function can
+//! be used in two ways. In the first way, every long-option understood by
+//! the program has a corresponding short-option, and the Option structure
+//! is only used to translate from long-options to short-options. When used
+//! in this fashion, getOption() behaves identically to getopt(). This is
+//! a good way to add long-option processing to an esxisting program with
+//! a minimum of rewriting.
+//!
+//! In the second mechanism, a long-option sets a flag in the Option
+//! structure structure passed, or will store a pointer to the command line
+//! argument in the Option structure passed to it for options that take
+//! arguments. Additionally, the long-option's argument may be specified as
+//! a single argument with an equal sign, eg:
+//!     @code myprogram --myoption=somevalue
+//!     @endcode
+//!
+//! When a long-option is processed, the call to getOption() will return 0.
+//! For this reason, long-option processing without shortcuts is not
+//! backwards compatible with getopt().
+//!
+//! It is possible to combine these methods, providing for long-options
+//! processing with short-option equivalents for some options.  Less
+//! frequently used options would be processed as long-options only.
+//!
+//! @param argc number of arguments.
+//! @param argv argument array of strings.
+//! @param optstr string containing the following elements:
+//!     individual characters, and characters followed by a colon to indicate
+//!     an option argument is to follow. For example, an option string "x"
+//!     recognizes an option "-x", and an option string "x:" recognizes an
+//!     option and argument "-x argument".
+//! @param longopts array of Option entries. The last element must be filled
+//!     with zeroes to indicate end-of-array.
+//! @param idx If not NULL, then the integer pointed to it will be set to
+//!     the index of the long-option relative to <b>longops</b>.
+//!
+//! @return If the <b>flag</b> field is NULL, <b>val</b> field is returned,
+//!     which is usually just the corresponding short-option.
+//!     If <b>flag</b> is not NULL, 0 is returned and <b>val</b> is
+//!     stored in the location pointed to by <b>flag</b> field.
+//!     A ':' will be returned if there was a missing option argument.
+//!     A '?' will be returned if an unknown or ambiguous option was used.
+//!     A -1 will be returned when the argument list has been exhausted.
+//!
+///////////////////////////////////////////////////////////////////////////////
+MP4V2_EXPORT
+int getOption( int argc, char* const* argv, const char* optstr, const Option* longopts, int* idx );
+
+///////////////////////////////////////////////////////////////////////////////
+//!
+//! Get option character from command line argument list and allow
+//! long-options with single-hyphens.
+//!
+//! Behaves identically to getOption() with the exception that long-options
+//! may start with '-' in addition to '--'.
+//! If an option starting with '-' does not match a long option but does match
+//! a single-character option, the single-character option is returned.
+//!
+//! @param argc number of arguments.
+//! @param argv argument array of strings.
+//! @param optstr string containing the following elements:
+//!     individual characters, and characters followed by a colon to indicate
+//!     an option argument is to follow. For example, an option string "x"
+//!     recognizes an option "-x", and an option string "x:" recognizes an
+//!     option and argument "-x argument".
+//! @param longopts array of Option entries. The last element must be filled
+//!     with zeroes to indicate end-of-array.
+//! @param idx If not NULL, then the integer pointed to it will be set to
+//!     the index of the long-option relative to <b>longops</b>.
+//!
+//! @return If the <b>flag</b> field is NULL, <b>val</b> field is returned,
+//!     which is usually just the corresponding short-option.
+//!     If <b>flag</b> is not NULL, 0 is returned and <b>val</b> is
+//!     stored in the location pointed to by <b>flag</b> field.
+//!     A ':' will be returned if there was a missing option argument.
+//!     A '?' will be returned if an unknown or ambiguous option was used.
+//!     A -1 will be returned when the argument list has been exhausted.
+//!
+///////////////////////////////////////////////////////////////////////////////
+MP4V2_EXPORT
+int getOptionSingle( int argc, char* const* argv, const char* optstr, const Option* longopts, int* idx );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::prog
+
+#endif // MP4V2_PLATFORM_PROG_OPTION_H
diff --git a/libplatform/sys/error.cpp b/libplatform/sys/error.cpp
new file mode 100644
index 0000000..094ace1
--- /dev/null
+++ b/libplatform/sys/error.cpp
@@ -0,0 +1,31 @@
+#include "libplatform/impl.h"
+
+namespace mp4v2 { namespace platform { namespace sys {
+
+///////////////////////////////////////////////////////////////////////////////
+
+const char*
+getErrorStr( int errno_ )
+{
+    return std::strerror( errno_ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int
+getLastError()
+{
+    return errno;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const char*
+getLastErrorStr()
+{
+    return strerror( errno );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::sys
diff --git a/libplatform/sys/error.h b/libplatform/sys/error.h
new file mode 100644
index 0000000..8776458
--- /dev/null
+++ b/libplatform/sys/error.h
@@ -0,0 +1,16 @@
+#ifndef MP4V2_PLATFORM_SYS_ERROR_H
+#define MP4V2_PLATFORM_SYS_ERROR_H
+
+namespace mp4v2 { namespace platform { namespace sys {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4V2_EXPORT int         getLastError    ( );
+MP4V2_EXPORT const char* getLastErrorStr ( );
+MP4V2_EXPORT const char* getErrorStr     ( int );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::sys
+
+#endif // MP4V2_PLATFORM_SYS_ERROR_H
diff --git a/libplatform/time/time.cpp b/libplatform/time/time.cpp
new file mode 100644
index 0000000..5fe8867
--- /dev/null
+++ b/libplatform/time/time.cpp
@@ -0,0 +1,15 @@
+#include "libplatform/impl.h"
+
+namespace mp4v2 { namespace platform { namespace time {
+
+///////////////////////////////////////////////////////////////////////////////
+
+seconds_t
+getLocalTimeSeconds()
+{
+    return getLocalTimeMilliseconds() / 1000;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::time
diff --git a/libplatform/time/time.h b/libplatform/time/time.h
new file mode 100644
index 0000000..d4d8e7a
--- /dev/null
+++ b/libplatform/time/time.h
@@ -0,0 +1,48 @@
+#ifndef MP4V2_PLATFORM_TIME_TIME_H
+#define MP4V2_PLATFORM_TIME_TIME_H
+
+/// @namespace mp4v2::platform::time (private) Time.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+namespace mp4v2 { namespace platform { namespace time {
+
+//! type used to represent milliseconds
+typedef int64_t milliseconds_t;
+
+//! type used to represent seconds
+typedef int64_t seconds_t;
+
+///////////////////////////////////////////////////////////////////////////////
+//!
+//! Get local-time in milliseconds.
+//!
+//! getLocalTimeMilliseconds obtains the system's notion of current Greenwich
+//! time, adjusted according to the current timezone of the host system.
+//! The time is expressed as an absolute value since midnight (0 hour),
+//! January 1, 1970. This is commonly referred to as the "epoch".
+//!
+//! @return local-time in milliseconds elapsed since the epoch.
+//!
+///////////////////////////////////////////////////////////////////////////////
+MP4V2_EXPORT milliseconds_t getLocalTimeMilliseconds();
+
+///////////////////////////////////////////////////////////////////////////////
+//!
+//! Get local-time in seconds.
+//!
+//! getLocalTimeMilliseconds obtains the system's notion of current Greenwich
+//! time, adjusted according to the current timezone of the host system.
+//! The time is expressed as an absolute value since midnight (0 hour),
+//! January 1, 1970. This is commonly referred to as the "epoch".
+//!
+//! @return local-time in seconds elapsed since the epoch.
+//!
+///////////////////////////////////////////////////////////////////////////////
+MP4V2_EXPORT seconds_t getLocalTimeSeconds();
+
+///////////////////////////////////////////////////////////////////////////////
+//! @}
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::time
+
+#endif // MP4V2_PLATFORM_TIME_TIME_H
diff --git a/libplatform/time/time_posix.cpp b/libplatform/time/time_posix.cpp
new file mode 100644
index 0000000..e3c82f7
--- /dev/null
+++ b/libplatform/time/time_posix.cpp
@@ -0,0 +1,19 @@
+#include "libplatform/impl.h"
+#include <sys/time.h>
+
+namespace mp4v2 { namespace platform { namespace time {
+
+///////////////////////////////////////////////////////////////////////////////
+
+milliseconds_t
+getLocalTimeMilliseconds()
+{
+    timeval buf;
+    if( gettimeofday( &buf, 0 ))
+        memset( &buf, 0, sizeof( buf ));
+    return milliseconds_t( buf.tv_sec ) * 1000 + buf.tv_usec / 1000;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::time
diff --git a/libplatform/time/time_win32.cpp b/libplatform/time/time_win32.cpp
new file mode 100644
index 0000000..88a7ea5
--- /dev/null
+++ b/libplatform/time/time_win32.cpp
@@ -0,0 +1,18 @@
+#include "libplatform/impl.h"
+#include <sys/timeb.h>
+
+namespace mp4v2 { namespace platform { namespace time {
+
+///////////////////////////////////////////////////////////////////////////////
+
+milliseconds_t
+getLocalTimeMilliseconds()
+{
+    __timeb64 buf;
+    _ftime64( &buf );
+    return milliseconds_t( buf.time ) * 1000 + buf.millitm;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::platform::time
diff --git a/libplatform/warning.h b/libplatform/warning.h
new file mode 100644
index 0000000..2a236ab
--- /dev/null
+++ b/libplatform/warning.h
@@ -0,0 +1,30 @@
+#ifndef MP4V2_PLATFORM_WARNING_H
+#define MP4V2_PLATFORM_WARNING_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+// TODO-KB: clean code to avoid disabling warnings
+
+#if defined( __GNUC__ ) && ( __GNUC__ >= 4 ) && ( __GNUC_MINOR__ >= 2 )
+#   pragma GCC diagnostic ignored "-Wwrite-strings"
+#elif defined( _MSC_VER )
+#   pragma warning( disable: 4244 )
+#   pragma warning( disable: 4251 )
+#   pragma warning( disable: 4800 )
+#   pragma warning( disable: 4996 )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+// this macro is used to mark printf-style functions for GCC to examine
+// the format string and arguments and issue warnings if needed
+
+#if defined( __GNUC__ )
+#   define MP4V2_WFORMAT_PRINTF(i,j)  __attribute__((format(__printf__,i,j)))
+#else
+#   define MP4V2_WFORMAT_PRINTF(i,j)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_PLATFORM_WARNING_H
diff --git a/libutil/Database.cpp b/libutil/Database.cpp
new file mode 100644
index 0000000..9cd72ed
--- /dev/null
+++ b/libutil/Database.cpp
@@ -0,0 +1,225 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+Database::Database( const string& filename, const string& key )
+    : _filename ( filename )
+    , _key      ( key )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Database::~Database()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Database::close()
+{
+    _stream.close();
+    _stream.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Database::open( bool write, string& fname )
+{
+    _currentKeyValue.clear();
+
+    _stream.clear();
+    _stream.open( fname.c_str(), write ? ios::out : ios::in );
+    return _stream.rdstate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Database::parseData( map<string,string>& data )
+{
+    data.clear();
+
+    string name;
+    string value;
+
+    if ( _currentKeyValue.length() ) {
+        data[ _key ] = _currentKeyValue;
+        _currentKeyValue.clear();
+    }
+
+    while ( !parsePair( name, value ) ) {
+        if ( name == _key ) {
+            _currentKeyValue = value;
+            break;
+        }
+        data[ name ] = value;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Database::parsePair( string& name, string& value )
+{
+    enum Mode { LTRIM, COMMENT, NAME, DELIM, VALUE };
+    Mode mode = LTRIM;
+    bool delimVisible = false;
+
+    int visibleLength = 0;
+
+    for ( char c; !_stream.get( c ).rdstate(); ) {
+        switch( mode ) {
+            case LTRIM:
+                switch( c ) {
+                    case '\0':  // NULL
+                    case '\t':  // TAB
+                    case ' ':   // SPACE
+                    case '\n':  // NEWLINE
+                    case '\r':  // CARRIAGE-RETURN
+                        break;
+
+                    case '#':  // COMMENT
+                        mode = COMMENT;
+                        break;
+
+                    default:
+                        mode = NAME;
+                        name = tolower( c );
+                        break;
+                }
+                break;
+
+            case COMMENT:
+                switch( c ) {
+                    case '\n':  // NEWLINE
+                    case '\r':  // CARRIAGE-RETURN
+                        mode = LTRIM;
+                        name.clear();
+                        break;
+
+                    default:
+                        break;
+                }
+                break;
+
+            case NAME:
+                switch( c ) {
+                    case '\0':  // NULL
+                        break;
+
+                    case '\n':  // NEWLINE
+                    case '\r':  // CARRIAGE-RETURN
+                        mode = LTRIM;
+                        break;
+
+                    case '\t':  // TAB
+                    case ' ':   // SPACE
+                    case '=':   // DELIMITER
+                        mode = DELIM;
+                        delimVisible = false;
+                        break;
+
+                    default:
+                        name += tolower( c );
+                        break;
+                }
+                break;
+
+            case DELIM:
+                switch( c ) {
+                    case '\n':  // NEWLINE
+                    case '\r':  // CARRIAGE-RETURN
+                        mode = LTRIM;
+                        name.clear();
+                        break;
+
+                    case '\0':  // NULL
+                    case '\t':  // TAB
+                    case ' ':   // SPACE
+                        break;
+
+                    case '=':  // DELIMITER
+                        if( delimVisible ) {
+                            mode = VALUE;
+                            value = c;
+                            visibleLength = (uint32_t)value.length();
+                        }
+                        delimVisible = true;
+                        break;
+
+                    default:
+                        mode = VALUE;
+                        value = c;
+                        visibleLength = (uint32_t)value.length();
+                        break;
+                }
+                break;
+
+            case VALUE:
+                switch (c) {
+                    case '\0':  // NULL
+                        break;
+
+                    case '\n':  // NEWLINE
+                    case '\r':  // CARRIAGE-RETURN
+                        if( visibleLength )
+                            value.resize( visibleLength );
+                        return false;
+
+                    case '\t':  // TAB
+                    case ' ':   // SPACE
+                        value += ' ';
+                        break;
+
+                    default:
+                        value += c;
+                        visibleLength = (uint32_t)value.length();
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    if( mode != VALUE )
+        return true;
+
+    if( visibleLength )
+        value.resize( visibleLength );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
diff --git a/libutil/Database.h b/libutil/Database.h
new file mode 100644
index 0000000..9f96c3e
--- /dev/null
+++ b/libutil/Database.h
@@ -0,0 +1,105 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_UTIL_DATABASE_H
+#define MP4V2_UTIL_DATABASE_H
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// Database class is the base implementation for persistent databases.
+///
+/// All databases use an ASCII file format:
+///     @li leading/trailing spaces on any line are trimmed.
+///     @li lines beginning with '#' are considered comments.
+///     @li lines of { <NAME><DELIMITER><VALUE><EOL> } form NAME/VALUEs pairs.
+///     @li <NAME> cannot contain any <DELIMITER> characters.
+///     @li <DELIMITER> is any combination of { ' ', '=', '\t' }.
+///     @li <VALUE> continues until <EOL>.
+///     @li <EOL> is optional for last line.
+///     @li <NAME> of value this._key marks the beginning of a new record.
+///     @li <NAME> is case-insensitive.
+///     @li subsequent lines of NAME/VALUE pairs are part of the same record.
+///
+///////////////////////////////////////////////////////////////////////////////
+class Database {
+public:
+    virtual ~Database();
+
+protected:
+    /// Constructor.
+    ///
+    /// @param file specifies filename for IO operations.
+    /// @param key specifies the name of primary key.
+    ///
+    Database( const string& file, const string& key );
+
+    /// Close database file.
+    void close();
+
+    /// Open database file.
+    ///
+    /// @param write <b>true</b> to open file for writing, <b>false</b> for reading.
+    /// @param fname filename to open.
+    ///     On Windows, this should be a UTF-8 encoded string.
+    ///     On other platforms, it should be an 8-bit encoding that is
+    ///     appropriate for the platform, locale, file system, etc.
+    ///     (prefer to use UTF-8 when possible).
+    ///
+    /// @return <b>true</b> on error.
+    ///
+    bool open( bool write, string& fname );
+
+    /// Parse a record-data from open intput stream.
+    ///
+    /// @param data is populated with NAME/VALUE pairs if any.
+    ///
+    void parseData( map<string,string>& data );
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    const string  _filename; // filename (basename only) used for database
+    const string  _key;      // name of key for record boundries
+    fstream       _stream;   // // IO object
+
+private:
+    /// parse a name/value pair from open input stream.
+    ///
+    /// @param name stores the parsed name.
+    /// @param value stores the parsed value.
+    ///
+    /// @return <b>true</b> on error (no name/value pair was parised).
+    ///
+    bool parsePair( string& name, string& value );
+
+    /*************************************************************************/
+
+    string _currentKeyValue;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+#endif // MP4V2_UTIL_DATABASE_H
diff --git a/libutil/Timecode.cpp b/libutil/Timecode.cpp
new file mode 100644
index 0000000..195a009
--- /dev/null
+++ b/libutil/Timecode.cpp
@@ -0,0 +1,591 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode::Timecode( const Timecode& obj )
+    : _scale            ( 1.0 )
+    , _duration         ( 0 )
+    , _format           ( FRAME )
+    , _svalue           ( "" )
+    , _hours            ( 0 )
+    , _minutes          ( 0 )
+    , _seconds          ( 0 )
+    , _subseconds       ( 0 )
+    , scale             ( _scale )
+    , duration          ( _duration )
+    , format            ( _format )
+    , svalue            ( _svalue )
+    , hours             ( _hours )
+    , minutes           ( _minutes )
+    , seconds           ( _seconds )
+    , subseconds        ( _subseconds )
+{
+    operator=( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode::Timecode( const string& time_, double scale_ )
+    : _scale            ( scale_ < 1.0 ? 1.0 : scale_ )
+    , _duration         ( 0 )
+    , _format           ( FRAME )
+    , _svalue           ( "" )
+    , _hours            ( 0 )
+    , _minutes          ( 0 )
+    , _seconds          ( 0 )
+    , _subseconds       ( 0 )
+    , scale             ( _scale )
+    , duration          ( _duration )
+    , format            ( _format )
+    , svalue            ( _svalue )
+    , hours             ( _hours )
+    , minutes           ( _minutes )
+    , seconds           ( _seconds )
+    , subseconds        ( _subseconds )
+{
+    parse( time_ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode::Timecode( uint64_t duration_, double scale_ )
+    : _scale            ( scale_ < 1.0 ? 1.0 : scale_ )
+    , _duration         ( 0 )
+    , _format           ( FRAME )
+    , _svalue           ( "" )
+    , _hours            ( 0 )
+    , _minutes          ( 0 )
+    , _seconds          ( 0 )
+    , _subseconds       ( 0 )
+    , scale             ( _scale )
+    , duration          ( _duration )
+    , format            ( _format )
+    , svalue            ( _svalue )
+    , hours             ( _hours )
+    , minutes           ( _minutes )
+    , seconds           ( _seconds )
+    , subseconds        ( _subseconds )
+{
+    setDuration( duration_ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint64_t
+Timecode::convertDuration( const Timecode& obj ) const
+{
+    if( _scale == obj._scale )
+        return obj._duration;
+
+    return static_cast<uint64_t>( ( _scale / obj._scale ) * obj._duration );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode&
+Timecode::operator=( const Timecode& rhs )
+{
+    _scale    = rhs._scale;
+    _duration = rhs._duration;
+    _format   = FRAME;
+    _svalue   = rhs._svalue;
+
+    _hours      = rhs._hours;
+    _minutes    = rhs._minutes;
+    _seconds    = rhs._seconds;
+    _subseconds = rhs._subseconds;
+
+    return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode&
+Timecode::operator+=( const Timecode& rhs )
+{
+    uint64_t dur = _duration + convertDuration( rhs );
+    // overflow check
+    if( dur < _duration )
+        dur = numeric_limits<long long>::max();
+
+    setDuration( dur );
+    return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode&
+Timecode::operator-=( const Timecode& rhs )
+{
+    uint64_t dur = _duration - convertDuration( rhs );
+    // underflow check
+    if( dur > _duration )
+        dur = 0;
+
+    setDuration( dur );
+    return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator<( const Timecode& obj ) const
+{
+    return _duration < convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator<=( const Timecode& obj ) const
+{
+    return _duration <= convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator>( const Timecode& obj ) const
+{
+    return _duration < convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator>=( const Timecode& obj ) const
+{
+    return _duration < convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator!=( const Timecode& obj ) const
+{
+    return _duration != convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator==( const Timecode& obj ) const
+{
+    return _duration == convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode
+Timecode::operator+( const Timecode& obj ) const
+{
+    Timecode t( *this );
+    t += obj;
+    return t;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode
+Timecode::operator-( const Timecode& obj ) const
+{
+    Timecode t( *this );
+    t -= obj;
+    return t;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::parse( const string& time, string* outError )
+{
+    string outErrorPlacebo;
+    string& error = outError ? *outError : outErrorPlacebo;
+    error.clear();
+
+    _format     = FRAME;
+    _hours      = 0;
+    _minutes    = 0;
+    _seconds    = 0;
+    _subseconds = 0;
+
+    // bail if empty
+    if( time.empty() ) {
+        recompute();
+        return false;
+    }
+
+    // count number of ':'
+    int nsect = 0;
+    int nsemi = 0;
+    int ndot  = 0;
+
+    const string::size_type max = time.length();
+    for( string::size_type i = 0; i < max; i++ ) {
+        switch( time[i] ) {
+            case ':':
+                nsect++;
+                break;
+
+            case ';':
+                if( nsemi++ ) {
+                    error = "too many semicolons";
+                    return true;
+                }
+                nsect++;
+                break;
+
+            case '.':
+                if( ndot++ ) {
+                    error = "too many periods";
+                    return true;
+                }
+                nsect++;
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    // bail if impossible number of sections
+    if( nsect > 3 ) {
+        recompute();
+        error = "too many sections";
+        return true;
+    }
+
+    enum Target {
+        HOURS,
+        MINUTES,
+        SECONDS,
+        SUBSECONDS,
+    };
+
+    // setup target before parsing
+    Target target;
+    uint64_t* tvalue;
+    switch( nsect ) {
+        default:
+        case 0:
+            target = SUBSECONDS;
+            tvalue = &_subseconds;
+            break;
+
+        case 1:
+            target = SECONDS;
+            tvalue = &_seconds;
+            break;
+
+        case 2:
+            target = MINUTES;
+            tvalue = &_minutes;
+            break;
+
+        case 3:
+            target = HOURS;
+            tvalue = &_hours;
+            break;
+    }
+
+    istringstream convert;
+    string tbuffer;
+    for( string::size_type i = 0; i < max; i++ ) {
+        const char c = time[i];
+        switch( c ) {
+            case ':':
+                switch( target ) {
+                    case HOURS:
+                        convert.clear();
+                        convert.str( tbuffer );
+                        if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+                            error = "failed to convert integer";
+                            return true;
+                        }
+                        tbuffer.clear();
+                        target = MINUTES;
+                        tvalue = &_minutes;
+                        break;
+
+                    case MINUTES:
+                        convert.clear();
+                        convert.str( tbuffer );
+                        if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+                            error = "failed to convert integer";
+                            return true;
+                        }
+                        tbuffer.clear();
+                        target = SECONDS;
+                        tvalue = &_seconds;
+                        break;
+
+                    case SECONDS:
+                        convert.clear();
+                        convert.str( tbuffer );
+                        if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+                            error = "failed to convert integer";
+                            return true;
+                        }
+                        tbuffer.clear();
+                        target = SUBSECONDS;
+                        tvalue = &_subseconds;
+                        break;
+
+                    default:
+                    case SUBSECONDS:
+                        error = "unexpected char ':'";
+                        return true;
+                }
+                break;
+
+            case '.':
+            {
+                if( target != SECONDS ) {
+                    error = "unexpected char '.'";
+                    return true;
+                }
+                _format = DECIMAL;
+                convert.clear();
+                convert.str( tbuffer );
+                if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+                    error = "failed to convert integer";
+                    return true;
+                }
+                tbuffer.clear();
+                target = SUBSECONDS;
+                tvalue = &_subseconds;
+                break;
+            }
+
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+                tbuffer += c;
+                if( tbuffer.length() > 16 ) {
+                    error = "overflow";
+                    return true;
+                }
+                break;
+
+            default:
+                error = "unexpected char '";
+                error += c;
+                error += "'";
+                return true;
+        }
+    }
+
+    // apply final section
+    if( !tbuffer.empty() ) {
+        convert.clear();
+        convert.str( tbuffer );
+        if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+            error = "failed to convert integer";
+            return true;
+        }
+    }
+
+    // special post processing
+    switch( _format ) {
+        case FRAME:
+        default:
+            break;
+
+        case DECIMAL:
+        {
+            double div = std::pow( 10.0, static_cast<double>(tbuffer.length()) );
+            if( div < 1.0 )
+                div = 1.0;
+            *tvalue = static_cast<uint64_t>( static_cast<double>(*tvalue) / div * std::ceil( _scale ));
+            break;
+        }
+    }
+
+    recompute();
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::recompute()
+{
+    // case: 29.97 becomes 30.0
+    // case: 30.0  becomes 30.0
+    const uint64_t iscale = uint64_t( std::ceil( _scale ));
+
+    if( _subseconds > iscale - 1 ) {
+        uint64_t n = _subseconds / iscale;
+        _seconds += n;
+        _subseconds -= n * iscale;
+    }
+
+    if( _seconds > 59 ) {
+        uint64_t n = _seconds / 60;
+        _minutes += n;
+        _seconds -= n * 60;
+    }
+
+    if( _minutes > 59 ) {
+        uint64_t n = _minutes / 60;
+        _hours += n;
+        _minutes -= n * 60;
+    }
+
+    _duration = _subseconds + (iscale * _seconds) + (iscale * _minutes * 60) + (iscale * _hours * 3600);
+
+    ostringstream oss;
+    oss << setfill('0') << right
+        << setw(2) << _hours
+        << ':'
+        << setw(2) << _minutes
+        << ':'
+        << setw(2) << _seconds;
+
+    switch( _format ) {
+        case FRAME:
+            oss << ':' << setw(2) << setfill( '0' ) << _subseconds;
+            break;
+
+        case DECIMAL:
+        {
+            oss << '.' << setw(3) << setfill( '0' ) << static_cast<uint64_t>(_subseconds / _scale * 1000.0 + 0.5);
+            break;
+        }
+    }
+
+    _svalue = oss.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::reset()
+{
+    setDuration( 0 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setDuration( uint64_t duration_, double scale_ )
+{
+    if( scale_ != 0.0 ) {
+        _scale = scale_;
+        if( _scale < 1.0 )
+            _scale = 1.0;
+    }
+
+    _duration = duration_;
+
+    const uint64_t iscale = uint64_t( std::ceil( _scale ));
+    uint64_t i = _duration;
+
+    _hours = i / (iscale * 3600);
+    i -= (iscale * 3600 * _hours);
+
+    _minutes = i / (iscale * 60);
+    i -= (iscale * 60 * _minutes);
+
+    _seconds = i / iscale;
+    i -= (iscale * _seconds);
+
+    _subseconds = i;
+
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setFormat( Format format_ )
+{
+    _format = format_;
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setHours( uint64_t hours_ )
+{
+    _hours = hours_;
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setMinutes( uint64_t minutes_ )
+{
+    _minutes = minutes_;
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setScale( double scale_ )
+{
+    const double oldscale = _scale;
+    _scale = scale_;
+    if( _scale < 1.0 )
+        _scale = 1.0;
+
+    _subseconds = static_cast<uint64_t>( (_scale / oldscale) * _subseconds );
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setSeconds( uint64_t seconds_ )
+{
+    _seconds = seconds_;
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setSubseconds( uint64_t subseconds_ )
+{
+    _subseconds = subseconds_;
+    recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
diff --git a/libutil/Timecode.h b/libutil/Timecode.h
new file mode 100644
index 0000000..dd164e6
--- /dev/null
+++ b/libutil/Timecode.h
@@ -0,0 +1,119 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_UTIL_TIMECODE_H
+#define MP4V2_UTIL_TIMECODE_H
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Class representing SMPTE timecode.
+///
+/// Objects of this class represent a specific time or duration and can
+/// be converted to/from string values, and various epochs.
+///
+/// The standard pattern for string representation is as follows:
+///     @li HH:MM:SS:FF
+///     @li HH:MM:SS.DDD
+///
+/// where:
+///     @li <b>HH</b> specifies hours
+///     @li <b>MM</b> specifies minutes
+///     @li <b>SS</b> specifies seconds
+///     @li <b>:</b> specifies normal timecode
+///     @li <b>FF</b> specifies the frame number
+///     @li <b>.</b> specifies decimal fractions of a second follow
+///     @li <b>DDD</b> specifies decimal fractions of a second, rounded down to closest scale
+///
+class MP4V2_EXPORT Timecode {
+public:
+    enum Format {
+        FRAME,
+        DECIMAL,
+    };
+
+private:
+    double   _scale;
+    uint64_t _duration;
+    Format   _format;
+    string   _svalue;
+
+    uint64_t _hours;
+    uint64_t _minutes;
+    uint64_t _seconds;
+    uint64_t _subseconds;
+
+public:
+    const double&   scale;
+    const uint64_t& duration;
+    const Format&   format;
+    const string&   svalue;
+
+    const uint64_t& hours;
+    const uint64_t& minutes;
+    const uint64_t& seconds;
+    const uint64_t& subseconds;
+
+public:
+    Timecode( const Timecode& );
+    explicit Timecode( const string&, double = 1.0 );
+    explicit Timecode( uint64_t = 0, double = 1.0 );
+
+    Timecode& operator=  ( const Timecode& );
+    Timecode& operator+= ( const Timecode& );
+    Timecode& operator-= ( const Timecode& );
+
+    bool operator<  ( const Timecode& ) const;
+    bool operator<= ( const Timecode& ) const;
+    bool operator>  ( const Timecode& ) const;
+    bool operator>= ( const Timecode& ) const;
+    bool operator!= ( const Timecode& ) const;
+    bool operator== ( const Timecode& ) const;
+
+    Timecode operator+ ( const Timecode& ) const;
+    Timecode operator- ( const Timecode& ) const;
+
+    bool parse( const string&, string* = NULL );
+
+    void reset();
+
+    void setScale    ( double );
+    void setDuration ( uint64_t, double = 0.0 );
+    void setFormat   ( Format );
+
+    void setHours      ( uint64_t );
+    void setMinutes    ( uint64_t );
+    void setSeconds    ( uint64_t );
+    void setSubseconds ( uint64_t );
+
+private:
+    uint64_t convertDuration( const Timecode& ) const;
+    void recompute();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+#endif // MP4V2_UTIL_TIMECODE_H
diff --git a/libutil/TrackModifier.cpp b/libutil/TrackModifier.cpp
new file mode 100644
index 0000000..2fdae7e
--- /dev/null
+++ b/libutil/TrackModifier.cpp
@@ -0,0 +1,503 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+TrackModifier::TrackModifier( MP4FileHandle file_, uint16_t trackIndex_ )
+    : _track          ( refTrackAtom( *static_cast<MP4File*>(file_), trackIndex_ ))
+    , _props          ( *this ) // must come after _track is initialized
+    , _enabled        ( false )
+    , _inMovie        ( false )
+    , _inPreview      ( false )
+    , _layer          ( 0 )
+    , _alternateGroup ( 0 )
+    , _volume         ( 1.0f )
+    , _width          ( 0.0f )
+    , _height         ( 0.0f )
+    , _language       ( bmff::ILC_UND )
+    , _handlerType    ( "" )
+    , _handlerName    ( "" )
+    , _userDataName   ( "" )
+    , file            ( *static_cast<MP4File*>(file_) )
+    , trackIndex      ( trackIndex_ )
+    , trackId         ( MP4FindTrackId( file_, trackIndex_ ))
+    , enabled         ( _enabled )
+    , inMovie         ( _inMovie )
+    , inPreview       ( _inPreview )
+    , layer           ( _layer )
+    , alternateGroup  ( _alternateGroup )
+    , volume          ( _volume )
+    , width           ( _width )
+    , height          ( _height )
+    , language        ( _language )
+    , handlerType     ( _handlerType )
+    , handlerName     ( _handlerName )
+    , userDataName    ( _userDataName )
+{
+    fetch();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+TrackModifier::~TrackModifier()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::dump( ostream& out, const string& xind )
+{
+    const uint32_t w = 14;
+    const string eq = " = ";
+    const string ind = "  ";
+
+    out << left << xind << "track[" << trackIndex << "] id=" << trackId
+        << '\n' << xind << ind << setw( w ) << "type" << eq << toStringTrackType( handlerType )
+        << '\n' << xind << ind << setw( w ) << "enabled" << eq << toString( enabled )
+        << '\n' << xind << ind << setw( w ) << "inMovie"  << eq << toString( inMovie )
+        << '\n' << xind << ind << setw( w ) << "inPreview"  << eq << toString( inPreview )
+        << '\n' << xind << ind << setw( w ) << "layer"  << eq << layer
+        << '\n' << xind << ind << setw( w ) << "alternateGroup"  << eq << alternateGroup
+        << '\n' << xind << ind << setw( w ) << "volume"  << eq << toString( volume, 8, 8 )
+        << '\n' << xind << ind << setw( w ) << "width"  << eq << toString( width, 16, 16 )
+        << '\n' << xind << ind << setw( w ) << "height"  << eq << toString( height, 16, 16 )
+        << '\n' << xind << ind << setw( w ) << "language"  << eq << bmff::enumLanguageCode.toString( language, true )
+        << '\n' << xind << ind << setw( w ) << "handlerName"  << eq << handlerName;
+
+    out << '\n' << xind << ind << setw( w ) << "userDataName"  << eq
+        << ( _props.userDataName ? userDataName : "<absent>" );
+
+    out << '\n';
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::fetch()
+{
+    _props.update();
+
+    const uint32_t flags = _props.flags.GetValue();
+    _enabled   = flags & 0x01;
+    _inMovie   = flags & 0x02;
+    _inPreview = flags & 0x04;
+
+    _layer          = _props.layer.GetValue();
+    _alternateGroup = _props.alternateGroup.GetValue();
+    _volume         = _props.volume.GetValue();
+    _width          = _props.width.GetValue();
+    _height         = _props.height.GetValue();
+
+    _language     = _props.language.GetValue();
+    _handlerType  = _props.handlerType.GetValue();
+    _handlerName  = _props.handlerName.GetValue();
+
+    if( _props.userDataName ) {
+        uint8_t* buffer;
+        uint32_t size;
+        _props.userDataName->GetValue( &buffer, &size );
+        _userDataName = string( reinterpret_cast<char*>(buffer), size );
+    }
+    else {
+        _userDataName.clear();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool&
+TrackModifier::fromString( const string& src, bool& dst )
+{
+    if( src == "true" )
+        dst = true;
+    else if ( src == "false" )
+        dst = false;
+    else {
+        istringstream iss( src );
+        iss >> dst;
+        if( iss.rdstate() != ios::eofbit ) {
+            ostringstream oss;
+            oss << "invalid value: " << src;
+            throw new Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ );
+        }
+    }
+
+    return dst;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+float&
+TrackModifier::fromString( const string& src, float& dst )
+{
+    istringstream iss( src );
+    iss >> dst;
+    if( iss.rdstate() != ios::eofbit ) {
+        ostringstream oss;
+        oss << "invalid value: " << src;
+        throw new Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    return dst;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint16_t&
+TrackModifier::fromString( const string& src, uint16_t& dst )
+{
+    istringstream iss( src );
+    iss >> dst;
+    if( iss.rdstate() != ios::eofbit ) { 
+        ostringstream oss;
+        oss << "invalid value: " << src;
+        throw new Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }   
+
+    return dst;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackModifier::hasUserDataName() const
+{
+    return _props.userDataName != NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Atom&
+TrackModifier::refTrackAtom( MP4File& file, uint16_t index )
+{
+    MP4Atom& root = *file.FindAtom( NULL );
+
+    ostringstream oss;
+    oss << "moov.trak[" << index << "]";
+    MP4Atom* trak = root.FindAtom( oss.str().c_str() );
+    if( !trak ) {
+        oss.str( "" );
+        oss << "trackIndex " << index << " not found";
+        throw new Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    return *trak;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::removeUserDataName()
+{
+    MP4Atom* name = _track.FindAtom( "trak.udta.name" );
+    if( name )
+        name->GetParentAtom()->DeleteChildAtom( name );
+
+    MP4Atom* udta = _track.FindAtom( "trak.udta" );
+    if( udta && !udta->GetNumberOfChildAtoms() )
+        udta->GetParentAtom()->DeleteChildAtom( udta );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setAlternateGroup( uint16_t value )
+{
+    _props.alternateGroup.SetValue( value );
+    fetch();
+}
+
+void
+TrackModifier::setAlternateGroup( const string& value )
+{
+    uint16_t tmp;
+    setAlternateGroup( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setEnabled( bool value )
+{
+    _enabled = value;
+    _props.flags.SetValue( (_enabled ? 0x01 : 0) | (_inMovie ? 0x02 : 0) | (_inPreview ? 0x04 : 0) );
+    fetch();
+}
+
+void
+TrackModifier::setEnabled( const string& value )
+{
+    bool tmp;
+    setEnabled( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setHandlerName( const string& value )
+{
+    _props.handlerName.SetValue( value.c_str() );
+    fetch();
+}
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setHeight( float value )
+{
+    _props.height.SetValue( value );
+    fetch();
+}
+
+void
+TrackModifier::setHeight( const string& value )
+{
+    float tmp;
+    setHeight( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setInMovie( bool value )
+{
+    _inMovie = value;
+    _props.flags.SetValue( (_enabled ? 0x01 : 0) | (_inMovie ? 0x02 : 0) | (_inPreview ? 0x04 : 0) );
+    fetch();
+}
+
+void
+TrackModifier::setInMovie( const string& value )
+{
+    bool tmp;
+    setInMovie( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setInPreview( bool value )
+{
+    _inPreview = value;
+    _props.flags.SetValue( (_enabled ? 0x01 : 0) | (_inMovie ? 0x02 : 0) | (_inPreview ? 0x04 : 0) );
+    fetch();
+}
+
+void
+TrackModifier::setInPreview( const string& value )
+{
+    bool tmp;
+    setInPreview( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setLanguage( bmff::LanguageCode value )
+{
+    _props.language.SetValue( value );
+    fetch();
+}
+
+void
+TrackModifier::setLanguage( const string& value )
+{
+    setLanguage( bmff::enumLanguageCode.toType( value ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setLayer( uint16_t value )
+{
+    _props.layer.SetValue( value );
+    fetch();
+}
+
+void
+TrackModifier::setLayer( const string& value )
+{
+    uint16_t tmp;
+    setLayer( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setUserDataName( const string& value )
+{
+    if( !_props.userDataName ) {
+        ostringstream oss;
+        oss << "moov.trak[" << trackIndex << "]";
+        file.AddDescendantAtoms( oss.str().c_str(), "udta.name" );
+        _props.update();
+    }
+
+    _props.userDataName->SetValue( reinterpret_cast<const uint8_t*>(value.c_str()), (uint32_t)value.size() );
+    fetch();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setVolume( float value )
+{
+    _props.volume.SetValue( value );
+    fetch();
+}
+
+void
+TrackModifier::setVolume( const string& value )
+{
+    float tmp;
+    setVolume( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::setWidth( float value )
+{
+    _props.width.SetValue( value );
+    fetch();
+}
+
+void
+TrackModifier::setWidth( const string& value )
+{
+    float tmp;
+    setWidth( fromString( value, tmp ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+TrackModifier::toString( bool value )
+{
+    ostringstream oss;
+    oss << (value ? "true" : "false");
+    return oss.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+TrackModifier::toString( float value, uint8_t i, uint8_t f )
+{
+    ostringstream oss;
+    oss << fixed << setprecision(f <= 8 ? 4 : 8) << value;
+    return oss.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+TrackModifier::toStringTrackType( const string& code )
+{
+    if( !code.compare( "vide" ))    // 14496-12
+        return "video";
+    if( !code.compare( "soun" ))    // 14496-12
+        return "audio";
+    if( !code.compare( "hint" ))    // 14496-12
+        return "hint";
+
+    if( !code.compare( "text" ))    // QTFF
+        return "text";
+    if( !code.compare( "tmcd" ))    // QTFF
+        return "timecode";
+
+    if( !code.compare( "subt" ))    // QTFF
+        return "subtitle";
+
+    return string( "(" ) + code + ")";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+TrackModifier::Properties::Properties( TrackModifier& trackModifier_ )
+    : _trackModifier ( trackModifier_ )
+    , flags          ( static_cast<MP4Integer24Property&>   ( refProperty(  "trak.tkhd.flags" )))
+    , layer          ( static_cast<MP4Integer16Property&>   ( refProperty(  "trak.tkhd.layer" )))
+    , alternateGroup ( static_cast<MP4Integer16Property&>   ( refProperty(  "trak.tkhd.alternate_group" )))
+    , volume         ( static_cast<MP4Float32Property&>     ( refProperty(  "trak.tkhd.volume" )))
+    , width          ( static_cast<MP4Float32Property&>     ( refProperty(  "trak.tkhd.width" )))
+    , height         ( static_cast<MP4Float32Property&>     ( refProperty(  "trak.tkhd.height" )))
+    , language       ( static_cast<MP4LanguageCodeProperty&>( refProperty(  "trak.mdia.mdhd.language" )))
+    , handlerType    ( static_cast<MP4StringProperty&>      ( refProperty(  "trak.mdia.hdlr.handlerType" )))
+    , handlerName    ( static_cast<MP4StringProperty&>      ( refProperty(  "trak.mdia.hdlr.name" )))
+    , userDataName   ( static_cast<MP4BytesProperty*>       ( findProperty( "trak.udta.name.value" )))
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Property*
+TrackModifier::Properties::findProperty( const char* name )
+{
+    MP4Property* property;
+    if( !_trackModifier._track.FindProperty( name, &property ))
+        return NULL;
+
+    return property;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Property&
+TrackModifier::Properties::refProperty( const char* name )
+{
+    MP4Property* property;
+    if( !_trackModifier._track.FindProperty( name, &property )) {
+        ostringstream oss;
+        oss << "trackId " << _trackModifier.trackId << " property '" << name << "' not found";
+        throw new Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    return *property;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::Properties::update()
+{
+    // update optional properties
+    updateProperty( "trak.udta.name.value", reinterpret_cast<MP4Property**>( &userDataName ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+TrackModifier::Properties::updateProperty( const char* name, MP4Property** pp )
+{
+    *pp = NULL;
+    _trackModifier._track.FindProperty( name, pp );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
diff --git a/libutil/TrackModifier.h b/libutil/TrackModifier.h
new file mode 100644
index 0000000..798a061
--- /dev/null
+++ b/libutil/TrackModifier.h
@@ -0,0 +1,160 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_UTIL_TRACKMODIFIER_H
+#define MP4V2_UTIL_TRACKMODIFIER_H
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4V2_EXPORT TrackModifier
+{
+private:
+    class Properties
+    {
+    private:
+        TrackModifier& _trackModifier;
+
+    public:
+        Properties( TrackModifier& );
+
+        void update();
+
+        MP4Integer24Property&    flags;
+        MP4Integer16Property&    layer;
+        MP4Integer16Property&    alternateGroup;
+        MP4Float32Property&      volume;
+        MP4Float32Property&      width;
+        MP4Float32Property&      height;
+        MP4LanguageCodeProperty& language;
+        MP4StringProperty&       handlerType;
+        MP4StringProperty&       handlerName;
+        MP4BytesProperty*        userDataName;
+
+    private:
+        MP4Property& refProperty( const char* );
+        MP4Property* findProperty( const char* );
+        void updateProperty( const char*, MP4Property** );
+    };
+
+    friend class Properties;
+
+private:
+    static MP4Atom& refTrackAtom( MP4File&, uint16_t );
+
+private:
+    MP4Atom&   _track;
+    Properties _props;
+
+    // Track Header
+    bool     _enabled;
+    bool     _inMovie;
+    bool     _inPreview;
+    uint16_t _layer;
+    uint16_t _alternateGroup;
+    float    _volume;
+    float    _width;
+    float    _height;
+
+    // Media Header
+    bmff::LanguageCode _language;
+
+    // Handler Reference
+    string _handlerType;
+    string _handlerName;
+
+    // User Data name
+    string _userDataName;
+
+public:
+    MP4File&         file;
+    const uint16_t   trackIndex;
+    const MP4TrackId trackId;
+
+    const bool&     enabled;
+    const bool&     inMovie;
+    const bool&     inPreview;
+    const uint16_t& layer;
+    const uint16_t& alternateGroup;
+    const float&    volume;
+    const float&    width;
+    const float&    height;
+
+    const bmff::LanguageCode& language;
+
+    const string& handlerType;
+    const string& handlerName;
+
+    const string& userDataName;
+
+public:
+    TrackModifier( MP4FileHandle, uint16_t );
+    ~TrackModifier();
+
+    void setEnabled        ( bool );
+    void setInMovie        ( bool );
+    void setInPreview      ( bool );
+    void setLayer          ( uint16_t );
+    void setAlternateGroup ( uint16_t );
+    void setVolume         ( float );
+    void setWidth          ( float );
+    void setHeight         ( float );
+    void setLanguage       ( bmff::LanguageCode );
+    void setHandlerName    ( const string& );
+    void setUserDataName   ( const string& );
+
+    // set by string
+    void setEnabled        ( const string& );
+    void setInMovie        ( const string& );
+    void setInPreview      ( const string& );
+    void setLayer          ( const string& );
+    void setAlternateGroup ( const string& );
+    void setVolume         ( const string& );
+    void setWidth          ( const string& );
+    void setHeight         ( const string& );
+    void setLanguage       ( const string& );
+
+    bool hasUserDataName() const;
+    void removeUserDataName();
+
+    void dump( ostream&, const string& );
+
+private:
+    void fetch();
+
+    static string toString( bool );
+    static string toString( float, uint8_t, uint8_t );
+
+    static bool&     fromString( const string&, bool& );
+    static float&    fromString( const string&, float& );
+    static uint16_t& fromString( const string&, uint16_t& );
+
+    static string toStringTrackType( const string& );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+#endif // MP4V2_UTIL_TRACKMODIFIER_H
diff --git a/libutil/Utility.cpp b/libutil/Utility.cpp
new file mode 100644
index 0000000..76cdd12
--- /dev/null
+++ b/libutil/Utility.cpp
@@ -0,0 +1,758 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+Utility::Utility( string name_, int argc_, char** argv_ )
+    : _longOptions      ( NULL )
+    , _name             ( name_ )
+    , _argc             ( argc_ )
+    , _argv             ( argv_ )
+    , _optimize         ( false )
+    , _dryrun           ( false )
+    , _keepgoing        ( false )
+    , _overwrite        ( false )
+    , _force            ( false )
+    , _debug            ( 0 )
+    , _verbosity        ( 1 )
+    , _jobCount         ( 0 )
+    , _debugImplicits   ( false )
+    , _group            ( "OPTIONS" )
+
+,STD_OPTIMIZE( 'z', false, "optimize", false, LC_NONE, "optimize mp4 file after modification" )
+,STD_DRYRUN( 'y', false, "dryrun", false, LC_NONE, "do not actually create or modify any files" )
+,STD_KEEPGOING( 'k', false, "keepgoing", false, LC_NONE, "continue batch processing even after errors" )
+,STD_OVERWRITE( 'o', false, "overwrite", false, LC_NONE, "overwrite existing files when creating" )
+,STD_FORCE( 'f', false, "force", false, LC_NONE, "force overwrite even if file is read-only" )
+,STD_QUIET( 'q', false, "quiet", false, LC_NONE, "equivalent to --verbose 0" )
+,STD_DEBUG( 'd', false, "debug", true, LC_DEBUG, "increase debug or long-option to set NUM", "NUM",
+    // 79-cols, inclusive, max desired width
+    // |----------------------------------------------------------------------------|
+    "\nDEBUG LEVELS (for raw mp4 file I/O)"
+    "\n  0  supressed"
+    "\n  1  add warnings and errors (default)"
+    "\n  2  add table details"
+    "\n  3  add implicits"
+    "\n  4  everything" )
+,STD_VERBOSE( 'v', false, "verbose", true, LC_VERBOSE, "increase verbosity or long-option to set NUM", "NUM",
+    // 79-cols, inclusive, max desired width
+    // |----------------------------------------------------------------------------|
+    "\nVERBOSE LEVELS"
+    "\n  0  warnings and errors"
+    "\n  1  normal informative messages (default)"
+    "\n  2  more informative messages"
+    "\n  3  everything" )
+,STD_HELP( 'h', false, "help", false, LC_HELP, "print brief help or long-option for extended help" )
+,STD_VERSION( 0, false, "version", false, LC_VERSION, "print version information and exit" )
+,STD_VERSIONX( 0, false, "versionx", false, LC_VERSIONX, "print extended version information", "ARG", "", true )
+
+{
+    debugUpdate( 1 );
+
+    _usage = "<UNDEFINED>";
+    _description = "<UNDEFINED>";
+    _groups.push_back( &_group );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Utility::~Utility()
+{
+    delete[] _longOptions;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::batch( int argi )
+{
+    _jobCount = 0;
+    _jobTotal = _argc - argi;
+
+    // nothing to be done
+    if( !_jobTotal )
+        return SUCCESS;
+
+    bool batchResult = FAILURE;
+    for( int i = argi; i < _argc; i++ ) {
+        bool subResult = FAILURE;
+        try {
+            if( !job( _argv[i] )) {
+                batchResult = SUCCESS;
+                subResult = SUCCESS;
+            }
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+ 
+        if( !_keepgoing && subResult == FAILURE )
+            return FAILURE;
+    }
+    
+    return batchResult;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::debugUpdate( uint32_t debug )
+{
+    MP4LogLevel level;
+
+    _debug = debug;
+    verbose2f( "debug level: %u\n", _debug );
+
+    switch( _debug ) {
+        case 0:
+            level = MP4_LOG_NONE;
+            _debugImplicits = false;
+            break;
+
+        case 1:
+            level = MP4_LOG_ERROR;
+            _debugImplicits = false;
+            break;
+
+        case 2:
+            level = MP4_LOG_VERBOSE2;
+            _debugImplicits = false;
+            break;
+
+        case 3:
+            level = MP4_LOG_VERBOSE2;
+            _debugImplicits = true;
+            break;
+
+        case 4:
+        default:
+            level = MP4_LOG_VERBOSE4;
+            _debugImplicits = true;
+            break;
+    }
+
+    MP4LogSetLevel(level);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::dryrunAbort()
+{
+    if( !_dryrun )
+        return false;
+
+    verbose2f( "skipping: dry-run mode enabled\n" );
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::errf( const char* format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+    vfprintf( stderr, format, ap );
+    va_end( ap );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::formatGroups()
+{
+    // determine longest long-option [+space +argname]
+    int longMax = 0;
+    list<Group*>::reverse_iterator ie = _groups.rend();
+    for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++ ) {
+        Group& group = **it;
+        const Group::List::const_iterator ieo = group.options.end();
+        for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++ ) {
+            const Option& option = **ito;
+            if( option.hidden )
+                continue;
+
+            int len = (int)option.lname.length();
+            if( option.lhasarg )
+                len += 1 + (int)option.argname.length();
+            if( len > longMax )
+                longMax = len;
+        }
+    }
+
+    // format help output (no line-wrapping yet)
+    ostringstream oss;
+
+    int groupCount = 0;
+    int optionCount = 0;
+    ie = _groups.rend();
+    for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++, groupCount++ ) {
+        if( groupCount )
+            oss << '\n';
+        Group& group = **it;
+        oss << '\n' << group.name;
+        const Group::List::const_iterator ieo = group.options.end();
+        for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++, optionCount++ ) {
+            const Option& option = **ito;
+            if( option.hidden )
+                continue;
+
+            oss << "\n ";
+
+            if( option.scode == 0 )
+                oss << "    --";
+            else
+                oss << '-' << option.scode << ", --";
+
+            if( option.lhasarg ) {
+                oss << option.lname << ' ' << option.argname;
+                oss << setw( longMax - option.lname.length() - 1 - option.argname.length() ) << "";
+            }
+            else {
+                oss << setw( longMax ) << left << option.lname;
+            }
+
+            oss << "  ";
+
+            const string::size_type imax = option.descr.length();
+            for( string::size_type i = 0; i < imax; i++ )
+                oss << option.descr[i];
+        }
+    }
+
+    _help = oss.str();
+
+    // allocate and populate C-style options
+    delete[] _longOptions;
+    _longOptions = new prog::Option[optionCount + 1];
+
+    // fill EOL marker
+    _longOptions[optionCount].name = NULL;
+    _longOptions[optionCount].type = prog::Option::NO_ARG;
+    _longOptions[optionCount].flag = 0;
+    _longOptions[optionCount].val  = 0;
+
+    _shortOptions.clear();
+
+    int optionIndex = 0;
+    ie = _groups.rend();
+    for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++ ) {
+        Group& group = **it;
+        const Group::List::const_iterator ieo = group.options.end();
+        for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++, optionIndex++ ) {
+            const Option& a = **ito;
+            prog::Option& b = _longOptions[optionIndex];
+
+            b.name = const_cast<char*>(a.lname.c_str());
+            b.type = a.lhasarg ? prog::Option::REQUIRED_ARG : prog::Option::NO_ARG;
+            b.flag = 0;
+            b.val  = (a.lcode == LC_NONE) ? a.scode : a.lcode;
+
+            if( a.scode != 0 ) {
+                _shortOptions += a.scode;
+                if( a.shasarg )
+                    _shortOptions += ':';
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::job( string arg )
+{
+    verbose2f( "job begin: %s\n", arg.c_str() );
+
+    // perform job
+    JobContext job( arg );
+    bool result = FAILURE;
+    try {
+        result = utility_job( job );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+
+    // close file handle flagged with job
+    if( job.fileHandle != MP4_INVALID_FILE_HANDLE ) {
+        verbose2f( "closing %s\n", job.file.c_str() );
+        MP4Close( job.fileHandle );
+
+        // invoke optimize if flagged
+        if( _optimize && job.optimizeApplicable ) {
+            verbose1f( "optimizing %s\n", job.file.c_str() );
+            if( !MP4Optimize( job.file.c_str(), NULL ))
+                hwarnf( "optimize failed: %s\n", job.file.c_str() );
+        }
+    }
+
+    // free data flagged with job
+    list<void*>::iterator ie = job.tofree.end();
+    for( list<void*>::iterator it = job.tofree.begin(); it != ie; it++ )
+        free( *it );
+
+
+    verbose2f( "job end\n" );
+    _jobCount++;
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::herrf( const char* format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+
+    if( _keepgoing ) {
+        fprintf( stdout, "WARNING: " );
+        vfprintf( stdout, format, ap );
+    }
+    else {
+        fprintf( stderr, "ERROR: " );
+        vfprintf( stderr, format, ap );
+    }
+
+    va_end( ap );
+    return FAILURE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::hwarnf( const char* format, ... )
+{
+    fprintf( stdout, "WARNING: " );
+    va_list ap;
+    va_start( ap, format );
+    vfprintf( stdout, format, ap );
+    va_end( ap );
+    return FAILURE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::outf( const char* format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+    vfprintf( stdout, format, ap );
+    va_end( ap );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::printHelp( bool extended, bool toerr )
+{
+    ostringstream oss;
+    oss << "Usage: " << _name << " " << _usage << '\n' << _description << '\n' << _help;
+
+    if( extended ) {
+        const list<Group*>::reverse_iterator ie = _groups.rend();
+        for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++ ) {
+            Group& group = **it;
+            const Group::List::const_iterator ieo = group.options.end();
+            for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++ ) {
+                const Option& option = **ito;
+                if( option.help.empty() )
+                    continue;
+
+                oss << '\n' << option.help;
+            }
+        }
+    }
+
+    if( toerr )
+        errf( "%s\n\n", oss.str().c_str() );
+    else
+        outf( "%s\n\n", oss.str().c_str() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::printUsage( bool toerr )
+{
+    ostringstream oss;
+    oss <<   "Usage: " << _name << " " << _usage
+        << "\nTry -h for brief help or --help for extended help";
+ 
+    if( toerr )
+        errf( "%s\n", oss.str().c_str() );
+    else
+        outf( "%s\n", oss.str().c_str() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::printVersion( bool extended )
+{
+    ostringstream oss;
+    oss << left;
+
+    if( extended ) {
+        oss <<         setw(13) << "utility:" << _name
+            << '\n' << setw(13) << "product:" << MP4V2_PROJECT_name
+            << '\n' << setw(13) << "version:" << MP4V2_PROJECT_version
+            << '\n' << setw(13) << "build date:" << MP4V2_PROJECT_build
+            << '\n'
+            << '\n' << setw(18) << "repository URL:" << MP4V2_PROJECT_repo_url
+            << '\n' << setw(18) << "repository root:" << MP4V2_PROJECT_repo_root
+            << '\n' << setw(18) << "repository UUID:" << MP4V2_PROJECT_repo_uuid
+            << '\n' << setw(18) << "repository rev:" << MP4V2_PROJECT_repo_rev
+            << '\n' << setw(18) << "repository date:" << MP4V2_PROJECT_repo_date
+            << '\n' << setw(18) << "repository type:" << MP4V2_PROJECT_repo_type;
+    }
+    else {
+        oss << _name << " - " << MP4V2_PROJECT_name_formal;
+    }
+
+    outf( "%s\n", oss.str().c_str() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::process()
+{
+    bool rv = true;
+
+    try {
+        rv = process_impl();
+    }
+    catch( Exception* x ) {
+        _keepgoing = false;
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+
+    return rv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::process_impl()
+{
+    formatGroups();
+
+    // populate code lookup set
+    set<int> codes;
+    const Group::List::const_iterator ie = _group.options.end();
+    for( Group::List::const_iterator it = _group.options.begin(); it != ie; it++ ) {
+        const Option& option = **it;
+        if( option.scode != 0 )
+            codes.insert( option.scode );
+        if( option.lcode != LC_NONE )
+            codes.insert( option.lcode );
+    }
+
+    for( ;; ) {
+        const int code = prog::getOption( _argc, _argv, _shortOptions.c_str(), _longOptions, NULL );
+        if( code == -1 )
+            break;
+
+        bool handled = false;
+        if( utility_option( code, handled ))
+            return FAILURE;
+        if( handled )
+            continue;
+
+        if( codes.find( code ) == codes.end() )
+            continue;
+
+        switch( code ) {
+            case 'z':
+                _optimize = true;
+                break;
+
+            case 'y':
+                _dryrun = true;
+                break;
+
+            case 'k':
+                _keepgoing = true;
+                break;
+
+            case 'o':
+                _overwrite = true;
+                break;
+
+            case 'f':
+                _force = true;
+                break;
+
+            case 'q':
+                _verbosity = 0;
+                debugUpdate( 0 );
+                break;
+
+            case 'v':
+                _verbosity++;
+                break;
+
+            case 'd':
+                debugUpdate( _debug + 1 );
+                break;
+
+            case 'h':
+                printHelp( false, false );
+                return SUCCESS;
+
+            case LC_DEBUG:
+                debugUpdate( std::strtoul( prog::optarg, NULL, 0 ) );
+                break;
+
+            case LC_VERBOSE:
+            {
+                const uint32_t level = std::strtoul( prog::optarg, NULL, 0 );
+                _verbosity = ( level < 4 ) ? level : 3;
+                break;
+            }
+
+            case LC_HELP:
+                printHelp( true, false );
+                return SUCCESS;
+
+            case LC_VERSION:
+                printVersion( false );
+                return SUCCESS;
+
+            case LC_VERSIONX:
+                printVersion( true );
+                return SUCCESS;
+
+            default:
+                printUsage( true );
+                return FAILURE;
+        }
+    }
+
+    if( !(prog::optind < _argc) ) {
+        printUsage( true );
+        return FAILURE;
+    }
+
+    const bool result = batch( prog::optind );
+    verbose2f( "exit code %d\n", result );
+    return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Utility::openFileForWriting( io::File& file )
+{
+    // simple case is file does not exist
+    if( !io::FileSystem::exists( file.name )) {
+        if( file.open() )
+            return herrf( "unable to open %s for write: %s\n", file.name.c_str(), sys::getLastErrorStr() );
+        return SUCCESS;
+    }
+
+    // fail if overwrite is not enabled
+    if( !_overwrite )
+        return herrf( "file already exists: %s\n", file.name.c_str() );
+
+    // only overwrite if it is a file
+    if( !io::FileSystem::isFile( file.name ))
+        return herrf( "cannot overwrite non-file: %s\n", file.name.c_str() );
+
+    // first attemp to re-open/truncate so as to keep any file perms
+    if( !file.open() )
+        return SUCCESS;
+
+    // fail if force is not enabled
+    if( !_force )
+        return herrf( "unable to overwrite file: %s\n", file.name.c_str() );
+
+    // first attempt to open, truncating file
+    if( !file.open() )
+        return SUCCESS;
+
+    // nuke file
+    if( ::remove( file.name.c_str() ))
+        return herrf( "unable to remove %s: %s\n", file.name.c_str(), sys::getLastErrorStr() );
+
+    // final effort
+    if( !file.open() )
+        return SUCCESS;
+
+    return herrf( "unable to open %s for write: %s\n", file.name.c_str(), sys::getLastErrorStr() );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::verbose( uint32_t level, const char* format, va_list ap )
+{
+    if( level > _verbosity )
+        return;
+    vfprintf( stdout, format, ap );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::verbose1f( const char* format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+    verbose( 1, format, ap );
+    va_end( ap );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::verbose2f( const char* format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+    verbose( 2, format, ap );
+    va_end( ap );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::verbose3f( const char* format, ... )
+{
+    va_list ap;
+    va_start( ap, format );
+    verbose( 3, format, ap );
+    va_end( ap );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const bool Utility::SUCCESS = false;
+const bool Utility::FAILURE = true;
+
+///////////////////////////////////////////////////////////////////////////////
+
+Utility::Group::Group( string name_ )
+    : name    ( name_ )
+    , options ( _options )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Utility::Group::~Group()
+{
+    const List::iterator ie = _optionsDelete.end();
+    for( List::iterator it = _optionsDelete.begin(); it != ie; it++ )
+        delete *it;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::Group::add( const Option& option )
+{
+    _options.push_back( &option );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::Group::add(
+    char     scode,
+    bool     shasarg,
+    string   lname,
+    bool     lhasarg,
+    uint32_t lcode,
+    string   descr,
+    string   argname,
+    string   help,
+    bool     hidden )
+{
+    Option* o = new Option( scode, shasarg, lname, lhasarg, lcode, descr, argname, help, hidden );
+    _options.push_back( o );
+    _optionsDelete.push_back( o );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Utility::Group::add( 
+    string   lname,
+    bool     lhasarg,
+    uint32_t lcode,
+    string   descr,
+    string   argname,
+    string   help,
+    bool     hidden )
+{
+    add( 0, false, lname, lhasarg, lcode, descr, argname, help, hidden );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Utility::Option::Option(
+    char     scode_,
+    bool     shasarg_,
+    string   lname_,
+    bool     lhasarg_,
+    uint32_t lcode_,
+    string   descr_,
+    string   argname_,
+    string   help_,
+    bool     hidden_ )
+        : scode   ( scode_ )
+        , shasarg ( shasarg_ )
+        , lname   ( lname_ )
+        , lhasarg ( lhasarg_ )
+        , lcode   ( lcode_ )
+        , descr   ( descr_ )
+        , argname ( argname_ )
+        , help    ( help_ )
+        , hidden  ( hidden_ )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Utility::JobContext::JobContext( string file_ )
+    : file               ( file_ )
+    , fileHandle         ( MP4_INVALID_FILE_HANDLE )
+    , optimizeApplicable ( false )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
diff --git a/libutil/Utility.h b/libutil/Utility.h
new file mode 100644
index 0000000..26d57b8
--- /dev/null
+++ b/libutil/Utility.h
@@ -0,0 +1,205 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_UTIL_UTILITY_H
+#define MP4V2_UTIL_UTILITY_H
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// Utility general program class.
+///
+/// This class provides a base implementation for a general utility which
+/// helps meet behavioral criteria for command-line executables.
+///
+/// Inherit batch processing ability is also provided and is used optionally
+/// by the concrete implementation.
+///
+/// Criteria and guidelines for utility behavior in MP4v2 are as follows:
+///     @li exit with 0 when the utility succeeds at its main task, 1 for failure.
+///     @li print brief-usage when no arguments are supplied and exit with 1.
+///     @li print brief-usage when too many arguments are supplied and exit with 1.
+///     @li issue brief-usage when invalid argument is supplied and exit with 1.
+///     @li support --help option and exit with 0.
+///     @li support --version option and exit with 0.
+///     @li utilities which <b>create</b> new output files should never
+///         over-write an existing file unless given the project's universal
+///         '-o' or '--overwrite' option.
+///
+///////////////////////////////////////////////////////////////////////////////
+class MP4V2_EXPORT Utility
+{
+protected:
+    enum LongCode {
+        LC_NONE = 0xf0000000, // safe (cannot conflict with char values)
+        LC_DEBUG,
+        LC_VERBOSE,
+        LC_HELP,
+        LC_VERSION,
+        LC_VERSIONX,
+        _LC_MAX // will be used to seeed derived-class long-codes enum
+    };
+
+    class MP4V2_EXPORT Option {
+    public:
+        Option( char, bool, string, bool, uint32_t, string, string = "ARG", string = "", bool = false );
+
+        const char     scode;
+        const bool     shasarg;
+        const string   lname;
+        const bool     lhasarg;
+        const uint32_t lcode;
+        const string   descr;
+        const string   argname;
+        const string   help;
+        const bool     hidden;
+    };
+
+    class MP4V2_EXPORT Group {
+    public:
+        explicit Group( string );
+        ~Group();
+
+        void add( const Option& ); // options added this way will not be deleted
+        void add( char, bool, string, bool, uint32_t, string, string = "ARG", string = "", bool = false );
+        void add( string, bool, uint32_t, string, string = "ARG", string = "", bool = false );
+
+        const string name;
+
+    public:
+        typedef list<const Option*> List;
+
+    private:
+        List _options;
+        List _optionsDelete;
+
+    public:
+        const List& options;
+    };
+
+    //! structure passed as argument to each job during batch processing
+    class MP4V2_EXPORT JobContext
+    {
+    public:
+        JobContext( string file_ );
+
+        const string  file;               //!< file job is working on
+        MP4FileHandle fileHandle;         //!< handle of file, if applicable to job
+        bool          optimizeApplicable; //!< indicate file optimization is applicable
+        list<void*>   tofree;             //!< memory to free at end of job
+    };
+
+public:
+    virtual ~Utility();
+
+    bool process();
+
+protected:
+    Utility( string, int, char** );
+
+    void printUsage   ( bool );       //!< print usage
+    void printHelp    ( bool, bool ); //!< print help
+    void printVersion ( bool );       //!< print utility version
+
+    void errf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stderr
+    void outf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stdout
+
+    bool herrf  ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stderr indicating error
+    bool hwarnf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stderr indicating warning
+
+    void verbose1f ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void verbose2f ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void verbose3f ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+
+    bool batch ( int );    //!< process all remaining arguments (jobs)
+    bool job   ( string ); //!< process next argument
+
+    //! open file in consideration of overwrite/force options
+    bool openFileForWriting( io::File& );
+
+    bool dryrunAbort();
+
+    // delegates
+    virtual bool utility_option( int, bool& ) = 0; //!< process command-line option
+    virtual bool utility_job( JobContext& )   = 0; //!< process positional argument
+
+private:
+    void formatGroups();
+    void debugUpdate( uint32_t );
+    void verbose( uint32_t, const char*, va_list );
+    bool process_impl();
+
+private:
+    string _help;
+
+    prog::Option* _longOptions;
+    string        _shortOptions;
+
+protected:
+    const string       _name; //!< executable basename
+    const int          _argc; //!< arg count
+    char* const* const _argv; //!< arg vector
+
+    // common options state
+    bool     _optimize;  //!< optimize mp4 file after modification
+    bool     _dryrun;    //!< dry-run, no writing is actually performed
+    bool     _keepgoing; //!< contine batch processing even after error
+    bool     _overwrite; //!< overwrite file if already exists
+    bool     _force;     //!< force overwriting a file even if read-only
+    uint32_t _debug;     //!< mp4 file I/O verbosity
+    uint32_t _verbosity; //!< verbosity level, default=1
+
+    uint32_t          _jobCount;
+    uint32_t          _jobTotal;
+    bool              _debugImplicits;
+
+    Group         _group; // group to which standard options are added
+    string        _usage;
+    string        _description;
+    list<Group*>  _groups;
+
+protected:
+    // standard options for concrete utilities to add to _group in constructor
+    const Option STD_OPTIMIZE;
+    const Option STD_DRYRUN;
+    const Option STD_KEEPGOING;
+    const Option STD_OVERWRITE;
+    const Option STD_FORCE;
+    const Option STD_QUIET;
+    const Option STD_DEBUG;
+    const Option STD_VERBOSE;
+    const Option STD_HELP;
+    const Option STD_VERSION;
+    const Option STD_VERSIONX;
+
+public:
+    static const bool SUCCESS;
+    static const bool FAILURE;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+#endif // MP4V2_UTIL_UTILITY_H
diff --git a/libutil/crc.cpp b/libutil/crc.cpp
new file mode 100644
index 0000000..18d1147
--- /dev/null
+++ b/libutil/crc.cpp
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1988, 1992, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t
+crc32( const unsigned char* data, uint32_t size )
+{
+    static const uint32_t __crctab[256] = {
+        0x0,
+        0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+        0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+        0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+        0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+        0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+        0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+        0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+        0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+        0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+        0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+        0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+        0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+        0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+        0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+        0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+        0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+        0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+        0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+        0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+        0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+        0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+        0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+        0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+        0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+        0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+        0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+        0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+        0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+        0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+        0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+        0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+        0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+        0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+        0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+        0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+        0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+        0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+        0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+        0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4,
+    };
+
+#define COMPUTE(var,ch) (var) = (var) << 8 ^ __crctab[(var) >> 24 ^ (ch)]
+
+    uint32_t crc = 0;
+    const unsigned char* const max = data + size;
+
+    for (const unsigned char* p = data; p < max; p++)
+        COMPUTE( crc, *p );
+
+    for( ; size != 0; size >>= 8 )
+        COMPUTE( crc, size & 0xff );
+
+    return ~crc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
diff --git a/libutil/crc.h b/libutil/crc.h
new file mode 100644
index 0000000..470b14d
--- /dev/null
+++ b/libutil/crc.h
@@ -0,0 +1,38 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_UTIL_CRC_H
+#define MP4V2_UTIL_CRC_H
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4V2_EXPORT
+uint32_t crc32( const unsigned char*, uint32_t ); // ISO/IEC 8802-3:1989
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+#endif // MP4V2_UTIL_CRC_H
diff --git a/libutil/impl.h b/libutil/impl.h
new file mode 100644
index 0000000..3824a14
--- /dev/null
+++ b/libutil/impl.h
@@ -0,0 +1,10 @@
+#ifndef MP4V2_UTIL_IMPL_H
+#define MP4V2_UTIL_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "util.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_UTIL_IMPL_H
diff --git a/libutil/other.cpp b/libutil/other.cpp
new file mode 100644
index 0000000..698b588
--- /dev/null
+++ b/libutil/other.cpp
@@ -0,0 +1,106 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+    using namespace mp4v2::impl;
+
+///////////////////////////////////////////////////////////////////////////////
+
+// search atom recursively for any 64-bit characteristics.
+// nlargsize indicates number of atoms which use largesize extension.
+// nversion1 indicates number of atoms which use version==1 extension.
+// nspecial indicates number of special 64-bit atoms;
+//   eg: stbl may container one of { stco || co64 } for chunkoffsets.
+
+void
+searchFor64bit( MP4Atom& atom, FileSummaryInfo& info )
+{
+    const uint32_t max = atom.GetNumberOfChildAtoms();
+    for( uint32_t i = 0; i < max; i++ ) {
+        MP4Atom& child = *atom.GetChildAtom( i );
+
+        if( child.GetLargesizeMode() )
+            info.nlargesize++;
+
+        MP4Integer8Property* version;
+        if( child.FindProperty( "version", (MP4Property**)&version ) && version->GetValue() == 1 )
+            info.nversion1++;
+
+        if( !strcmp( child.GetType(), "co64" ))
+            info.nspecial++;
+
+        searchFor64bit( child, info );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+fileFetchSummaryInfo( MP4FileHandle file, FileSummaryInfo& info )
+{
+    if( file == MP4_INVALID_FILE_HANDLE )
+        return true;
+    MP4File& mp4 = *((MP4File*)file);
+
+    MP4Atom* root = mp4.FindAtom( "" );
+    if( !root )
+        return true;
+
+    MP4FtypAtom* ftyp = (MP4FtypAtom*)root->FindAtom( "ftyp" );
+    if( !ftyp )
+        return true;
+
+    info.major_brand   = ftyp->majorBrand.GetValue();
+    info.minor_version = ftyp->minorVersion.GetValue();
+
+    const uint32_t cbmax = ftyp->compatibleBrands.GetCount();
+    for( uint32_t i = 0; i < cbmax; i++ ) {
+        string s = ftyp->compatibleBrands.GetValue( i );
+
+        // remove spaces so brand set is presentable
+        string stripped;
+        const string::size_type max = s.length();
+        for( string::size_type pos = 0; pos < max; pos++ ) {
+            if( s[pos] != ' ' )
+                stripped += s[pos];
+        }
+
+        if( stripped.empty() )
+            continue;
+
+        info.compatible_brands.insert( stripped );
+    }
+
+    info.nlargesize = 0;
+    info.nversion1  = 0;
+    info.nspecial   = 0;
+    searchFor64bit( *root, info );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
diff --git a/libutil/other.h b/libutil/other.h
new file mode 100644
index 0000000..83d5411
--- /dev/null
+++ b/libutil/other.h
@@ -0,0 +1,40 @@
+#ifndef MP4V2_UTIL_OTHER_H
+#define MP4V2_UTIL_OTHER_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct MP4V2_EXPORT FileSummaryInfo {
+    typedef set<string>  BrandSet;
+
+    // standard ftyp box attributes
+    string   major_brand;
+    uint32_t minor_version;
+    BrandSet compatible_brands;
+
+    uint32_t nlargesize;
+    uint32_t nversion1;
+    uint32_t nspecial;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// Fetch mp4 file summary information.
+///
+/// This function fetches summary information for <b>file</b> and information
+/// is stored in <b>info</b>.
+///
+/// @return On success <b>true</b>.
+///     On failure <b>false</b>, and contents of <b>info</b> are undefined.
+///
+MP4V2_EXPORT
+bool fileFetchSummaryInfo( MP4FileHandle file, FileSummaryInfo& info );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+#endif // MP4V2_UTIL_OTHER_H
diff --git a/libutil/util.h b/libutil/util.h
new file mode 100644
index 0000000..2ea7246
--- /dev/null
+++ b/libutil/util.h
@@ -0,0 +1,34 @@
+#ifndef MP4V2_UTIL_UTIL_H
+#define MP4V2_UTIL_UTIL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/src.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// @namespace mp4v2::util (private) Command-line utility support.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+///
+/// This namespace is used for command-line utilities. Some symbols from this
+/// namespace are exported from libmp4v2 in order to support new functionality
+/// which may or may not make it into some form of public API, at which time
+/// it will be moved out of this namespace.
+///
+namespace mp4v2 { namespace util {
+    using namespace std;
+    using namespace mp4v2::impl;
+}} // namespace mp4v2::util
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "Database.h"
+#include "Timecode.h"
+#include "TrackModifier.h"
+#include "Utility.h"
+#include "crc.h"
+#include "other.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_UTIL_UTIL_H
diff --git a/maintainer/HOWTO-ADD_SOURCE.txt b/maintainer/HOWTO-ADD_SOURCE.txt
new file mode 100644
index 0000000..1e4e7e4
--- /dev/null
+++ b/maintainer/HOWTO-ADD_SOURCE.txt
@@ -0,0 +1,26 @@
+Maintainer Add Source Files
+***************************
+
+Almost any new .cpp or .h files will have to be added to GNUmakefile.am .
+It is trivial to add hand-written source files. Just add them to the list
+for the particular library or program which already exists. For example,
+to add a source file to libmp4v2:
+
+    libmp4v2_la_SOURCES = ...
+
+In the rare situation that you desire to add a header file to be installed
+during 'make install' and used by the public API (such as mp4v2.h) then it
+should be taken out of libmp4v2_la_SOURCES and added to:
+
+    mp4v2inc_HEADERS = ...
+
+Once done, autotool files such as configure will need to be regenerated.
+This is usually done automatically for you by the generated makefile,
+but the following command is always an excellent way to make sure
+everything is regenerated properly:
+
+    autoreconf -fiv
+
+Don't be alarmed at the number of files created by autoreconf. These
+files will be part of any dist-bundle created but do not add them to the
+repository -- they are dist-only files.
diff --git a/maintainer/HOWTO-AUTOTOOLS.txt b/maintainer/HOWTO-AUTOTOOLS.txt
new file mode 100644
index 0000000..d4f0e90
--- /dev/null
+++ b/maintainer/HOWTO-AUTOTOOLS.txt
@@ -0,0 +1,35 @@
+Maintainer Autotools Instructions
+*********************************
+
+This project uses autotools and does not add/checkin files generated by
+autoreconf to the repository. These files are to be included only in
+dist-bundles.
+
+There is a clear distinction between building from the repository or
+building from a dist-bundle. This document is for project maintainers
+building from the repository.
+
+The maintainer is responsible for generating files associated with
+autotools. These files in turn are distributed with dist-bundles, but
+never should be added/checked-in to the repository.
+
+Whenever any information for which autotools may depend on is changed,
+then you must issue the following command which will force generation
+of all files and install support scripts required at configure-time:
+
+    autoreconf -fiv
+
+Where is Makefile.am? And what is this GNUmakefile stuff?
+We already have a sophisticated project which requires enough GNU-tools
+at build-time that it makes no sense to hope-and-pray that BSD flavors
+of make will work. As such, the files are renamed to be those which are
+only recognized by GNU make. This helps avoid confusion when people run
+the wrong make flavor -- it's better to see Makefile not found, rather
+than 100 obscure make incompatibility-errors. So we have GNUmakefile.am
+which is just like the old Makefile.am only better.
+
+Post configure, we end up with GNUmakefile in your build directory.
+
+Care has been taken to avoid subdir makefiles. And while they may
+eventually become part of this project, it's nice to avoid them when
+when possible.
diff --git a/maintainer/HOWTO-RELEASE.txt b/maintainer/HOWTO-RELEASE.txt
new file mode 100644
index 0000000..3945412
--- /dev/null
+++ b/maintainer/HOWTO-RELEASE.txt
@@ -0,0 +1,134 @@
+Maintainer Release Instructions
+*******************************
+
+Official releases are made by creating a dist-bundle as supported by
+the autotools framework. This is not a normal target for users building
+the library to use, and as such it may introduce extra tool requirements.
+For example, 'makeinfo' is required to generate some documentation before
+a bundle can be created.
+
+
+Official Releases
+=================
+
+Releases use a version tuple of pattern MAJOR.MINOR.POINT and changing
+the version is accomplished by editing the corresponding values in:
+
+    project/project.m4sugar
+
+And all releases follow this repository URL convention:
+
+    https://mp4v2.googlecode.com/svn/releases/MAJOR.MINOR.POINT
+
+Thus, releases will produce dist bundles of this pattern:
+
+    mp4v2-MAJOR.MINOR.POINT.tar.bz2
+
+MAJOR-RELEASE. This kind of release has a major impact to the public
+interface and/or other aspects of the project. You must increment
+MAJOR and reset MINOR and reset POINT. Typically used when API
+components are modified or removed.
+
+MINOR-RELEASE. This kind of release has some minor but significant
+changes to the public interface. You must increment MINOR and
+reset POINT. Typically used when API components are added.
+
+POINT-RELEASE. This kind of release is usually for bugfix/maintenance
+purposes and does not adversely effect the public interface. You
+must increment POINT.
+
+A release is branched from either the trunk or an earlier release.
+If branched from the trunk, it will naturally be the "chase-version"
+which the trunk has been developing. For example, on a new project
+trunk might work towards a 1.0.0 release. When that project is
+deemed ready, trunk is branched to 1.0.0. Any final changes are
+then applied on the new branch, while trunk continues development,
+chasing it's next release, which might be 1.1.0.
+
+
+WORKFLOW - MINOR-RELEASE
+------------------------
+
+    # branch release from trunk (head)
+    svn cp -m "BRANCHED: RELEASE 1.0.0" \
+        https://mp4v2.googlecode.com/svn/trunk \
+        https://mp4v2.googlecode.com/svn/releases/1.0.0
+
+    # cd to your favorite work area for mp4v2
+    cd /work/mp4v2/
+    
+    # checkout release, local workdir 1.0.0 default is good
+    svn co https://mp4v2.googlecode.com/svn/releases/1.0.0
+    cd 1.0.0/
+
+    # if needed, edit version, checkin, update, autoreconf.
+    vi project/project.m4sugar
+    svn ci
+    svn update
+    autoreconf -fiv
+
+    # clean-room discipline
+    #   at this point there should be no pending changes in working dir
+    #   if there are, you're probably just working out kinks in the
+    #   autotool system but in the final run, all files should be
+    #   checked in at this point.
+    #
+    # make distcheck || make dist (the latter if you're in a hurry)
+    rm -fr build/
+    mkdir build/
+    cd build/
+    ../configure
+    make distcheck
+
+    # upload bundle somewhere
+    mkdir -p ~/OUTGOING
+    cp mp4v2-1.0.0.tar.bz2 ~/OUTGOING/.
+
+    # FINISHED - CLOSED: https://mp4v2.googlecode.com/svn/releases/1.0.0
+    # Once the bundle is published, no more changes allowed!
+
+    # TRUNK - now go back to trunk, edit version, checkin, update, autoreconf.
+    # note that we bump trunk automatically to the next anticpated
+    # major-release or minor-release, as point-releases do not belong here.
+    cd /work/mp4v2/
+    svn co https://mp4v2.googlecode.com/svn/trunk/
+    cd trunk/
+    vi project/project.m4sugar # bump version to 1.1.0
+    svn ci -m "BUMP: TRUNK 1.1.0"
+    svn update
+    autoreconf -fiv
+
+
+WORKFLOW - POINT-RELEASE
+------------------------
+
+If a point release such as 1.0.1 needs to be done, then it is important
+to branch from 1.0.0 (not trunk!). Here is an example 1.0.1 point-release.
+
+    svn cp -m "BRANCHED: RELEASE 1.0.1" \
+        https://mp4v2.googlecode.com/svn/releases/1.0.0 \
+        https://mp4v2.googlecode.com/svn/releases/1.0.1
+    svn co https://mp4v2.googlecode.com/svn/releases/1.0.1
+    cd 1.0.1/
+    vi project/project.m4sugar # bump version to 1.0.1
+    svn ci
+    svn update
+    autoreconf -fiv
+    .
+    .
+    .
+    # *** APPLY CODE CHANGES ***
+    # *** TEST ***
+    .
+    .
+    .
+    svn ci
+    svn update
+    autoreconf -fiv
+    rm -fr build/
+    mkdir build/
+    cd build/
+    ../configure
+    make distcheck
+    cp mp4v2-1.0.1.tar.bz2 ~/OUTGOING/.
+    # FINISHED - CLOSED: https://mp4v2.googlecode.com/svn/releases/1.0.1
diff --git a/maintainer/HOWTO-TEST.txt b/maintainer/HOWTO-TEST.txt
new file mode 100644
index 0000000..363a2ef
--- /dev/null
+++ b/maintainer/HOWTO-TEST.txt
@@ -0,0 +1,63 @@
+Maintainer Automated Testing Instructions
+*****************************************
+
+Automated testing is available for the project. It can be used to write tests
+for batched and interactive command-line utilities. DejaGNU is the framework
+used. Testing is not a mandatory requirement when building, however it is
+strongly encouraged.
+
+Tests may be run after building the project with the following command:
+
+    make check
+
+And the quick-and-dirty method to see if all tests passed is to look at the
+last line of output which indicates the number of expected passes and
+failures. Any 'unexpected' results are the ones to be concerned about.
+Assuming there are 50 tests for a project, the ideal output might be:
+
+    === <TESTSUITE_NAME> Summary ===
+    # of expected passes    24
+
+Test output is available in testlog/ with *.log and *.sum for log and
+summary files, respectively.
+
+
+Requirements
+------------
+
+The testsuite is fairly new so the requirements may or may not be strict.
+Listed here are the versions used to by maintainers; be aware that
+this testing framework is generally a unix solution only.
+
+    expect 5.43.0
+    Tcl 8.5
+    DejaGNU 1.4.4
+
+
+DejaGNU Framework
+-----------------
+
+The framework is relatively thin; basically a wrapper around things which
+can be made to work relatively easily with Autotools. The underlying
+tests are actually written using expect/tcl. However, since this project
+does not have any interactive command-line tools, the interactive nature
+of expect is not leveraged. Thus, most tests have so far been written
+using more or less Tcl with the DejaGNU framework.
+
+
+File Locations
+--------------
+
+All tests are stored in testsuite/ and generally any C/C++ code require
+for testing should reside therein. However, sometimes a test utility proves
+to be quite useful on its own and may be moved outside of testsuite/ .
+
+In the future we can for example create tests which might test certain
+aspects of the API; here are some useful examples:
+
+    - assert public C API header compiles
+    - assert private symbols are not available to public compile/linking
+    - assert contents of MP4Dump output are sane
+    - etc...
+
+Hopefully as new tests are added they will serve as good examples.
diff --git a/maintainer/INTERNALS.txt b/maintainer/INTERNALS.txt
new file mode 100644
index 0000000..5d34de7
--- /dev/null
+++ b/maintainer/INTERNALS.txt
@@ -0,0 +1,223 @@
+January 7, 2002
+
+MP4V2 LIBRARY INTERNALS
+=======================
+
+This document provides an overview of the internals of the mp4v2 library 
+to aid those who wish to modify and extend it. Before reading this document,
+I recommend familiarizing yourself with the MP4 (or Quicktime) file format 
+standard and the mp4v2 library API. The API is described in a set of man pages
+in mpeg4ip/doc/mp4v2, or if you prefer by looking at mp4.h.
+
+All the library code is written in C++, however the library API follows uses
+C calling conventions hence is linkable by both C and C++ programs. The
+library has been compiled and used on Linux, BSD, Windows, and Mac OS X.
+Other than libc, the library has no external dependencies, and hence can
+be used independently of the mpeg4ip package if desired.  The library is 
+used for both real-time recording and playback in mpeg4ip, and its runtime 
+performance is up to those tasks. On the IA32 architecture compiled with gcc,
+the stripped library is approximately 600 KB code and initialized data.
+
+It is useful to think of the mp4v2 library as consisting of four layers:
+infrastructure, file format, generic tracks, and type specific track helpers.
+A description of each layer follows, from the fundamental to the optional.
+
+
+Infrastructure
+==============
+
+The infrastructure layer provides basic file I/O, memory allocation, 
+error handling, string utilities, and protected arrays. The source files 
+for this layer are mp4file_io, mp4util, and mp4array. 
+
+Note that the array classes uses preprocessor macros instead of C++ 
+templates. The rationale for this is to increase portability given the 
+sometimes incomplete support by some compilers for templates.
+
+
+File Format
+===========
+
+The file format layer provides the translation from the on-disk MP4 file 
+format to in-memory C++ structures and back to disk. It is intended 
+to exactly match the MP4 specification in syntax and semantics. It 
+represents the majority of the code.
+
+There are three key structures at the file format layer: atoms, properties,
+and descriptors. 
+
+Atoms are the primary containers within an mp4 file. They can contain 
+any combination of properties, other atoms, or descriptors.
+
+The mp4atom files contain the base class for all the atoms, and provide 
+generic functions that cover most cases. Most atoms are covered in
+atom_standard.cpp.  Atoms that have a special read, generation or
+write needs are contained in their subclass contained in file atom_<name>.cpp,
+ where <name> is the four letter name of the atom defined in the MP4 
+specification. 
+
+Atoms that only specifies the properties of the atom or the possible child 
+atoms in the case of a container atom are located in atom_standard.cpp.
+
+In more specialized cases the atom specific file provides routines to 
+initialize, read, or write the atom.
+
+Properties are the atomic pieces of information. The basic types of 
+properties are integers, floats, strings, and byte arrays. For integers 
+and floats there are subclasses that represent the different storage sizes,
+e.g. 8, 16, 24, 32, and 64 bit integers. For strings, there is 1 property 
+class with a number of options regarding exact storage details, e.g. null 
+terminated, fixed length, counted. 
+
+For implementation reasons, there are also two special properties, table 
+and descriptor, that are actually containers for groups of properties. 
+I.e by making these containers provide a property interface much code can 
+be written in a generic fashion.
+
+The mp4property files contain all the property related classes. 
+
+Descriptors are containers that derive from the MPEG conventions and use 
+different encoding rules than the atoms derived from the QuickTime file
+format. This means more use of bitfields and conditional existence with 
+an emphasis on bit efficiency at the cost of encoding/decoding complexity.
+Descriptors can contain other descriptors and/or properties.
+
+The mp4descriptor files contain the generic base class for descriptors. 
+Also the mp4property files have a descriptor wrapper class that allows a 
+descriptor to behave as if it were a property. The specific descriptors 
+are implemented as subclasses of the base class descriptor in manner similar 
+to that of atoms. The descriptors, ocidescriptors, and qosqualifiers files 
+contain these implementations.
+
+Each atom/property/descriptor has a name closely related to that in the 
+MP4 specification. The difference being that the mp4v2 library doesn't 
+use '-' or '_' in property names and capitalizes the first letter of each 
+word, e.g. "thisIsAPropertyName". A complete name specifies the complete 
+container path.  The names follow the C/C++ syntax for elements and array 
+indices. 
+
+Examples are:
+	"moov.mvhd.duration"
+	"moov.trak[2].tkhd.duration"
+	"moov.trak[3].minf.mdia.stbl.stsz[101].sampleSize"
+
+Note "*" can be used as a wildcard for an atom name (only). This is most 
+useful when dealing with the stsd atom which contains child atoms with 
+various names, but shared property names.
+
+Note that internally when performance matters the code looks up a property
+by name once, and then stores the returned pointer to the property class.
+
+To add an atom, first you should see if an existing atom exists that
+can be used.  If not, you need to decide if special read/write or
+generate properties need to be established; for example a property in the atom
+changes other properties (adds, or subtracts).  If there are no
+special cases, add the atom properties to atom_standard.cpp.  If there
+are special properties, add a new file, add a new class to atoms.h, and
+add the class to  MP4Atom::CreateAtom in mp4atom.cpp.
+
+
+
+Generic Tracks
+==============
+
+The two entities at this level are the mp4 file as a whole and the tracks 
+which are contained with it. The mp4file and mp4track files contain the 
+implementation.
+
+The critical work done by this layer is to map the collection of atoms,
+properties, and descriptors that represent a media track into a useful,
+and consistent set of operations. For example, reading or writing a media 
+sample of a track is a relatively simple operation from the library API
+perspective. However there are numerous pieces of information in the mp4
+file that need to be properly used and updated to do this. This layer
+handles all those details.
+
+Given familiarity with the mp4 spec, the code should be straight-forward.
+What may not be immediately obvious are the functions to handle chunks of
+media samples. These exist to allow optimization of the mp4 file layout by
+reordering the chunks on disk to interleave the media sample chunks of
+multiple tracks in time order. (See MP4Optimize API doc).
+
+
+Type Specific Track Helpers 
+===========================
+
+This specialized code goes beyond the meta-information about tracks in
+the mp4 file to understanding and manipulating the information in the
+track samples. There are currently two helpers in the library: 
+the MPEG-4 Systems Helper, and the RTP Hint Track Helper.
+ 
+The MPEG-4 Systems Helper is currently limited to creating the OD, BIFS,
+and SDP information about a minimal audio/video scene consistent with
+the Internet Streaming Media Alliance (ISMA) specifications. We will be
+evaluating how best to generalize the library's helper functions for
+MPEG-4 Systems without overburdening the implementation. The code for 
+this helper is found in the isma and odcommands files.
+
+The RTP Hint Track Helper is more extensive in its support. The hint 
+tracks contain the track packetization information needed to build 
+RTP packets for streaming. The library can construct RTP packets based 
+on the hint track making RTP based servers significantly easier to write.
+
+All code related to rtp hint tracks is in the rtphint files. It would also
+be useful to look at test/mp4broadcaster and mpeg4ip/server/mp4creator for
+examples of how this part of the library API can be used.
+
+
+Library API
+===========
+
+The library API is defined and implemented in the mp4 files. The API uses
+C linkage conventions, and the mp4.h file adapts itself according to whether
+C or C++ is the compilation mode.
+
+All API calls are implemented in mp4.cpp and basically pass thru's to the
+MP4File member functions. This ensures that the library has internal access
+to the same functions as available via the API. All the calls in mp4.cpp use
+C++ try/catch blocks to protect against any runtime errors in the library.
+Upon error the library will print a diagnostic message if the verbostiy level
+has MP4_DETAILS_ERROR set, and return a distinguished error value, typically
+0 or -1.
+
+The test and util subdirectories contain useful examples of how to
+use the library. Also the mp4creator and mp4live programs within
+mpeg4ip demonstrate more complete usage of the library API.
+
+
+Debugging
+=========
+
+Since mp4 files are fairly complicated, extensive debugging support is
+built into the library. Multi-level diagnostic messages are available 
+under the control of a verbosity bitmask described in the API.
+
+Also the library provides the MP4Dump() call which provides an ASCII
+version of the mp4 file meta-information. The mp4dump utilitity is a
+wrapper executable around this function.
+
+The mp4extract program is also provided in the utilities directory
+which is useful for extracting a track from an mp4file and putting the
+media data back into it's own file. It can also extract each sample of
+a track into its own file it that is desired.
+
+When all else fails, mp4 files are amenable to debugging by direct
+examination. Since the atom names are four letter ASCII codes finding
+reference points in a hex dump is feasible. On UNIX, the od command
+is your friend: "od -t x1z -A x [-j 0xXXXXXX] foo.mp4" will print
+a hex and ASCII dump, with hex addresses, starting optionally from
+a specified offset. The library diagnostic messages can provide
+information on where the library is reading or writing.
+
+
+General caveats
+===============
+	
+The coding convention is to use the C++ throw operator whenever an 
+unrecoverable error occurs. This throw is caught at the API layer 
+in mp4.cpp and translated into an error value. 
+
+Be careful about indices. Internally, we follow the C/C++ convention 
+to use zero-based indices. However the MP4 spec uses one-based indices 
+for things like samples and hence the library API uses this convention.
+
diff --git a/maintainer/LIBPLATFORM.txt b/maintainer/LIBPLATFORM.txt
new file mode 100644
index 0000000..0e67ca9
--- /dev/null
+++ b/maintainer/LIBPLATFORM.txt
@@ -0,0 +1,35 @@
+libplatform Notes
+*****************
+
+libplatform is a private library used by this project for portability
+across platforms. Simple file access, getting system time, parsing
+command-line arguments, endian byte-swap macros, are examples of
+things that belong in this convenience library.
+
+The implementation means to support the following platforms:
+
+    - posix (the default)
+    - win32 (Windows 2000 or newer)
+
+
+All symbols in libplatform reside in private namespace mp4v2::platform
+and must never be made visible through public interfaces. In general,
+implementation .h and .cpp files are free to include this library.
+
+Both libmp4v2 and various executables created by this project rely on
+libplatform and this creates a bit of a dilemma in that libmp4v2 must
+ship as a stand-alone library. Thus, when creating libmp4v2 we must always
+embed the needed bits from libplatform. In turn, the executables then
+link against both libmp4v2 and the remaining bits of libplatform.
+
+This approach requires advanced linking which is available using
+several different methods, varying by platforms. And to complicate
+matters further, libtool (even version 2.2.6) makes this impossible.
+So the simple solution is to add libplatform symbols to libmp4v2.
+The only problem with this is libmp4v2 becomes a little fatter than
+it needs to be, but since all the symbols are in a private namespace
+and we use set default symbol visibility to hidden, we protect our
+clients from inadvertantly becoming dependents.
+
+If libplatform ever becomes bulky we can revisit this issue, but at
+this time using advanced linking would only save ~10KB of binary size.
diff --git a/maintainer/NAMESPACES.txt b/maintainer/NAMESPACES.txt
new file mode 100644
index 0000000..b7185ab
--- /dev/null
+++ b/maintainer/NAMESPACES.txt
@@ -0,0 +1,42 @@
+Maintainer C++ Namespace Notes
+******************************
+
+Namespaces have been employed to help organize source and push all private
+symbols (those which are not part of the public C API) to a location
+that is unlikely to be accidentally used by anyone. It also virtually
+eliminates any chance that private impl symbols will cause symbol collisions
+at compile or link-time.
+
+The global namespace is used by C API which generally prefixes all
+of its symobls with MP4 to avoid collisions. This space is used by
+src/mp4.cpp and include/mp4v2/mp4v2.h . A few exceptions may exist where
+additional API is exported from some differently-implemented code.
+
+The rest of the codebase is tucked into various child-namespaces of
+the project's top-level 'mp4v2' namespace. The top-level namespace
+is reserved for this project, and unless someone branches our codebase
+and ignores their moral responsibility to rename their root namespace,
+we should neatly avoid symbol collisions. Code external to the project
+should never use any symbols which reside in mp4v2.
+
+The namespace 'mp4v2::platform' is private and used for code which abstracts
+various plaform differences or implements missing features.
+
+The namespace 'mp4v2::impl' is a private namespace for the main
+implementation of libmp4v2. This namespace generally depends on
+mp4v2::platform .
+
+The namespace 'mp4v2::util' is a private namespace which adds
+functionality for use with libutil and util/ command-line tools. The
+positioning is a little more complicated in that it has symbols which
+are exported from libmp4v2, which needs full access to mp4v2::impl
+hidden symbols. Then libutil itself adds to the same namespace, but
+because libutil does not require direct access to mp4v2::impl hidden
+symbols it is a separate static library. And finally, the util/ tools
+themselves use the namespace for implementation convenience.
+
+Nested (child) namespaces of those listed above are used for further
+organization when applicable.
+
+Further documentation is available in the API Reference;
+see Namespaces section.
diff --git a/maintainer/SOURCE.txt b/maintainer/SOURCE.txt
new file mode 100644
index 0000000..ea0f737
--- /dev/null
+++ b/maintainer/SOURCE.txt
@@ -0,0 +1,98 @@
+Maintainer C++ Formatting Suggestions
+*************************************
+
+Some consistency and readability to C++ source code in the MP4v2 project
+is desired. Every coder has different styles, uses different tools, but
+we need to find some common ground when possible. So please consider this
+document when creating and editing code.
+
+When in doubt, general suggestions are as follows:
+
+    - format for good readability.
+    - format in a similar fashion as the file you are editing.
+    - format code generally no wider than 119 columns.
+    - format code with 4-space indents (no hard-tabs!)
+    - the wider an identifier's scope is, the more descriptive it's name
+      should be (and generally longer). Short identifiers for local scopes
+      and loops are perfectly acceptable.
+    - make liberal use of TODO comments as necessary. Following the
+      pattern of marking *who* placed the TODO with a hyphen followed
+      by your initials or shortname. ie: TODO-XY; note this does not
+      imply the person who marked takes responsibility for the task.
+    - move pointer 'astericks' and reference 'ampersands' towards type
+
+
+EXAMPLE IF/ELSE
+---------------
+
+if( true )
+    doit();
+
+if( (a == 1) && (b == 2) ) {
+    doit();
+    doit();
+}
+else if( true && /* really long multi-line stuff
+    another long line
+    another line */ )
+{
+    doit();
+}
+
+
+EXAMPLE FUNCTION DELCARATIONS
+-----------------------------
+
+void processFoo( );
+bool processFoo( const char* name = 0 );
+bool processFoo( const string& name );
+
+
+EXAMPLE FUNCTION DEFINITIONS
+-----------------------------
+
+void
+processFoo()
+{
+    doit();
+}
+
+bool
+processFoo( const char* name = 0 )
+{
+    doit();
+    return thing != true; // no need for parenthesis
+}
+
+bool
+processFoo( const string& name )
+{
+    doit();
+    return (thing != true) || (another == false); // parens help readability
+}
+
+
+EXAMPLE CLASS DECL
+------------------
+
+class Foo
+{
+public:
+    Foo( int value );
+    virtual ~Foo();
+
+    int  get( );
+    void set( int value );
+
+protected:
+    virtual void doit() = 0;
+
+private:
+    int _value;
+
+public:
+    const int defaultValue;
+
+public:
+    static void foo();
+};
diff --git a/project/htmlcombine.py b/project/htmlcombine.py
new file mode 100755
index 0000000..a883f69
--- /dev/null
+++ b/project/htmlcombine.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python3
+#
+
+import re
+import sys
+
+from optparse import OptionParser
+
+###############################################################################
+
+parser = OptionParser( 'Usage: %prog [OPTIONS]' )
+parser.add_option( '--header', action='store', default=None, help='header file' )
+parser.add_option( '--footer', action='store', default=None, help='footer file' )
+parser.add_option( '--body', action='store', default=None, help='file to extract contents of body from' )
+parser.add_option( '-v', '--verbose', action='count', default=False, help='increase verbosity' )
+
+(options, args) = parser.parse_args()
+
+if( len(args) != 0 ):
+    parser.error( 'incorrect number of arguments' )
+
+if not options.header:
+    parser.error( '--header must be specified' )
+if not options.footer:
+    parser.error( '--footer must be specified' )
+if not options.body:
+    parser.error( '--body must be specified' )
+
+###############################################################################
+
+title = 'Unknown'
+
+# parse majorheading
+with open( options.body, 'r' ) as f:
+    rx = re.compile( '^.*class="majorheading".*>([^>]+)</.+>' )
+    for line in f:
+        m = rx.match( line )
+        if not m:
+            continue;
+        title = m.group( 1 )
+        break
+
+m = re.compile( '(\S+)\s+(\S+)\s+(.+)' ).match( title )
+if m:
+    shortTitle = m.group(3)
+else:
+    shortTitle = title
+
+menu = ''
+if not shortTitle == 'Documentation':
+    menu += '<li><a href="index.html">Documentation</a></li>\n'
+    menu += '<li class="active">%s</li>' % (shortTitle)
+else:
+    menu += '<li class="active">Documentation</li>'
+
+with open( options.header, 'r' ) as f:
+    rxTitle      = re.compile( '__TITLE__' )
+    rxShortTitle = re.compile( '__SHORT_TITLE__' )
+    rxMenu       = re.compile( '__MENU__' )
+    for line in f:
+        line = re.sub( rxTitle, title, line )
+        line = re.sub( rxShortTitle, shortTitle, line )
+        line = re.sub( rxMenu, menu, line )
+        sys.stdout.write( line )
+
+# out everything *after* first <body> and *before* </body>
+with open( options.body, 'r' ) as f:
+    rxBegin    = re.compile( '<body>' )
+    rxEnd      = re.compile( '</body>' )
+    inside  = False
+    lstrip  = False
+    for line in f:
+        if rxBegin.match( line ):
+            inside = True
+            continue
+        if rxEnd.match( line ):
+            outside = False
+            continue
+
+        if lstrip:
+            line = line.lstrip()
+        if inside:
+            sys.stdout.write( line )
+
+with open( options.footer, 'r' ) as f:
+    for line in f:
+        sys.stdout.write( line )
diff --git a/project/project.m4.in b/project/project.m4.in
new file mode 100644
index 0000000..dc96a41
--- /dev/null
+++ b/project/project.m4.in
@@ -0,0 +1,25 @@
+define(<<__PROJECT_name>>,            <<@PROJECT_name@>>)dnl
+define(<<__PROJECT_name_lower>>,      <<@PROJECT_name_lower@>>)dnl
+define(<<__PROJECT_name_upper>>,      <<@PROJECT_name_upper@>>)dnl
+define(<<__PROJECT_name_formal>>,     <<@PROJECT_name_formal@>>)dnl
+define(<<__PROJECT_url_website>>,     <<@PROJECT_url_website@>>)dnl
+define(<<__PROJECT_url_downloads>>,   <<@PROJECT_url_downloads@>>)dnl
+define(<<__PROJECT_url_discussion>>,  <<@PROJECT_url_discussion@>>)dnl
+define(<<__PROJECT_irc>>,             <<@PROJECT_irc@>>)dnl
+define(<<__PROJECT_bugreport>>,       <<@PROJECT_bugreport@>>)dnl
+define(<<__PROJECT_version>>,         <<@PROJECT_version@>>)dnl
+define(<<__PROJECT_version_hex>>,     <<@PROJECT_version_hex@>>)dnl
+define(<<__PROJECT_version_major>>,   <<@PROJECT_version_major@>>)dnl
+define(<<__PROJECT_version_minor>>,   <<@PROJECT_version_minor@>>)dnl
+define(<<__PROJECT_version_point>>,   <<@PROJECT_version_point@>>)dnl
+define(<<__PROJECT_repo_url>>,        <<@PROJECT_repo_url@>>)dnl
+define(<<__PROJECT_repo_branch>>,     <<@PROJECT_repo_branch@>>)dnl
+define(<<__PROJECT_repo_root>>,       <<@PROJECT_repo_root@>>)dnl
+define(<<__PROJECT_repo_uuid>>,       <<@PROJECT_repo_uuid@>>)dnl
+define(<<__PROJECT_repo_rev>>,        <<@PROJECT_repo_rev@>>)dnl
+define(<<__PROJECT_repo_date>>,       <<@PROJECT_repo_date@>>)dnl
+define(<<__PROJECT_repo_type>>,       <<@PROJECT_repo_type@>>)dnl
+define(<<__PROJECT_build>>,           <<@PROJECT_build@>>)dnl
+dnl
+define(<<__MAKE_ABS_TOP_BUILDDIR>>,  <<@abs_top_builddir@>>)dnl
+define(<<__MAKE_ABS_TOP_SRCDIR>>,    <<@abs_top_srcdir@>>)dnl
diff --git a/project/project.m4sugar b/project/project.m4sugar
new file mode 100644
index 0000000..6e91f04
--- /dev/null
+++ b/project/project.m4sugar
@@ -0,0 +1,15 @@
+dnl
+dnl This file defines highest-level project meta-data in autoconf M4 format.
+dnl It is needed downstream by configure.ac.
+dnl
+define([PRJ_name],           [MP4v2])
+define([PRJ_name_lower],     [mp4v2])
+define([PRJ_name_upper],     [MP4V2])
+define([PRJ_url_website],    [http://code.google.com/p/mp4v2])
+define([PRJ_url_downloads],  [http://code.google.com/p/mp4v2/downloads/list])
+define([PRJ_url_discussion], [http://groups.google.com/group/mp4v2])
+define([PRJ_irc],            [irc://irc.freenode.net/handbrake])
+define([PRJ_bugreport],      [kidjan@gmail.com])
+define([PRJ_version_major],  [2])
+define([PRJ_version_minor],  [1])
+define([PRJ_version_point],  [0])
diff --git a/project/xml2wiki.py b/project/xml2wiki.py
new file mode 100755
index 0000000..c788df4
--- /dev/null
+++ b/project/xml2wiki.py
@@ -0,0 +1,381 @@
+#!/usr/bin/env python3
+#
+
+import io
+import re
+import sys
+import time
+import xml.parsers.expat
+
+from optparse import OptionParser
+
+###############################################################################
+
+writer = None
+
+###############################################################################
+
+class StreamEntry:
+    def __init__( self, object, isElement ):
+        self.object    = object
+        self.isElement = isElement
+    
+###############################################################################
+
+class Element:
+    def __init__( self, parent, text=True, strip=True, delimBegin=None, delimEnd=None, newline=0 ):
+        self._parent     = parent
+        self._text       = text
+        self._strip      = strip
+        self._delimBegin = delimBegin
+        self._delimEnd   = delimEnd
+        self._newline    = newline
+        self._stream     = []
+
+    def _addElement( self, child ):
+        self._stream.append( StreamEntry( child, True ))
+
+    def _addText( self, text ):
+        if self._text:
+            self._stream.append( StreamEntry( text, False ))
+
+    def _write( self, file ):
+        if self._delimBegin:
+            file.write( self._delimBegin )
+        for entry in self._stream:
+            if entry.isElement:
+                entry.object.write( file )
+            else:
+                file.write( str(entry.object) )
+        if self._delimEnd:
+            file.write( self._delimEnd )
+
+    def write( self, file ):
+        if self._newline > writer.newlineCount:
+            file.write( '\n' * (self._newline - writer.newlineCount))
+        self._write( file )
+
+###############################################################################
+
+class Document( Element ):
+    def __init__( self ):
+        Element.__init__( self, None )
+        self._stack        = [ self ]
+        self._pending      = self
+        self._summary      = None
+        self._debugIndent  = ''
+        self._chapterLevel = 0
+        self._sectionLevel = 0
+        self._dividerCount = 0
+
+        self._pragmaSummary = PragmaElement( self, 'summary' )
+        self._pragmaLabels = PragmaElement( self, 'labels' )
+        self._pragmaLabels._addText( 'xml2wiki,Distribution,Featured' )
+
+    def _pop( self ):
+        self._stack.pop()
+        self._pending = self._stack[-1]
+        return self._pending
+
+    def _pushChild( self, child, add=True ):
+        if add:
+            self._pending._addElement( child );
+        self._stack.append( child )
+        self._pending = child
+        return self._pending
+
+    def _chapterBegin( self ):
+        self._chapterLevel = self._chapterLevel + 1
+
+    def _chapterEnd( self ):
+        self._chapterLevel = self._chapterLevel - 1
+
+    def _sectionBegin( self ):
+        self._sectionLevel = self._sectionLevel + 1
+
+    def _sectionEnd( self ):
+        self._sectionLevel = self._sectionLevel - 1
+
+    def _write( self, file ):
+        self._pragmaSummary.write( file )
+        file.write( '\n' )
+        self._pragmaLabels.write( file )
+        if options.date:
+            file.write( "\n\n  ===== `[`generated by xml2wiki on %s`]` =====" % (time.strftime( '%c' ) ))
+        if options.toc:
+            file.write( '\n\n<wiki:toc max_depth="3" />' )
+        Element._write( self, file )
+        file.write( '\n' )
+
+    def handleElementBegin( self, name, attrs ):
+        self._debugIndent = '    ' * (len(self._stack) - 1)
+        if options.verbose:
+            print( '%sBEGIN %s %s' % (self._debugIndent, name, attrs))
+
+        e = None
+        shouldAdd = True
+
+        if name == 'b':
+            # we prefix with italiac delims in case this is on indented line
+            # which gets confifused by google's wiki to mean bullet item
+            e = Element( self._pending, delimBegin='__*', delimEnd='*' )
+        elif name == 'chapter':
+            self._chapterBegin()
+        elif name == 'code':
+            e = Element( self._pending, delimBegin='`', delimEnd='`' )
+        elif name == 'command':
+            e = Element( self._pending, delimBegin='`', delimEnd='`' )
+        elif name == 'enumerate':
+            e = EnumerateElement( self._pending )
+        elif name == 'example':
+            e = CodeElement( self._pending )
+        elif name == 'file':
+            e = Element( self._pending, delimBegin='`', delimEnd='`' )
+        elif name == 'i':
+            e = Element( self._pending, delimBegin='_', delimEnd='_' )
+        elif name == 'itemize':
+            e = ItemizeElement( self._pending )
+        elif name == 'item':
+            e = ItemElement( self._pending )
+        elif name == 'majorheading':
+            e = self._pragmaSummary
+            shouldAdd = False
+        elif name == 'para':
+            e = ParagraphElement( self._pending )
+        elif name == 'quotation':
+            e = IndentedElement( self._pending )
+        elif name == 'samp':
+            e = Element( self._pending, delimBegin='`', delimEnd='`' )
+        elif name == 'section' or name == 'subsection':
+            self._sectionBegin()
+        elif name == 'table':
+            e = Element( self._pending, newline=1, delimBegin='<table border="1" cellpadding="4">', delimEnd='</table>', strip=True )
+        elif name == 'tableitem':
+            e = TableItemElement( self._pending )
+        elif name == 'tableterm':
+            e = Element( self._pending, delimBegin='<td width="15%">', delimEnd='</td>' )
+        elif name == 'title':
+            e = HeadingElement( self._pending, self._chapterLevel + self._sectionLevel )
+        elif name == 'unnumbered' or name == 'unnumberedsec':
+            self._chapterBegin()
+        elif name == 'uref':
+            e = UrefInline( self._pending )
+        elif name == 'urefdesc':
+            e = UrefDescInline( self._pending )
+        elif name == 'urefurl':
+            e = UrefUrlInline( self._pending )
+        elif name == 'xref':
+            e = XrefInline( self._pending )
+        elif name == 'xrefnodename':
+            e = XrefNodenameInline( self._pending )
+
+        if not e:
+            self._pushChild( UnknownElement( self._pending ) )
+            if options.verbose > 2:
+                print( 'UNKNOWN:', name )
+        else:
+            self._pushChild( e, add=shouldAdd )
+
+    def handleElementEnd( self, name ):
+        if name == 'chapter':
+            self._chapterEnd()
+        elif name == 'section' or name == 'subsection':
+            self._sectionEnd()
+        elif name == 'unnumbered' or name == 'unnumberedsec':
+            self._sectionEnd()
+
+        self._pop()
+        self._debugIndent = '    ' * (len(self._stack) - 1)
+        if options.verbose:
+            print( '%sEND %s' % (self._debugIndent, name))
+
+    def handleCharacterData( self, data ):
+        if options.verbose > 1:
+            print( '%s[%s]' % (self._debugIndent, data.strip()))
+        self._pending._addText( data )
+
+###############################################################################
+
+class UnknownElement( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, text=False )
+
+###############################################################################
+
+class PragmaElement( Element ):
+    def __init__( self, parent, keyword ):
+        Element.__init__( self, parent, delimBegin=('#' + keyword + ' ') )
+
+###############################################################################
+
+class BlockElement( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, newline=2, text=False )
+
+###############################################################################
+
+class CodeElement( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, newline=2, delimBegin='{{{\n', delimEnd='\n}}}\n' )
+
+###############################################################################
+
+class HeadingElement( Element ):
+    def __init__( self, parent, level ):
+        Element.__init__( self, parent, newline=2 )
+        self._delimBegin = ('=' * level) + ' '
+        self._delimEnd   = ' ' + ('=' * level) + '\n'
+
+        # insert divider for level 1 headers
+        if level == 1:
+            if options.toc or doc._dividerCount:
+                self._delimBegin = '----\n%s' % (self._delimBegin)
+            doc._dividerCount = doc._dividerCount + 1
+
+###############################################################################
+
+class IndentedElement( BlockElement ):
+    def _write( self, file ):
+        writer.increase()
+        Element._write( self, file )
+        writer.decrease()
+
+###############################################################################
+
+class EnumerateElement( IndentedElement ):
+    pass
+
+###############################################################################
+
+class ItemizeElement( IndentedElement ):
+    pass
+
+###############################################################################
+
+class ItemElement( BlockElement ):
+    def __init__( self, parent ):
+        BlockElement.__init__( self, parent )
+        self._newline = 1
+        if isinstance( parent, TableItemElement ):
+            self._newline    = 0
+            self._delimBegin = '<td>'
+            self._delimEnd   = '</td>'
+
+###############################################################################
+
+class ParagraphElement( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, newline=2 )
+        if isinstance( parent, ItemElement ):
+            if isinstance( parent._parent, TableItemElement ):
+                self._newline = 0
+            elif isinstance( parent._parent, EnumerateElement ):
+                self._newline    = 1
+                self._delimBegin = '# '
+            else:
+                self._newline    = 1
+                self._delimBegin = '* '
+
+###############################################################################
+
+class TableItemElement( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, newline=1, text=False )
+        self._delimBegin = '<tr>'
+        self._delimEnd   = '</tr>'
+
+###############################################################################
+
+class UrefInline( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, text=False, delimBegin='[', delimEnd=']' )
+
+###############################################################################
+
+class UrefDescInline( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, delimBegin=' ' )
+
+###############################################################################
+
+class UrefUrlInline( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent )
+
+###############################################################################
+
+class XrefInline( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent, text=False )
+
+###############################################################################
+
+class XrefNodenameInline( Element ):
+    def __init__( self, parent ):
+        Element.__init__( self, parent )
+
+    def _write( self, file ):
+        buffer = io.StringIO()
+        Element._write( self, buffer )
+        name = str( buffer.getvalue() )
+        anchor = re.sub( ' ', '_', name )
+        file.write( '[#%s %s]' % (anchor, name) )
+
+###############################################################################
+
+class IndentedWriter:
+    def __init__( self, size, file ):
+        self._chunk   = ' ' * size
+        self._file    = file
+        self._level   = 0
+        self._indent  = ''
+        self._pending = False
+
+        self.newlineCount = 0
+
+    def decrease( self ):
+        self._level  = self._level - 1
+        self._indent = self._chunk * self._level
+
+    def increase( self ):
+        self._level  = self._level + 1
+        self._indent = self._chunk * self._level
+
+    def write( self, data ):
+        for b in data:
+            if self._pending:
+                self._pending = False
+                self._file.write( self._indent )
+            if b == '\n':
+                self.newlineCount = self.newlineCount + 1
+                self._pending = True
+            else:
+                self.newlineCount = 0
+            self._file.write( b )
+
+###############################################################################
+
+parser = OptionParser( 'Usage: %prog [OPTIONS] xml' )
+parser.add_option( '-d', '--date', action='store_true', default=False, help='generate date-stamp under title' )
+parser.add_option( '-t', '--toc', action='store_true', default=False, help='generate table of contents' )
+parser.add_option( '-v', '--verbose', action='count', default=False, help='increase verbosity' )
+
+(options, args) = parser.parse_args()
+
+if( len(args) != 1 ):
+    parser.error( 'incorrect number of arguments' )
+
+###############################################################################
+
+doc = Document()
+xml = xml.parsers.expat.ParserCreate()
+
+xml.StartElementHandler  = doc.handleElementBegin
+xml.EndElementHandler    = doc.handleElementEnd
+xml.CharacterDataHandler = doc.handleCharacterData
+
+with open( args[0], 'rb' ) as fin:
+    xml.ParseFile( fin )
+
+writer = IndentedWriter( 4, sys.stdout )
+doc.write( writer )
diff --git a/src/3gp.cpp b/src/3gp.cpp
new file mode 100644
index 0000000..f6ec12e
--- /dev/null
+++ b/src/3gp.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.  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.
+ *
+ * Contributor(s):
+ *              Ximpo Group Ltd.          mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define _3GP_MAJOR_BRAND "3gp5"
+#define _3GP_MINOR_VERSION 0x0001
+
+void MP4File::Make3GPCompliant(const char* fileName,  char* majorBrand, uint32_t minorVersion, char** supportedBrands, uint32_t supportedBrandsCount, bool deleteIodsAtom)
+{
+    char brand[5] = "3gp5";
+    char* _3gpSupportedBrands[1] = { (char*)&brand };
+
+    if (majorBrand) {
+        if (!supportedBrands || !supportedBrandsCount) {
+            throw new Exception("Invalid parameters",  __FILE__, __LINE__, __FUNCTION__);
+        }
+    }
+
+    MakeFtypAtom(
+        majorBrand ? majorBrand : (char*)brand,
+        majorBrand ? minorVersion  : _3GP_MINOR_VERSION,
+        majorBrand ? supportedBrands : (char**)_3gpSupportedBrands,
+        majorBrand ? supportedBrandsCount : 1);
+
+    if (deleteIodsAtom) {
+        // Delete the iods atom, if it exists....
+        MP4Atom* iodsAtom = m_pRootAtom->FindAtom("moov.iods");
+        if (iodsAtom) {
+            MP4Atom* moovAtom = m_pRootAtom->FindAtom("moov");
+            ASSERT(moovAtom);
+
+            moovAtom->DeleteChildAtom(iodsAtom);
+        }
+    }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_ac3.cpp b/src/atom_ac3.cpp
new file mode 100644
index 0000000..300f4c1
--- /dev/null
+++ b/src/atom_ac3.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * See ETSI TS 102 366 V1.2.1 Annex F for how to put Ac3 in MP4.
+ * 
+ * Contributor(s):
+ *      Edward Groenendaal      egroenen@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Ac3Atom::MP4Ac3Atom(MP4File &file)
+        : MP4Atom(file, "ac-3")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this,"dataReferenceIndex"));
+
+    AddReserved(*this,"reserved2", 8); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this,"channelCount"));
+
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this,"sampleSize"));
+
+    AddReserved(*this,"reserved3", 4); /* 5 */
+
+    AddProperty( /* 6 */
+        new MP4Integer16Property(*this,"samplingRate"));
+
+    AddReserved(*this,"reserved4", 2); /* 7 */
+
+    ExpectChildAtom("dac3", Required, OnlyOne);
+}
+
+void MP4Ac3Atom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1); // data-reference-index
+    ((MP4Integer16Property*)m_pProperties[3])->SetValue(2); // channelCount - ignored
+    ((MP4Integer16Property*)m_pProperties[4])->SetValue(0x0010); // sampleSize - ignored
+
+    // The user should set the samplingRate as appropriate - and create the dac3 atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_amr.cpp b/src/atom_amr.cpp
new file mode 100644
index 0000000..b2a21b3
--- /dev/null
+++ b/src/atom_amr.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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.  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.
+ *
+ * Contributor(s):
+ *              Ximpo Group Ltd.          mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4AmrAtom::MP4AmrAtom(MP4File &file, const char *type)
+        : MP4Atom(file, type)
+{
+    AddReserved(*this,"reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this,"dataReferenceIndex"));
+
+    AddReserved(*this,"reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this,"timeScale"));
+
+    AddReserved(*this,"reserved3", 2); /* 4 */
+
+    ExpectChildAtom("damr", Required, OnlyOne);
+}
+
+void MP4AmrAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved2 has non-zero fixed values
+    static uint8_t reserved2[16] = {
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x00, 0x10,
+        0x00, 0x00, 0x00, 0x00,
+    };
+    m_pProperties[2]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[2])->
+    SetValue(reserved2, sizeof(reserved2));
+    m_pProperties[2]->SetReadOnly(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_avc1.cpp b/src/atom_avc1.cpp
new file mode 100644
index 0000000..3599f3e
--- /dev/null
+++ b/src/atom_avc1.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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. 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Bill May wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Avc1Atom::MP4Avc1Atom(MP4File &file)
+        : MP4Atom(file, "avc1")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddReserved(*this, "reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "width"));
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this, "height"));
+
+    AddReserved(*this, "reserved3", 14); /* 5 */
+
+    MP4StringProperty* pProp =
+        new MP4StringProperty(*this, "compressorName");
+    pProp->SetFixedLength(32);
+    pProp->SetCountedFormat(true);
+    pProp->SetValue("JVT/AVC Coding");
+    AddProperty(pProp); /* 6 */
+
+    AddReserved(*this, "reserved4", 4); /* 7 */
+
+    ExpectChildAtom("avcC", Required, OnlyOne);
+    ExpectChildAtom("btrt", Optional, OnlyOne);
+    ExpectChildAtom("colr", Optional, OnlyOne);
+    ExpectChildAtom("pasp", Optional, OnlyOne);
+    // for now ExpectChildAtom("m4ds", Optional, OnlyOne);
+}
+
+void MP4Avc1Atom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved3 has non-zero fixed values
+    static uint8_t reserved3[14] = {
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01,
+    };
+    m_pProperties[5]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[5])->
+    SetValue(reserved3, sizeof(reserved3));
+    m_pProperties[5]->SetReadOnly(true);
+
+    // property reserved4 has non-zero fixed values
+    static uint8_t reserved4[4] = {
+        0x00, 0x18, 0xFF, 0xFF,
+    };
+    m_pProperties[7]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[7])->
+    SetValue(reserved4, sizeof(reserved4));
+    m_pProperties[7]->SetReadOnly(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_avcC.cpp b/src/atom_avcC.cpp
new file mode 100644
index 0000000..ac477a2
--- /dev/null
+++ b/src/atom_avcC.cpp
@@ -0,0 +1,273 @@
+/*
+ * 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. 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Bill May wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * SizeTableProperty is a special version of the MP4TableProperty -
+ * the BytesProperty will need to set the value before it can read
+ * from the file
+ */
+class SizeTableProperty : public MP4TableProperty
+{
+public:
+    SizeTableProperty(MP4Atom& parentAtom, const char *name, MP4IntegerProperty *pCountProperty) :
+            MP4TableProperty(parentAtom, name, pCountProperty) {};
+protected:
+    void ReadEntry(MP4File& file, uint32_t index) {
+        // Each table has a size, followed by the length field
+        // first, read the length
+        m_pProperties[0]->Read(file, index);
+        MP4IntegerProperty *pIntProp = (MP4IntegerProperty *)m_pProperties[0];
+        // set the size in the bytes property
+        MP4BytesProperty *pBytesProp = (MP4BytesProperty *)m_pProperties[1];
+        pBytesProp->SetValueSize(pIntProp->GetValue(index), index);
+        // And read the bytes
+        m_pProperties[1]->Read(file, index);
+    };
+private:
+    SizeTableProperty();
+    SizeTableProperty ( const SizeTableProperty &src );
+    SizeTableProperty &operator= ( const SizeTableProperty &src );
+};
+
+MP4AvcCAtom::MP4AvcCAtom(MP4File &file)
+        : MP4Atom(file, "avcC")
+{
+    MP4BitfieldProperty *pCount;
+    MP4TableProperty *pTable;
+
+    AddProperty( new MP4Integer8Property(*this,"configurationVersion")); /* 0 */
+
+    AddProperty( new MP4Integer8Property(*this,"AVCProfileIndication")); /* 1 */
+
+    AddProperty( new MP4Integer8Property(*this,"profile_compatibility")); /* 2 */
+
+    AddProperty( new MP4Integer8Property(*this,"AVCLevelIndication")); /* 3 */
+
+    AddProperty( new MP4BitfieldProperty(*this,"reserved", 6)); /* 4 */
+    AddProperty( new MP4BitfieldProperty(*this,"lengthSizeMinusOne", 2)); /* 5 */
+    AddProperty( new MP4BitfieldProperty(*this,"reserved1", 3)); /* 6 */
+    pCount = new MP4BitfieldProperty(*this,"numOfSequenceParameterSets", 5);
+    AddProperty(pCount); /* 7 */
+
+    pTable = new SizeTableProperty(*this,"sequenceEntries", pCount);
+    AddProperty(pTable); /* 8 */
+    pTable->AddProperty(new MP4Integer16Property(pTable->GetParentAtom(),"sequenceParameterSetLength"));
+    pTable->AddProperty(new MP4BytesProperty(pTable->GetParentAtom(),"sequenceParameterSetNALUnit"));
+
+    MP4Integer8Property *pCount2 = new MP4Integer8Property(*this,"numOfPictureParameterSets");
+    AddProperty(pCount2); /* 9 */
+
+    pTable = new SizeTableProperty(*this,"pictureEntries", pCount2);
+    AddProperty(pTable); /* 10 */
+    pTable->AddProperty(new MP4Integer16Property(pTable->GetParentAtom(),"pictureParameterSetLength"));
+    pTable->AddProperty(new MP4BytesProperty(pTable->GetParentAtom(),"pictureParameterSetNALUnit"));
+}
+
+void MP4AvcCAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer8Property*)m_pProperties[0])->SetValue(1);
+
+    m_pProperties[4]->SetReadOnly(false);
+    ((MP4BitfieldProperty*)m_pProperties[4])->SetValue(0x3f);
+    m_pProperties[4]->SetReadOnly(true);
+
+    m_pProperties[6]->SetReadOnly(false);
+    ((MP4BitfieldProperty*)m_pProperties[6])->SetValue(0x7);
+    m_pProperties[6]->SetReadOnly(true);
+#if 0
+    // property reserved4 has non-zero fixed values
+    static uint8_t reserved4[4] = {
+        0x00, 0x18, 0xFF, 0xFF,
+    };
+    m_pProperties[7]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[7])->
+    SetValue(reserved4, sizeof(reserved4));
+    m_pProperties[7]->SetReadOnly(true);
+#endif
+}
+
+//
+// Clone - clone my properties to destination atom
+//
+// this method simplifies duplicating avcC atom properties from
+// source to destination file using a single API rather than
+// having to copy each property.  This API encapsulates the object
+// so the application layer need not concern with each property
+// thereby isolating any future changes to atom properties.
+//
+// ----------------------------------------
+// property   description
+// ----------------------------------------
+//
+// 0    configurationVersion
+// 1    AVCProfileIndication
+// 2    profile_compatibility
+// 3    AVCLevelIndication
+// 4    reserved
+// 5    lengthSizeMinusOne
+// 6    reserved
+// 7    number of SPS
+// 8    SPS entries
+// 9    number of PPS
+// 10   PPS entries
+//
+//
+void MP4AvcCAtom::Clone(MP4AvcCAtom *dstAtom)
+{
+
+    MP4Property *dstProperty;
+    MP4TableProperty *pTable;
+    uint16_t i16;
+    uint64_t i32;
+    uint64_t i64;
+    uint8_t *tmp;
+
+    // source pointer Property I16
+    MP4Integer16Property *spPI16;
+    // source pointer Property Bytes
+    MP4BytesProperty *spPB;
+
+    // dest pointer Property I16
+    MP4Integer16Property *dpPI16;
+    // dest pointer Property Bytes
+    MP4BytesProperty *dpPB;
+
+
+    // start with defaults and reserved fields
+    dstAtom->Generate();
+
+    // 0, 4, 6 are now generated from defaults
+    // leaving 1, 2, 3, 5, 7, 8, 9, 10 to export
+
+    dstProperty=dstAtom->GetProperty(1);
+    ((MP4Integer8Property *)dstProperty)->SetValue(
+        ((MP4Integer8Property *)m_pProperties[1])->GetValue());
+
+    dstProperty=dstAtom->GetProperty(2);
+    ((MP4Integer8Property *)dstProperty)->SetValue(
+        ((MP4Integer8Property *)m_pProperties[2])->GetValue());
+
+    dstProperty=dstAtom->GetProperty(3);
+    ((MP4Integer8Property *)dstProperty)->SetValue(
+        ((MP4Integer8Property *)m_pProperties[3])->GetValue());
+
+    dstProperty=dstAtom->GetProperty(5);
+    ((MP4BitfieldProperty *)dstProperty)->SetValue(
+        ((MP4BitfieldProperty *)m_pProperties[5])->GetValue());
+
+    //
+    // 7 and 8 are related SPS (one set of sequence parameters)
+    //
+    // first the count bitfield
+    //
+    dstProperty=dstAtom->GetProperty(7);
+    dstProperty->SetReadOnly(false);
+    ((MP4BitfieldProperty *)dstProperty)->SetValue(
+        ((MP4BitfieldProperty *)m_pProperties[7])->GetValue());
+    dstProperty->SetReadOnly(true);
+
+    // next export SPS Length and NAL bytes */
+
+    // first source pointers
+    pTable = (MP4TableProperty *) m_pProperties[8];
+    spPI16 = (MP4Integer16Property *)pTable->GetProperty(0);
+    spPB = (MP4BytesProperty *)pTable->GetProperty(1);
+
+    // now dest pointers
+    dstProperty=dstAtom->GetProperty(8);
+    pTable = (MP4TableProperty *) dstProperty;
+    dpPI16 = (MP4Integer16Property *)pTable->GetProperty(0);
+    dpPB = (MP4BytesProperty *)pTable->GetProperty(1);
+
+    // sps length
+    i16 = spPI16->GetValue();
+    i64 = i16;
+    // FIXME - this leaves m_maxNumElements =2
+    // but src atom m_maxNumElements is 1
+    dpPI16->InsertValue(i64, 0);
+
+    // export byte array
+    i32 = i16;
+    // copy bytes to local buffer
+    tmp = (uint8_t *)MP4Malloc(i32);
+    ASSERT(tmp != NULL);
+    spPB->CopyValue(tmp, 0);
+    // set element count
+    dpPB->SetCount(1);
+    // copy bytes
+    dpPB->SetValue(tmp, i32, 0);
+    MP4Free((void *)tmp);
+
+    //
+    // 9 and 10 are related PPS (one set of picture parameters)
+    //
+    // first the integer8 count
+    //
+    dstProperty=dstAtom->GetProperty(9);
+    dstProperty->SetReadOnly(false);
+    ((MP4Integer8Property *)dstProperty)->SetValue(
+        ((MP4Integer8Property *)m_pProperties[9])->GetValue());
+    dstProperty->SetReadOnly(true);
+
+    // next export PPS Length and NAL bytes */
+
+    // first source pointers
+    pTable = (MP4TableProperty *) m_pProperties[10];
+    spPI16 = (MP4Integer16Property *)pTable->GetProperty(0);
+    spPB = (MP4BytesProperty *)pTable->GetProperty(1);
+
+    // now dest pointers
+    dstProperty=dstAtom->GetProperty(10);
+    pTable = (MP4TableProperty *) dstProperty;
+    dpPI16 = (MP4Integer16Property *)pTable->GetProperty(0);
+    dpPB = (MP4BytesProperty *)pTable->GetProperty(1);
+
+    // pps length
+    i16 = spPI16->GetValue();
+    i64 = i16;
+    dpPI16->InsertValue(i64, 0);
+
+    // export byte array
+    i32 = i16;
+    // copy bytes to local buffer
+    tmp = (uint8_t *)MP4Malloc(i32);
+    ASSERT(tmp != NULL);
+    spPB->CopyValue(tmp, 0);
+    // set element count
+    dpPB->SetCount(1);
+    // copy bytes
+    dpPB->SetValue(tmp, i32, 0);
+    MP4Free((void *)tmp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_chpl.cpp b/src/atom_chpl.cpp
new file mode 100644
index 0000000..d4eea2b
--- /dev/null
+++ b/src/atom_chpl.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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.  All Rights Reserved.
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// MP4ChplAtom is for Nero chapter list atom which is a child of udta
+MP4ChplAtom::MP4ChplAtom (MP4File &file)
+        : MP4Atom(file, "chpl")
+{
+    // it is not completely clear if version, flags, reserved and chaptercount
+    // have the right sizes but
+    // one thing is clear: chaptercount is not only 8-bit it is at least 16-bit
+
+    // add the version
+    AddVersionAndFlags();
+
+    // add reserved bytes
+    AddReserved(*this,"reserved", 1);
+
+    // define the chaptercount
+    MP4Integer32Property * counter = new MP4Integer32Property(*this,"chaptercount");
+    AddProperty(counter);
+
+    // define the chapterlist
+    MP4TableProperty * list = new MP4TableProperty(*this,"chapters", counter);
+
+    // the start time as 100 nanoseconds units
+    list->AddProperty(new MP4Integer64Property(*this,"starttime"));
+
+    // the chapter name as UTF-8
+    list->AddProperty(new MP4StringProperty(*this,"title", true));
+
+    // add the chapterslist
+    AddProperty(list);
+}
+
+void MP4ChplAtom::Generate ()
+{
+    SetVersion(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_colr.cpp b/src/atom_colr.cpp
new file mode 100644
index 0000000..965173e
--- /dev/null
+++ b/src/atom_colr.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ * Contributer has declined to give copyright information, and gives
+ * it freely to the world.
+ *
+ * Contributor(s):
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ColrAtom::MP4ColrAtom(MP4File &file)
+        : MP4Atom(file, "colr")
+{
+    MP4StringProperty* cpt = new MP4StringProperty(*this,"colorParameterType");
+    cpt->SetFixedLength(4);
+    AddProperty(cpt); /* 0 */
+
+    AddProperty( /* 1 */ new MP4Integer16Property(*this,"primariesIndex"));
+
+    AddProperty( /* 2 */ new MP4Integer16Property(*this,"transferFunctionIndex"));
+
+    AddProperty( /* 3 */ new MP4Integer16Property(*this,"matrixIndex"));
+}
+
+void MP4ColrAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4StringProperty*)m_pProperties[0])->SetValue("nclc");
+    // default to ITU BT.709 values
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(1);
+    ((MP4Integer16Property*)m_pProperties[3])->SetValue(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_d263.cpp b/src/atom_d263.cpp
new file mode 100644
index 0000000..66e78ed
--- /dev/null
+++ b/src/atom_d263.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.  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.
+ *
+ * Contributor(s):
+ *              Ximpo Group Ltd.          mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define H263_VENDOR 0x6d346970
+
+MP4D263Atom::MP4D263Atom(MP4File &file)
+        : MP4Atom(file, "d263")
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(*this, "vendor"));
+
+    AddProperty( /* 1 */
+        new MP4Integer8Property(*this, "decoderVersion"));
+
+    AddProperty( /* 2 */
+        new MP4Integer8Property(*this, "h263Level"));
+
+    AddProperty( /* 3 */
+        new MP4Integer8Property(*this, "h263Profile"));
+
+    ExpectChildAtom("bitr", Optional, OnlyOne);
+
+}
+
+void MP4D263Atom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer32Property*)m_pProperties[0])->SetValue(H263_VENDOR);
+    ((MP4Integer8Property*)m_pProperties[1])->SetValue(1);
+
+}
+
+void MP4D263Atom::Write()
+{
+    // Check whether we have valid values in the bitr atom
+    // (if it exists, of course)
+    MP4Atom* bitrAtom = FindAtom("d263.bitr");
+    if (bitrAtom) {
+        uint32_t avgBitrate;
+        uint32_t maxBitrate;
+
+        MP4Integer32Property* pProp;
+        bitrAtom->FindProperty("bitr.avgBitrate",
+                               (MP4Property**)&pProp,
+                               NULL);
+        ASSERT(pProp);
+        avgBitrate = pProp->GetValue();
+
+        bitrAtom->FindProperty("bitr.maxBitrate",
+                               (MP4Property**)&pProp,
+                               NULL);
+        ASSERT(pProp);
+        maxBitrate = pProp->GetValue();
+
+        if (!maxBitrate && !avgBitrate) {
+            DeleteChildAtom(bitrAtom);
+        }
+    }
+
+    MP4Atom::Write();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_dac3.cpp b/src/atom_dac3.cpp
new file mode 100644
index 0000000..32f7e62
--- /dev/null
+++ b/src/atom_dac3.cpp
@@ -0,0 +1,245 @@
+/*
+ * 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.  All Rights Reserved.
+ * 
+ * See ETSI TS 102 366 V1.2.1 Annex F for how to put Ac3 in MP4.
+ *
+ * Contributor(s):
+ *      Edward Groenendaal      egroenen@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+MP4DAc3Atom::MP4DAc3Atom(MP4File &file)
+        : MP4Atom(file, "dac3")
+{
+    AddProperty( new MP4BitfieldProperty(*this, "fscod", 2)); /* 0 */
+    AddProperty( new MP4BitfieldProperty(*this, "bsid", 5)); /* 1 */
+    AddProperty( new MP4BitfieldProperty(*this, "bsmod", 3)); /* 2 */
+    AddProperty( new MP4BitfieldProperty(*this, "acmod", 3)); /* 3 */
+    AddProperty( new MP4BitfieldProperty(*this, "lfeon", 1)); /* 4 */
+    AddProperty( new MP4BitfieldProperty(*this, "bit_rate_code", 5)); /* 5 */
+    AddProperty( new MP4BitfieldProperty(*this, "reserved", 5)); /* 6 */
+    m_pProperties[6]->SetReadOnly(true);
+}
+
+void MP4DAc3Atom::Generate()
+{
+    MP4Atom::Generate();
+ 
+
+}
+
+/*
+ * Normalling the Dump of the atom looks like the following, we want to spruce
+ * this up a bit to give more information.
+ *
+ *      type = dac3
+ *       fscod = 0 (0x0) <2 bits>
+ *       bsid = 8 (0x08) <5 bits>
+ *       bsmod = 0 (0x0) <3 bits>
+ *       acmod = 7 (0x7) <3 bits>
+ *       lfeon = 1 (0x1) <1 bits>
+ *       bit_rate_code = 15 (0x0f) <5 bits>
+ *       reserved = 0 (0x00) <5 bits>
+ *
+ * into:
+ *      type = dac3
+ *       fscod = 0 (0x0) <2 bits> [48 kHz]
+ *       bsid = 8 (0x08) <5 bits>
+ *       bsmod = 0 (0x0) <3 bits> [Main audio service: complete main (CM)]
+ *       acmod = 7 (0x7) <3 bits> [3/2 (L, C, R, SL, SR)]
+ *       lfeon = 1 (0x1) <1 bits> [ENABLED]
+ *       bit_rate_code = 15 (0x0f) <5 bits> [448 kbit/s]
+ *       reserved = 0 (0x00) <5 bits>
+ *
+ */
+void MP4DAc3Atom::Dump(uint8_t indent, bool dumpImplicits)
+{
+  
+    MP4BitfieldProperty* fscodProp = ((MP4BitfieldProperty*)m_pProperties[0]);
+    MP4BitfieldProperty* bsidProp = ((MP4BitfieldProperty*)m_pProperties[1]);
+    MP4BitfieldProperty* bsmodProp = ((MP4BitfieldProperty*)m_pProperties[2]);
+    MP4BitfieldProperty* acmodProp = ((MP4BitfieldProperty*)m_pProperties[3]);
+    MP4BitfieldProperty* lfeonProp = ((MP4BitfieldProperty*)m_pProperties[4]);
+    MP4BitfieldProperty* brcProp = ((MP4BitfieldProperty*)m_pProperties[5]);
+    MP4BitfieldProperty* resProp = ((MP4BitfieldProperty*)m_pProperties[6]);
+    
+    log.dump(indent++, MP4_LOG_VERBOSE2, "\"%s\": type = dac3",
+             GetFile().GetFilename().c_str() );
+
+    if (fscodProp) { 
+        uint64_t fscod = 0xFF;
+        const char* fscodString; 
+        const char* fscods[] = {
+            "48", "44.1", "32", "Reserved",
+        };
+
+        fscod = fscodProp->GetValue();
+
+        if (fscod < (sizeof(fscods) / sizeof(fscods[0]))) {
+            fscodString = fscods[fscod];
+        } else {
+            fscodString = "Invalid value";
+        }
+
+        uint8_t hexWidth = fscodProp->GetNumBits() / 4;
+        if (hexWidth == 0 || (fscodProp->GetNumBits() % 4)) {
+            hexWidth++;
+        }
+
+        log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": fscod = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s kHz]",
+                 GetFile().GetFilename().c_str(),
+                 fscod, (int)hexWidth, fscod, fscodProp->GetNumBits(), fscodString);
+    }
+    if (bsidProp)  bsidProp->Dump(indent, dumpImplicits);
+
+    if (bsmodProp) { 
+        uint64_t bsmod = 0xFF;
+        const char* bsmodString; 
+        const char* bsmods[] = {
+            "Main audio service: complete main (CM)",
+            "Main audio srrvice: music and effects (ME)",
+            "Associated service: visually impaired (VI)",
+            "Associated service: hearing impaired (HI)",
+            "Associated service: dialogue (D)",
+            "Associated service: commentary (C)",
+            "Associated service: emergency (E)",
+            "Associated service: voice over (VO) or Main audio service: karaoke",
+        };
+
+        bsmod = bsmodProp->GetValue();
+
+        if (bsmod < (sizeof(bsmods) / sizeof(bsmods[0]))) {
+            bsmodString = bsmods[bsmod];
+        } else {
+            bsmodString = "Invalid value";
+        }
+
+        uint8_t hexWidth = bsmodProp->GetNumBits() / 4;
+        if (hexWidth == 0 || (bsmodProp->GetNumBits() % 4)) {
+            hexWidth++;
+        }
+
+        log.dump(indent, MP4_LOG_VERBOSE2,
+                "\"%s\": bsmod = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s]",
+                 GetFile().GetFilename().c_str(),
+                 bsmod, (int)hexWidth, bsmod, bsmodProp->GetNumBits(), bsmodString);
+    }
+    
+    if (acmodProp) { 
+        uint64_t acmod = 0xFF;
+        const char* acmodString; 
+
+        const char* acmods[] = {
+            "1 + 1 (Ch1, Ch2)",
+            "1/0 (C)",
+            "2/0 (L, R)",
+            "3/0 (L, C, R)",
+            "2/1 (L, R, S)",
+            "3/1 (L, C, R, S)",
+            "2/2 (L, R, SL, SR)",
+            "3/2 (L, C, R, SL, SR)",
+        };
+
+        acmod = acmodProp->GetValue();
+
+        if (acmod < (sizeof(acmods) / sizeof(acmods[0]))) {
+            acmodString = acmods[acmod];
+        } else {
+            acmodString = "Invalid value";
+        }
+
+        uint8_t hexWidth = acmodProp->GetNumBits() / 4;
+        if (hexWidth == 0 || (acmodProp->GetNumBits() % 4)) {
+            hexWidth++;
+        }
+
+        log.dump(indent, MP4_LOG_VERBOSE2,
+                 "\"%s\": acmod = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s]",
+                 GetFile().GetFilename().c_str(),
+                 acmod, (int)hexWidth, acmod, acmodProp->GetNumBits(), acmodString);
+    }
+
+    if (lfeonProp) {
+        uint64_t lfeon = lfeonProp->GetValue();
+        uint8_t hexWidth = lfeonProp->GetNumBits() / 4;
+        
+        if (hexWidth == 0 || (lfeonProp->GetNumBits() % 4)) {
+            hexWidth++;
+        }
+        
+        log.dump(indent, MP4_LOG_VERBOSE2,
+                "\"%s\": lfeon = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%s]",
+                 GetFile().GetFilename().c_str(), lfeon, (int)hexWidth, lfeon, 
+                 lfeonProp->GetNumBits(), lfeon ? "ENABLED" : "DISABLED"); 
+    }
+    
+    if (brcProp) {
+        uint32_t bit_rate_codes[] = {
+            32,
+            40, 
+            48, 
+            56, 
+            64,
+            80,
+            96, 
+            112, 
+            128, 
+            160, 
+            192, 
+            224, 
+            256, 
+            320, 
+            384, 
+            448, 
+            512, 
+            576, 
+            640,
+        };
+        uint64_t bit_rate_code = brcProp->GetValue();
+        uint32_t bit_rate;
+
+        if (bit_rate_code < (sizeof(bit_rate_codes) / sizeof(bit_rate_codes[0]))) {
+            bit_rate = bit_rate_codes[bit_rate_code];
+        } else {
+            bit_rate = 0;
+        }
+
+        uint8_t hexWidth = brcProp->GetNumBits() / 4;
+        if (hexWidth == 0 || (brcProp->GetNumBits() % 4)) {
+            hexWidth++;
+        }
+        
+        log.dump(indent, MP4_LOG_VERBOSE2,
+                 "\"%s\": bit_rate_code = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits> [%" PRIu32 " kbit/s]",
+                 GetFile().GetFilename().c_str(),
+                 bit_rate_code, (int)hexWidth, bit_rate_code, 
+                 brcProp->GetNumBits(), bit_rate); 
+    }
+    if (resProp) resProp->Dump(indent, dumpImplicits);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_damr.cpp b/src/atom_damr.cpp
new file mode 100644
index 0000000..1617782
--- /dev/null
+++ b/src/atom_damr.cpp
@@ -0,0 +1,69 @@
+/*
+ * 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.  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.
+ *
+ * Contributor(s):
+ *              Ximpo Group Ltd.          mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define AMR_VENDOR 0x6d346970
+
+MP4DamrAtom::MP4DamrAtom(MP4File &file)
+        : MP4Atom(file, "damr")
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(*this, "vendor"));
+
+    AddProperty( /* 1 */
+        new MP4Integer8Property(*this, "decoderVersion"));
+
+    AddProperty( /* 2 */
+        new MP4Integer16Property(*this, "modeSet"));
+
+    AddProperty( /* 3 */
+        new MP4Integer8Property(*this, "modeChangePeriod"));
+
+    AddProperty( /* 4 */
+        new MP4Integer8Property(*this, "framesPerSample"));
+
+}
+
+void MP4DamrAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer32Property*)m_pProperties[0])->SetValue(AMR_VENDOR);
+    ((MP4Integer8Property*)m_pProperties[1])->SetValue(1);
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_dref.cpp b/src/atom_dref.cpp
new file mode 100644
index 0000000..b174c7a
--- /dev/null
+++ b/src/atom_dref.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4DrefAtom::MP4DrefAtom(MP4File &file)
+        : MP4Atom(file, "dref")
+{
+    AddVersionAndFlags();
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "entryCount");
+    pCount->SetReadOnly();
+    AddProperty(pCount);
+
+    ExpectChildAtom("url ", Optional, Many);
+    ExpectChildAtom("urn ", Optional, Many);
+    ExpectChildAtom("alis", Optional, Many);
+}
+
+void MP4DrefAtom::Read()
+{
+    /* do the usual read */
+    MP4Atom::Read();
+
+    // check that number of children == entryCount
+    MP4Integer32Property* pCount =
+        (MP4Integer32Property*)m_pProperties[2];
+
+    if (m_pChildAtoms.Size() != pCount->GetValue()) {
+        log.warningf("%s: \"%s\": dref inconsistency with number of entries",
+                     __FUNCTION__, GetFile().GetFilename().c_str() );
+
+        /* fix it */
+        pCount->SetReadOnly(false);
+        pCount->SetValue(m_pChildAtoms.Size());
+        pCount->SetReadOnly(true);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_elst.cpp b/src/atom_elst.cpp
new file mode 100644
index 0000000..cadc107
--- /dev/null
+++ b/src/atom_elst.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ElstAtom::MP4ElstAtom(MP4File &file)
+        : MP4Atom(file, "elst")
+{
+    AddVersionAndFlags();
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "entryCount");
+    AddProperty(pCount);
+
+    MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+    AddProperty(pTable);
+}
+
+void MP4ElstAtom::AddProperties(uint8_t version)
+{
+    MP4TableProperty* pTable = (MP4TableProperty*)m_pProperties[3];
+
+    if (version == 1) {
+        pTable->AddProperty(
+            new MP4Integer64Property(pTable->GetParentAtom(), "segmentDuration"));
+        pTable->AddProperty(
+            new MP4Integer64Property(pTable->GetParentAtom(), "mediaTime"));
+    } else {
+        pTable->AddProperty(
+            new MP4Integer32Property(pTable->GetParentAtom(), "segmentDuration"));
+        pTable->AddProperty(
+            new MP4Integer32Property(pTable->GetParentAtom(), "mediaTime"));
+    }
+
+    pTable->AddProperty(
+        new MP4Integer16Property(pTable->GetParentAtom(), "mediaRate"));
+    pTable->AddProperty(
+        new MP4Integer16Property(pTable->GetParentAtom(), "reserved"));
+}
+
+void MP4ElstAtom::Generate()
+{
+    SetVersion(0);
+    AddProperties(GetVersion());
+
+    MP4Atom::Generate();
+}
+
+void MP4ElstAtom::Read()
+{
+    /* read atom version */
+    ReadProperties(0, 1);
+
+    /* need to create the properties based on the atom version */
+    AddProperties(GetVersion());
+
+    /* now we can read the remaining properties */
+    ReadProperties(1);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_enca.cpp b/src/atom_enca.cpp
new file mode 100644
index 0000000..700fa57
--- /dev/null
+++ b/src/atom_enca.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie         dmackie@cisco.com
+ *      Alix Marchandise-Franquet   alix@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4EncaAtom::MP4EncaAtom(MP4File &file)
+        : MP4Atom(file, "enca")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddReserved(*this, "reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "timeScale"));
+
+    AddReserved(*this, "reserved3", 2); /* 4 */
+
+    ExpectChildAtom("esds", Required, OnlyOne);
+    ExpectChildAtom("sinf", Required, OnlyOne);
+}
+
+void MP4EncaAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved2 has non-zero fixed values
+    static uint8_t reserved2[16] = {
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x02, 0x00, 0x10,
+        0x00, 0x00, 0x00, 0x00,
+    };
+    m_pProperties[2]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[2])->
+    SetValue(reserved2, sizeof(reserved2));
+    m_pProperties[2]->SetReadOnly(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_encv.cpp b/src/atom_encv.cpp
new file mode 100644
index 0000000..ce97ece
--- /dev/null
+++ b/src/atom_encv.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie         dmackie@cisco.com
+ *      Alix Marchandise-Franquet   alix@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4EncvAtom::MP4EncvAtom(MP4File &file)
+        : MP4Atom(file, "encv")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddReserved(*this, "reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "width"));
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this, "height"));
+
+    AddReserved(*this, "reserved3", 14); /* 5 */
+
+    MP4StringProperty* pProp =
+        new MP4StringProperty(*this, "compressorName");
+    pProp->SetFixedLength(32);
+    pProp->SetCountedFormat(true);
+    pProp->SetValue("");
+    AddProperty(pProp); /* 6 */
+    AddReserved(*this, "reserved4", 4); /* 7 */
+
+    ExpectChildAtom("esds", Required, OnlyOne);
+    ExpectChildAtom("sinf", Required, OnlyOne);
+    ExpectChildAtom("avcC", Optional, OnlyOne);
+}
+
+void MP4EncvAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved3 has non-zero fixed values
+    static uint8_t reserved3[14] = {
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01,
+    };
+    m_pProperties[5]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[5])->
+    SetValue(reserved3, sizeof(reserved3));
+    m_pProperties[5]->SetReadOnly(true);
+
+    // property reserved4 has non-zero fixed values
+    static uint8_t reserved4[4] = {
+        0x00, 0x18, 0xFF, 0xFF,
+    };
+    m_pProperties[7]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[7])->
+    SetValue(reserved4, sizeof(reserved4));
+    m_pProperties[7]->SetReadOnly(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_free.cpp b/src/atom_free.cpp
new file mode 100644
index 0000000..5450e04
--- /dev/null
+++ b/src/atom_free.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4FreeAtom::MP4FreeAtom( MP4File &file, const char* type )
+    : MP4Atom( file, type ? type : "free" )
+{
+}
+
+void MP4FreeAtom::Read()
+{
+    Skip();
+}
+
+void MP4FreeAtom::Write()
+{
+    bool use64 = (GetSize() > (0xFFFFFFFF - 8));
+    BeginWrite(use64);
+#if 1
+    for (uint64_t ix = 0; ix < GetSize(); ix++) {
+        m_File.WriteUInt8(0);
+    }
+#else
+    m_File.SetPosition(m_File.GetPosition() + GetSize());
+#endif
+    FinishWrite(use64);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_ftab.cpp b/src/atom_ftab.cpp
new file mode 100644
index 0000000..1a1ea05
--- /dev/null
+++ b/src/atom_ftab.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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.  All Rights Reserved.
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4FtabAtom::MP4FtabAtom(MP4File &file)
+        : MP4Atom(file, "ftab")
+{
+    MP4Integer16Property* pCount = new MP4Integer16Property(*this, "entryCount"); /* 0 */
+    AddProperty(pCount);
+
+    MP4TableProperty* pTable = new MP4TableProperty(*this, "fontEntries", pCount);  /* 1 */
+    AddProperty(pTable);
+
+    pTable->AddProperty(new MP4Integer16Property(pTable->GetParentAtom(), "fontID"));  /* 0 */
+    pTable->AddProperty(new MP4StringProperty(pTable->GetParentAtom(), "name", true));  /* 1 */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_ftyp.cpp b/src/atom_ftyp.cpp
new file mode 100644
index 0000000..92b47d6
--- /dev/null
+++ b/src/atom_ftyp.cpp
@@ -0,0 +1,62 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4FtypAtom::MP4FtypAtom(MP4File &file)
+    : MP4Atom( file, "ftyp" )
+    , majorBrand       ( *new MP4StringProperty( *this, "majorBrand" ))
+    , minorVersion     ( *new MP4Integer32Property( *this, "minorVersion" ))
+    , compatibleBrands ( *new MP4StringProperty( *this, "compatibleBrands", false, false, true ))
+{
+    majorBrand.SetFixedLength( 4 );
+    compatibleBrands.SetFixedLength( 4 );
+
+    AddProperty( &majorBrand );
+    AddProperty( &minorVersion );
+    AddProperty( &compatibleBrands );
+}
+
+void MP4FtypAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    majorBrand.SetValue( "mp42" );
+    minorVersion.SetValue( 0 );
+
+    compatibleBrands.SetCount( 2 );
+    compatibleBrands.SetValue( "mp42", 0 );
+    compatibleBrands.SetValue( "isom", 1 );
+}
+
+void MP4FtypAtom::Read()
+{
+    compatibleBrands.SetCount( (m_size - 8) / 4 ); // brands array fills rest of atom
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_gmin.cpp b/src/atom_gmin.cpp
new file mode 100644
index 0000000..cb94a1e
--- /dev/null
+++ b/src/atom_gmin.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ *
+ * Contributer has declined to give copyright information, and gives
+ * it freely to the world.
+ *
+ * Contributor(s):
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4GminAtom::MP4GminAtom(MP4File &file)
+        : MP4Atom(file, "gmin")
+{
+
+    AddVersionAndFlags(); /* 0, 1 */
+
+    AddProperty(new MP4Integer16Property(*this, "graphicsMode")); /* 2 */
+    AddProperty(new MP4Integer16Property(*this, "opColorRed")); /* 3 */
+    AddProperty(new MP4Integer16Property(*this, "opColorGreen")); /* 4 */
+    AddProperty(new MP4Integer16Property(*this, "opColorBlue")); /* 5 */
+    AddProperty(new MP4Integer16Property(*this, "balance")); /* 6 */
+    AddReserved(*this, "reserved", 2); /* 7 */
+
+}
+
+void MP4GminAtom::Generate()
+{
+
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(0x0040);
+    ((MP4Integer16Property*)m_pProperties[3])->SetValue(0x8000);
+    ((MP4Integer16Property*)m_pProperties[4])->SetValue(0x8000);
+    ((MP4Integer16Property*)m_pProperties[5])->SetValue(0x8000);
+    ((MP4Integer16Property*)m_pProperties[6])->SetValue(0x0000);
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_hdlr.cpp b/src/atom_hdlr.cpp
new file mode 100644
index 0000000..db33e7e
--- /dev/null
+++ b/src/atom_hdlr.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4HdlrAtom::MP4HdlrAtom(MP4File &file)
+        : MP4Atom(file, "hdlr")
+{
+    AddVersionAndFlags(); /* 0, 1 */
+    AddReserved(*this, "reserved1", 4); /* 2 */
+    MP4StringProperty* pProp = new MP4StringProperty(*this, "handlerType");
+    pProp->SetFixedLength(4);
+    AddProperty(pProp); /* 3 */
+    AddReserved(*this, "reserved2", 12); /* 4 */
+    AddProperty( /* 5 */
+        new MP4StringProperty(*this, "name"));
+}
+
+// There is a spec incompatiblity between QT and MP4
+// QT says name field is a counted string
+// MP4 says name field is a null terminated string
+// Here we attempt to make all things work
+void MP4HdlrAtom::Read()
+{
+    // read all the properties but the "name" field
+    ReadProperties(0, 5);
+
+    uint64_t pos = m_File.GetPosition();
+    uint64_t end = GetEnd();
+    if (pos == end) {
+        // A hdlr atom with missing "name".     
+        // Apparently that's what some of the iTunes m4p files have.
+        return;
+    }
+
+    // take a peek at the next byte
+    uint8_t strLength;
+    m_File.PeekBytes(&strLength, 1);
+    // if the value matches the remaining atom length
+    if (pos + strLength + 1 == end) {
+        // read a counted string
+        MP4StringProperty* pNameProp =
+            (MP4StringProperty*)m_pProperties[5];
+        pNameProp->SetCountedFormat(true);
+        ReadProperties(5);
+        pNameProp->SetCountedFormat(false);
+    } else {
+        // read a null terminated string
+        try {
+            // Unfortunately, there are some invalid mp4 writers that don't
+            // null the hdlr name string.  Generally this will be "automatically"
+            // terminated for them by the size field of the subsequent atom.  So if
+            // our size is off by one...let it slide.  otherwise, rethrow.
+            // The Skip() call will set our start to the correct location
+            // for the next Atom. See issue #52
+            ReadProperties(5);
+        }
+        catch(Exception* x) { 
+            if( m_File.GetPosition() - GetEnd() == 1 )
+                delete x;
+            else
+                throw x;
+        }
+    }
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_hinf.cpp b/src/atom_hinf.cpp
new file mode 100644
index 0000000..5f6f8d7
--- /dev/null
+++ b/src/atom_hinf.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4HinfAtom::MP4HinfAtom(MP4File &file)
+        : MP4Atom(file, "hinf")
+{
+    ExpectChildAtom("trpy", Optional, OnlyOne);
+    ExpectChildAtom("nump", Optional, OnlyOne);
+    ExpectChildAtom("tpyl", Optional, OnlyOne);
+    ExpectChildAtom("maxr", Optional, Many);
+    ExpectChildAtom("dmed", Optional, OnlyOne);
+    ExpectChildAtom("dimm", Optional, OnlyOne);
+    ExpectChildAtom("drep", Optional, OnlyOne);
+    ExpectChildAtom("tmin", Optional, OnlyOne);
+    ExpectChildAtom("tmax", Optional, OnlyOne);
+    ExpectChildAtom("pmax", Optional, OnlyOne);
+    ExpectChildAtom("dmax", Optional, OnlyOne);
+    ExpectChildAtom("payt", Optional, OnlyOne);
+}
+
+void MP4HinfAtom::Generate()
+{
+    // hinf is special in that although all it's child atoms
+    // are optional (on read), if we generate it for writing
+    // we really want all the children
+
+    for (uint32_t i = 0; i < m_pChildAtomInfos.Size(); i++) {
+        MP4Atom* pChildAtom =
+            CreateAtom(m_File, this, m_pChildAtomInfos[i]->m_name);
+
+        AddChildAtom(pChildAtom);
+
+        // and ask it to self generate
+        pChildAtom->Generate();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_hnti.cpp b/src/atom_hnti.cpp
new file mode 100644
index 0000000..9f2cac1
--- /dev/null
+++ b/src/atom_hnti.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4HntiAtom::MP4HntiAtom(MP4File &file)
+        : MP4Atom(file, "hnti")
+{
+}
+
+void MP4HntiAtom::Read()
+{
+    MP4Atom* grandParent = m_pParentAtom->GetParentAtom();
+    ASSERT(grandParent);
+    if (ATOMID(grandParent->GetType()) == ATOMID("trak")) {
+        ExpectChildAtom("sdp ", Optional, OnlyOne);
+    } else {
+        ExpectChildAtom("rtp ", Optional, OnlyOne);
+    }
+
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_href.cpp b/src/atom_href.cpp
new file mode 100644
index 0000000..a15fe79
--- /dev/null
+++ b/src/atom_href.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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. 2005.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Bill May wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4HrefAtom::MP4HrefAtom(MP4File &file)
+        : MP4Atom(file, "href")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+    ExpectChildAtom("burl", Optional, OnlyOne);
+}
+
+void MP4HrefAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_mdat.cpp b/src/atom_mdat.cpp
new file mode 100644
index 0000000..fe50cb9
--- /dev/null
+++ b/src/atom_mdat.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4MdatAtom::MP4MdatAtom(MP4File &file)
+        : MP4Atom(file, "mdat")
+{
+}
+
+void MP4MdatAtom::Read()
+{
+    Skip();
+}
+
+void MP4MdatAtom::Write()
+{
+    // should never get here
+    ASSERT(false);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_mdhd.cpp b/src/atom_mdhd.cpp
new file mode 100644
index 0000000..28f2888
--- /dev/null
+++ b/src/atom_mdhd.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4MdhdAtom::MP4MdhdAtom(MP4File &file)
+        : MP4Atom(file, "mdhd")
+{
+    AddVersionAndFlags();
+}
+
+void MP4MdhdAtom::AddProperties(uint8_t version)
+{
+    if (version == 1) {
+        AddProperty(
+            new MP4Integer64Property(*this, "creationTime"));
+        AddProperty(
+            new MP4Integer64Property(*this, "modificationTime"));
+    } else {
+        AddProperty(
+            new MP4Integer32Property(*this, "creationTime"));
+        AddProperty(
+            new MP4Integer32Property(*this, "modificationTime"));
+    }
+
+    AddProperty(
+        new MP4Integer32Property(*this, "timeScale"));
+
+    if (version == 1) {
+        AddProperty(
+            new MP4Integer64Property(*this, "duration"));
+    } else {
+        AddProperty(
+            new MP4Integer32Property(*this, "duration"));
+    }
+
+    AddProperty( new MP4LanguageCodeProperty(*this,  "language" ));
+    AddReserved(*this, "reserved", 2);
+}
+
+void MP4MdhdAtom::Generate()
+{
+    uint8_t version = m_File.Use64Bits(GetType()) ? 1 : 0;
+    SetVersion(version);
+    AddProperties(version);
+
+    MP4Atom::Generate();
+
+    // set creation and modification times
+    MP4Timestamp now = MP4GetAbsTimestamp();
+    if (version == 1) {
+        ((MP4Integer64Property*)m_pProperties[2])->SetValue(now);
+        ((MP4Integer64Property*)m_pProperties[3])->SetValue(now);
+    } else {
+        ((MP4Integer32Property*)m_pProperties[2])->SetValue(now);
+        ((MP4Integer32Property*)m_pProperties[3])->SetValue(now);
+    }
+}
+
+void MP4MdhdAtom::Read()
+{
+    /* read atom version */
+    ReadProperties(0, 1);
+
+    /* need to create the properties based on the atom version */
+    AddProperties(GetVersion());
+
+    /* now we can read the remaining properties */
+    ReadProperties(1);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_meta.cpp b/src/atom_meta.cpp
new file mode 100644
index 0000000..9e6f1a6
--- /dev/null
+++ b/src/atom_meta.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      M. Bakker     mbakker at nero.com
+ *
+ * Apple iTunes META data
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4DataAtom::MP4DataAtom(MP4File &file)
+    : MP4Atom ( file, "data" )
+    , typeReserved      ( *new MP4Integer16Property( *this, "typeReserved" ))
+    , typeSetIdentifier ( *new MP4Integer8Property( *this, "typeSetIdentifier" ))
+    , typeCode          ( *new MP4BasicTypeProperty( *this, "typeCode" ))
+    , locale            ( *new MP4Integer32Property( *this, "locale" ))
+    , metadata          ( *new MP4BytesProperty( *this, "metadata" ))
+{
+    AddProperty( &typeReserved );
+    AddProperty( &typeSetIdentifier );
+    AddProperty( &typeCode );
+    AddProperty( &locale );
+    AddProperty( &metadata );
+}
+
+void
+MP4DataAtom::Read()
+{
+    // calculate size of the metadata from the atom size
+    metadata.SetValueSize( m_size - 8 );
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4FullAtom::MP4FullAtom( MP4File &file, const char* type )
+    : MP4Atom ( file, type )
+    , version ( *new MP4Integer8Property( *this, "version" ))
+    , flags   ( *new MP4Integer24Property( *this, "flags" ))
+{
+    AddProperty( &version );
+    AddProperty( &flags );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItemAtom::MP4ItemAtom( MP4File &file, const char* type )
+    : MP4Atom( file, type )
+{
+    ExpectChildAtom( "mean", Optional, OnlyOne );
+    ExpectChildAtom( "name", Optional, OnlyOne );
+    ExpectChildAtom( "data", Required, Many );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfHdlrAtom::MP4ItmfHdlrAtom(MP4File &file)
+    : MP4FullAtom ( file, "hdlr" )
+    , reserved1   ( *new MP4Integer32Property( *this, "reserved1" ))
+    , handlerType ( *new MP4BytesProperty( *this, "handlerType", 4 ))
+    , reserved2   ( *new MP4BytesProperty( *this, "reserved2", 12 ))
+    , name        ( *new MP4BytesProperty( *this, "name", 1 ))
+{
+    AddProperty( &reserved1 );
+    AddProperty( &handlerType );
+    AddProperty( &reserved2 );
+    AddProperty( &name );
+
+    const uint8_t htData[] = { 'm', 'd', 'i', 'r' };
+    handlerType.SetValue( htData, sizeof( htData ));
+
+    const uint8_t nameData[] = { 0 };
+    name.SetValue( nameData, sizeof( nameData ));
+}
+
+void
+MP4ItmfHdlrAtom::Read()
+{
+    name.SetValueSize( m_size - 24 );
+    MP4FullAtom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4MeanAtom::MP4MeanAtom(MP4File &file)
+    : MP4FullAtom ( file, "mean" )
+    , value       ( *new MP4BytesProperty( *this, "value" ))
+{
+    AddProperty( &value );
+}
+
+void
+MP4MeanAtom::Read()
+{
+    value.SetValueSize( m_size - 4 );
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4NameAtom::MP4NameAtom(MP4File &file)
+    : MP4FullAtom ( file, "name" )
+    , value       ( *new MP4BytesProperty( *this, "value" ))
+{
+    AddProperty( &value );
+}
+
+void
+MP4NameAtom::Read()
+{
+    value.SetValueSize( m_size - 4 );
+    MP4FullAtom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4UdtaElementAtom::MP4UdtaElementAtom( MP4File &file, const char* type )
+    : MP4Atom ( file, type )
+    , value   ( *new MP4BytesProperty( *this, "value" ))
+{
+    AddProperty( &value );
+}
+
+void
+MP4UdtaElementAtom::Read()
+{
+    // calculate size of the metadata from the atom size
+    value.SetValueSize( m_size );
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_mp4s.cpp b/src/atom_mp4s.cpp
new file mode 100644
index 0000000..293a554
--- /dev/null
+++ b/src/atom_mp4s.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Mp4sAtom::MP4Mp4sAtom(MP4File &file)
+        : MP4Atom(file, "mp4s")
+{
+    AddReserved(*this, "reserved1", 6);
+    AddProperty(
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    ExpectChildAtom("esds", Required, OnlyOne);
+}
+
+void MP4Mp4sAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_mp4v.cpp b/src/atom_mp4v.cpp
new file mode 100644
index 0000000..9cf6444
--- /dev/null
+++ b/src/atom_mp4v.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Mp4vAtom::MP4Mp4vAtom(MP4File &file)
+        : MP4Atom(file, "mp4v")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddReserved(*this, "reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "width"));
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this, "height"));
+
+    AddReserved(*this, "reserved3", 14); /* 5 */
+
+    MP4StringProperty* pProp =
+        new MP4StringProperty(*this, "compressorName");
+    pProp->SetFixedLength(32);
+    pProp->SetCountedFormat(true);
+    pProp->SetValue("");
+    AddProperty(pProp); /* 6 */
+
+    AddReserved(*this, "reserved4", 4); /* 7 */
+
+    ExpectChildAtom("colr", Optional, OnlyOne);
+    ExpectChildAtom("esds", Required, OnlyOne);
+    ExpectChildAtom("pasp", Optional, OnlyOne);
+}
+
+void MP4Mp4vAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved3 has non-zero fixed values
+    static uint8_t reserved3[14] = {
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01,
+    };
+    m_pProperties[5]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[5])->
+    SetValue(reserved3, sizeof(reserved3));
+    m_pProperties[5]->SetReadOnly(true);
+
+    // property reserved4 has non-zero fixed values
+    static uint8_t reserved4[4] = {
+        0x00, 0x18, 0xFF, 0xFF,
+    };
+    m_pProperties[7]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[7])->
+    SetValue(reserved4, sizeof(reserved4));
+    m_pProperties[7]->SetReadOnly(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_mvhd.cpp b/src/atom_mvhd.cpp
new file mode 100644
index 0000000..f3863de
--- /dev/null
+++ b/src/atom_mvhd.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4MvhdAtom::MP4MvhdAtom(MP4File &file)
+        : MP4Atom(file, "mvhd")
+{
+    AddVersionAndFlags();
+}
+
+void MP4MvhdAtom::AddProperties(uint8_t version)
+{
+    if (version == 1) {
+        AddProperty( /* 2 */
+            new MP4Integer64Property(*this, "creationTime"));
+        AddProperty( /* 3 */
+            new MP4Integer64Property(*this, "modificationTime"));
+    } else {
+        AddProperty( /* 2 */
+            new MP4Integer32Property(*this, "creationTime"));
+        AddProperty( /* 3 */
+            new MP4Integer32Property(*this, "modificationTime"));
+    }
+
+    AddProperty( /* 4 */
+        new MP4Integer32Property(*this, "timeScale"));
+
+    if (version == 1) {
+        AddProperty( /* 5 */
+            new MP4Integer64Property(*this, "duration"));
+    } else {
+        AddProperty( /* 5 */
+            new MP4Integer32Property(*this, "duration"));
+    }
+
+    MP4Float32Property* pProp;
+
+    pProp = new MP4Float32Property(*this, "rate");
+    pProp->SetFixed32Format();
+    AddProperty(pProp); /* 6 */
+
+    pProp = new MP4Float32Property(*this, "volume");
+    pProp->SetFixed16Format();
+    AddProperty(pProp); /* 7 */
+
+    AddReserved(*this, "reserved1", 70); /* 8 */
+
+    AddProperty( /* 9 */
+        new MP4Integer32Property(*this, "nextTrackId"));
+}
+
+void MP4MvhdAtom::Generate()
+{
+    uint8_t version = m_File.Use64Bits(GetType()) ? 1 : 0;
+    SetVersion(version);
+    AddProperties(version);
+
+    MP4Atom::Generate();
+
+    // set creation and modification times
+    MP4Timestamp now = MP4GetAbsTimestamp();
+    if (version == 1) {
+        ((MP4Integer64Property*)m_pProperties[2])->SetValue(now);
+        ((MP4Integer64Property*)m_pProperties[3])->SetValue(now);
+    } else {
+        ((MP4Integer32Property*)m_pProperties[2])->SetValue(now);
+        ((MP4Integer32Property*)m_pProperties[3])->SetValue(now);
+    }
+
+    ((MP4Integer32Property*)m_pProperties[4])->SetValue(1000);
+
+    ((MP4Float32Property*)m_pProperties[6])->SetValue(1.0);
+    ((MP4Float32Property*)m_pProperties[7])->SetValue(1.0);
+
+    // property reserved has non-zero fixed values
+    static uint8_t reserved[70] = {
+        0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+    };
+    m_pProperties[8]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[8])->
+    SetValue(reserved, sizeof(reserved));
+    m_pProperties[8]->SetReadOnly(true);
+
+    // set next track id
+    ((MP4Integer32Property*)m_pProperties[9])->SetValue(1);
+}
+
+void MP4MvhdAtom::Read()
+{
+    /* read atom version */
+    ReadProperties(0, 1);
+
+    /* need to create the properties based on the atom version */
+    AddProperties(GetVersion());
+
+    /* now we can read the remaining properties */
+    ReadProperties(1);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_nmhd.cpp b/src/atom_nmhd.cpp
new file mode 100644
index 0000000..8e3a52d
--- /dev/null
+++ b/src/atom_nmhd.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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.  All Rights Reserved.
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4NmhdAtom::MP4NmhdAtom(MP4File &file)
+        : MP4Atom(file, "nmhd")
+{
+    AddVersionAndFlags();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_ohdr.cpp b/src/atom_ohdr.cpp
new file mode 100644
index 0000000..5652c16
--- /dev/null
+++ b/src/atom_ohdr.cpp
@@ -0,0 +1,106 @@
+/** \file atom_ohdr.cpp
+
+    \author Danijel Kopcinovic (danijel.kopcinovic@adnecto.net)
+*/
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*! \brief Patch class for read/write operations when string is 0-length.
+
+    We want to use string property, but mpeg4ip doesn't support ohdr way of
+    encoding of string (in ohdr atom we first have 3 lengths of 3 strings and
+    then their string values, and it cannot be simulated with any of the
+    current mpeg4ip string property parameters), so we have to write our own
+    Read() and Write() routines.
+*/
+class OhdrMP4StringProperty: public MP4StringProperty {
+public:
+    /*! \brief Constructor.
+
+        \param name                name of the property.
+        \param useCountedFormat    counted format flag.
+        \param useUnicode          unicode flag.
+    */
+    OhdrMP4StringProperty(MP4Atom& parentAtom, const char* name, bool useCountedFormat = false,
+                          bool useUnicode = false): MP4StringProperty(parentAtom, name, useCountedFormat,
+                                          useUnicode) {
+    }
+
+    /*! \brief Read property from file.
+
+        \param pFile                input, file handle.
+        \param index                input, index to read.
+    */
+    void Read(MP4File& file, uint32_t index = 0) {
+        MP4Free(m_values[index]);
+        m_values[index] = (char*)MP4Calloc(m_fixedLength + 1);
+        (void)file.ReadBytes((uint8_t*)m_values[index], m_fixedLength);
+    }
+
+    /*! \brief Write property to file.
+
+        \param pFile                input, file handle.
+        \param index                input, index to write.
+    */
+    void Write(MP4File& file, uint32_t index = 0) {
+        file.WriteBytes((uint8_t*)m_values[index], m_fixedLength);
+    }
+private:
+    OhdrMP4StringProperty();
+    OhdrMP4StringProperty ( const OhdrMP4StringProperty &src );
+    OhdrMP4StringProperty &operator= ( const OhdrMP4StringProperty &src );
+};
+
+/*! \brief OMA DRM headers atom.
+
+    Contained in OMA DRM key management atom. It must contain content identifier.
+*/
+/*! \brief Constructor.
+*/
+MP4OhdrAtom::MP4OhdrAtom(MP4File &file): MP4Atom(file, "ohdr") {
+    AddVersionAndFlags();
+
+    AddProperty(new MP4Integer8Property(*this, "EncryptionMethod"));
+    AddProperty(new MP4Integer8Property(*this, "EncryptionPadding"));
+    AddProperty(new MP4Integer64Property(*this, "PlaintextLength"));
+    AddProperty(new MP4Integer16Property(*this, "ContentIDLength"));
+    AddProperty(new MP4Integer16Property(*this, "RightsIssuerURLLength"));
+    AddProperty(new MP4Integer16Property(*this, "TextualHeadersLength"));
+    AddProperty(new OhdrMP4StringProperty(*this, "ContentID"));
+    AddProperty(new OhdrMP4StringProperty(*this, "RightsIssuerURL"));
+    AddProperty(new MP4BytesProperty(*this, "TextualHeaders"));
+}
+
+MP4OhdrAtom::~MP4OhdrAtom() {
+}
+
+/*! \brief Read atom.
+*/
+void MP4OhdrAtom::Read() {
+    ReadProperties(0, 8);
+    MP4Property* lProperty;
+    MP4Property* property;
+    lProperty = GetProperty(5);
+    property = GetProperty(8);
+    ((OhdrMP4StringProperty*)property)->SetFixedLength(
+        ((MP4Integer16Property*)lProperty)->GetValue());
+    lProperty = GetProperty(6);
+    property = GetProperty(9);
+    ((OhdrMP4StringProperty*)property)->SetFixedLength(
+        ((MP4Integer16Property*)lProperty)->GetValue());
+    lProperty = GetProperty(7);
+    property = GetProperty(10);
+    ((MP4BytesProperty*)property)->SetFixedSize(
+        ((MP4Integer16Property*)lProperty)->GetValue());
+    ReadProperties(8, 3);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_pasp.cpp b/src/atom_pasp.cpp
new file mode 100644
index 0000000..0ecebf2
--- /dev/null
+++ b/src/atom_pasp.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ * Contributer has declined to give copyright information, and gives
+ * it freely to the world.
+ *
+ * Contributor(s):
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4PaspAtom::MP4PaspAtom(MP4File &file)
+        : MP4Atom(file, "pasp")
+{
+
+    AddProperty( /* 0 */
+        new MP4Integer32Property(*this, "hSpacing"));
+
+    AddProperty( /* 1 */
+        new MP4Integer32Property(*this, "vSpacing"));
+
+}
+
+void MP4PaspAtom::Generate()
+{
+
+    MP4Atom::Generate();
+
+    ((MP4Integer32Property*)m_pProperties[0])->SetValue(1);
+    ((MP4Integer32Property*)m_pProperties[1])->SetValue(1);
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_root.cpp b/src/atom_root.cpp
new file mode 100644
index 0000000..4702bb9
--- /dev/null
+++ b/src/atom_root.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4RootAtom::MP4RootAtom(MP4File &file)
+    : MP4Atom( file, NULL )
+    , m_rewrite_ftyp         ( NULL )
+    , m_rewrite_ftypPosition ( 0 )
+    , m_rewrite_free         ( NULL )
+    , m_rewrite_freePosition ( 0 )
+{
+    ExpectChildAtom( "moov", Required, OnlyOne );
+    ExpectChildAtom( "ftyp", Optional, OnlyOne );
+    ExpectChildAtom( "mdat", Optional, Many );
+    ExpectChildAtom( "free", Optional, Many );
+    ExpectChildAtom( "skip", Optional, Many );
+    ExpectChildAtom( "udta", Optional, Many );
+    ExpectChildAtom( "moof", Optional, Many );
+}
+
+void MP4RootAtom::BeginWrite(bool use64)
+{
+    m_rewrite_ftyp = (MP4FtypAtom*)FindChildAtom( "ftyp" );
+    if( m_rewrite_ftyp ) {
+        m_rewrite_free = (MP4FreeAtom*)MP4Atom::CreateAtom( m_File, NULL, "free" );
+        m_rewrite_free->SetSize( 32*4 ); // room for 32 additional brands
+        AddChildAtom( m_rewrite_free );
+
+        m_rewrite_ftypPosition = m_File.GetPosition();
+        m_rewrite_ftyp->Write();
+
+        m_rewrite_freePosition = m_File.GetPosition();
+        m_rewrite_free->Write();
+    }
+
+    m_pChildAtoms[GetLastMdatIndex()]->BeginWrite( m_File.Use64Bits( "mdat" ));
+}
+
+void MP4RootAtom::Write()
+{
+    // no-op
+}
+
+void MP4RootAtom::FinishWrite(bool use64)
+{
+    if( m_rewrite_ftyp ) {
+        const uint64_t savepos = m_File.GetPosition();
+        m_File.SetPosition( m_rewrite_ftypPosition );
+        m_rewrite_ftyp->Write();
+
+        const uint64_t newpos = m_File.GetPosition();
+        if( newpos > m_rewrite_freePosition )
+            m_rewrite_free->SetSize( m_rewrite_free->GetSize() - (newpos - m_rewrite_freePosition) ); // shrink
+        else if( newpos < m_rewrite_freePosition )
+            m_rewrite_free->SetSize( m_rewrite_free->GetSize() + (m_rewrite_freePosition - newpos) ); // grow
+
+        m_rewrite_free->Write();
+        m_File.SetPosition( savepos );
+    }
+
+    // finish writing last mdat atom
+    const uint32_t mdatIndex = GetLastMdatIndex();
+    m_pChildAtoms[mdatIndex]->FinishWrite( m_File.Use64Bits( "mdat" ));
+
+    // write all atoms after last mdat
+    const uint32_t size = m_pChildAtoms.Size();
+    for ( uint32_t i = mdatIndex + 1; i < size; i++ )
+        m_pChildAtoms[i]->Write();
+}
+
+void MP4RootAtom::BeginOptimalWrite()
+{
+    WriteAtomType("ftyp", OnlyOne);
+    WriteAtomType("moov", OnlyOne);
+    WriteAtomType("udta", Many);
+
+    m_pChildAtoms[GetLastMdatIndex()]->BeginWrite(m_File.Use64Bits("mdat"));
+}
+
+void MP4RootAtom::FinishOptimalWrite()
+{
+    // finish writing mdat
+    m_pChildAtoms[GetLastMdatIndex()]->FinishWrite(m_File.Use64Bits("mdat"));
+
+    // find moov atom
+    uint32_t size = m_pChildAtoms.Size();
+    MP4Atom* pMoovAtom = NULL;
+
+    uint32_t i;
+    for (i = 0; i < size; i++) {
+        if (!strcmp("moov", m_pChildAtoms[i]->GetType())) {
+            pMoovAtom = m_pChildAtoms[i];
+            break;
+        }
+    }
+    ASSERT(i < size);
+    ASSERT(pMoovAtom != NULL);
+
+    // rewrite moov so that updated chunkOffsets are written to disk
+    m_File.SetPosition(pMoovAtom->GetStart());
+    uint64_t oldSize = pMoovAtom->GetSize();
+
+    pMoovAtom->Write();
+
+    // sanity check
+    uint64_t newSize = pMoovAtom->GetSize();
+    ASSERT(oldSize == newSize);
+}
+
+uint32_t MP4RootAtom::GetLastMdatIndex()
+{
+    for (int32_t i = m_pChildAtoms.Size() - 1; i >= 0; i--) {
+        if (!strcmp("mdat", m_pChildAtoms[i]->GetType())) {
+            return i;
+        }
+    }
+    ASSERT(false);
+    return (uint32_t)-1;
+}
+
+void MP4RootAtom::WriteAtomType(const char* type, bool onlyOne)
+{
+    uint32_t size = m_pChildAtoms.Size();
+
+    for (uint32_t i = 0; i < size; i++) {
+        if (!strcmp(type, m_pChildAtoms[i]->GetType())) {
+            m_pChildAtoms[i]->Write();
+            if (onlyOne) {
+                break;
+            }
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_rtp.cpp b/src/atom_rtp.cpp
new file mode 100644
index 0000000..af459ed
--- /dev/null
+++ b/src/atom_rtp.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4RtpAtom::MP4RtpAtom(MP4File &file)
+        : MP4Atom(file, "rtp ")
+{
+    // The atom type "rtp " is used in two complete unrelated ways
+    // i.e. it's real two atoms with the same name
+    // To handle that we need to postpone property creation until
+    // we know who our parent atom is (stsd or hnti) which gives us
+    // the context info we need to know who we are
+}
+
+void MP4RtpAtom::AddPropertiesStsdType()
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddProperty( /* 2 */
+        new MP4Integer16Property(*this, "hintTrackVersion"));
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "highestCompatibleVersion"));
+    AddProperty( /* 4 */
+        new MP4Integer32Property(*this, "maxPacketSize"));
+
+    ExpectChildAtom("tims", Required, OnlyOne);
+    ExpectChildAtom("tsro", Optional, OnlyOne);
+    ExpectChildAtom("snro", Optional, OnlyOne);
+}
+
+void MP4RtpAtom::AddPropertiesHntiType()
+{
+    MP4StringProperty* pProp =
+        new MP4StringProperty(*this, "descriptionFormat");
+    pProp->SetFixedLength(4);
+    AddProperty(pProp); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4StringProperty(*this, "sdpText"));
+}
+
+void MP4RtpAtom::Generate()
+{
+    ASSERT(m_pParentAtom);
+    if (!strcmp(m_pParentAtom->GetType(), "stsd")) {
+        AddPropertiesStsdType();
+        GenerateStsdType();
+    } else if (!strcmp(m_pParentAtom->GetType(), "hnti")) {
+        AddPropertiesHntiType();
+        GenerateHntiType();
+    } else {
+        log.warningf("%s: \"%s\": rtp atom in unexpected context, can not generate",
+                     __FUNCTION__, GetFile().GetFilename().c_str() );
+    }
+}
+
+void MP4RtpAtom::GenerateStsdType()
+{
+    // generate children
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(1);
+    ((MP4Integer16Property*)m_pProperties[3])->SetValue(1);
+}
+
+void MP4RtpAtom::GenerateHntiType()
+{
+    MP4Atom::Generate();
+
+    ((MP4StringProperty*)m_pProperties[0])->SetValue("sdp ");
+}
+
+void MP4RtpAtom::Read()
+{
+    ASSERT(m_pParentAtom);
+    if (!strcmp(m_pParentAtom->GetType(), "stsd")) {
+        AddPropertiesStsdType();
+        ReadStsdType();
+    } else if (!strcmp(m_pParentAtom->GetType(), "hnti")) {
+        AddPropertiesHntiType();
+        ReadHntiType();
+    } else {
+        log.verbose1f("rtp atom in unexpected context, can not read");
+    }
+
+    Skip(); // to end of atom
+}
+
+void MP4RtpAtom::ReadStsdType()
+{
+    MP4Atom::Read();
+}
+
+void MP4RtpAtom::ReadHntiType()
+{
+    ReadProperties(0, 1);
+
+    // read sdp string, length is implicit in size of atom
+    uint64_t size = GetEnd() - m_File.GetPosition();
+    char* data = (char*)MP4Malloc(size + 1);
+    ASSERT(data != NULL);
+    m_File.ReadBytes((uint8_t*)data, size);
+    data[size] = '\0';
+    ((MP4StringProperty*)m_pProperties[1])->SetValue(data);
+    MP4Free(data);
+}
+
+void MP4RtpAtom::Write()
+{
+    if (!strcmp(m_pParentAtom->GetType(), "hnti")) {
+        WriteHntiType();
+    } else {
+        MP4Atom::Write();
+    }
+}
+
+void MP4RtpAtom::WriteHntiType()
+{
+    // since length of string is implicit in size of atom
+    // we need to handle this specially, and not write the terminating \0
+    MP4StringProperty* pSdp = (MP4StringProperty*)m_pProperties[1];
+    pSdp->SetFixedLength((uint32_t)strlen(pSdp->GetValue()));
+    MP4Atom::Write();
+    pSdp->SetFixedLength(0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_s263.cpp b/src/atom_s263.cpp
new file mode 100644
index 0000000..c53e00a
--- /dev/null
+++ b/src/atom_s263.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.  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.
+ *
+ * Contributor(s):
+ *              Ximpo Group Ltd.          mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4S263Atom::MP4S263Atom(MP4File &file)
+        : MP4Atom(file, "s263")
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddReserved(*this, "reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "width"));
+
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this, "height"));
+
+    AddReserved(*this, "reserved3", 50); /* 5 */
+
+
+    ExpectChildAtom("d263", Required, OnlyOne);
+}
+
+void MP4S263Atom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved2 has non-zero fixed values
+    static uint8_t reserved3[50] = {
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x24,
+        0xFF, 0xFF
+    };
+    m_pProperties[5]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[5])->
+    SetValue(reserved3, sizeof(reserved3));
+    m_pProperties[5]->SetReadOnly(true);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_sdp.cpp b/src/atom_sdp.cpp
new file mode 100644
index 0000000..386a0a1
--- /dev/null
+++ b/src/atom_sdp.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4SdpAtom::MP4SdpAtom(MP4File &file) : MP4Atom(file, "sdp ")
+{
+    AddProperty(
+        new MP4StringProperty(*this, "sdpText"));
+}
+
+void MP4SdpAtom::Read()
+{
+    // read sdp string, length is implicit in size of atom
+    uint64_t size = GetEnd() - m_File.GetPosition();
+    char* data = (char*)MP4Malloc(size + 1);
+    ASSERT(data != NULL);
+    m_File.ReadBytes((uint8_t*)data, size);
+    data[size] = '\0';
+    ((MP4StringProperty*)m_pProperties[0])->SetValue(data);
+    MP4Free(data);
+}
+
+void MP4SdpAtom::Write()
+{
+    // since length of string is implicit in size of atom
+    // we need to handle this specially, and not write the terminating \0
+    MP4StringProperty* pSdp = (MP4StringProperty*)m_pProperties[0];
+    const char* sdpText = pSdp->GetValue();
+    if (sdpText) {
+        pSdp->SetFixedLength((uint32_t)strlen(sdpText));
+    }
+    MP4Atom::Write();
+    pSdp->SetFixedLength(0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_sdtp.cpp b/src/atom_sdtp.cpp
new file mode 100644
index 0000000..4afe653
--- /dev/null
+++ b/src/atom_sdtp.cpp
@@ -0,0 +1,47 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4SdtpAtom::MP4SdtpAtom(MP4File &file)
+    : MP4FullAtom ( file, "sdtp" )
+    , data        ( *new MP4BytesProperty( *this, "data" ))
+{
+    AddProperty( &data );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void MP4SdtpAtom::Read()
+{
+    data.SetValueSize( m_size - 4 );
+    MP4FullAtom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_smi.cpp b/src/atom_smi.cpp
new file mode 100644
index 0000000..348b9ab
--- /dev/null
+++ b/src/atom_smi.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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. 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *       Bill May  wmay@cisco.com
+ *
+ * Apple iTunes META data
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4SmiAtom::MP4SmiAtom(MP4File &file)
+        : MP4Atom(file, "meta")
+{
+
+    AddProperty( new MP4BytesProperty(*this, "metadata"));
+
+}
+
+void MP4SmiAtom::Read()
+{
+    // calculate size of the metadata from the atom size
+    ((MP4BytesProperty*)m_pProperties[0])->SetValueSize(m_size);
+
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_sound.cpp b/src/atom_sound.cpp
new file mode 100644
index 0000000..368f431
--- /dev/null
+++ b/src/atom_sound.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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. 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Bill May        wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4SoundAtom::MP4SoundAtom(MP4File &file, const char *atomid)
+        : MP4Atom(file, atomid)
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+    AddProperty( /* 2 */
+        new MP4Integer16Property(*this, "soundVersion"));
+    AddReserved( *this, "reserved2", 6); /* 3 */
+
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this, "channels"));
+    AddProperty( /* 5 */
+        new MP4Integer16Property(*this, "sampleSize"));
+    AddProperty( /* 6 */
+        new MP4Integer16Property(*this, "compressionId"));
+    AddProperty( /* 7 */
+        new MP4Integer16Property(*this, "packetSize"));
+    AddProperty( /* 8 */
+        new MP4Integer32Property(*this, "timeScale"));
+
+    if (ATOMID(atomid) == ATOMID("mp4a")) {
+        ExpectChildAtom("esds", Required, OnlyOne);
+        ExpectChildAtom("wave", Optional, OnlyOne);
+    } else if (ATOMID(atomid) == ATOMID("alac")) {
+        ExpectChildAtom("alac", Optional, Optional);
+        //AddProperty( new MP4BytesProperty(*this, "alacInfo", 36));
+    }
+}
+
+void MP4SoundAtom::AddProperties (uint8_t version)
+{
+    if (version > 0) {
+        AddProperty( /* 9 */
+            new MP4Integer32Property(*this, "samplesPerPacket"));
+        AddProperty( /* 10 */
+            new MP4Integer32Property(*this, "bytesPerPacket"));
+        AddProperty( /* 11 */
+            new MP4Integer32Property(*this, "bytesPerFrame"));
+        AddProperty( /* 12 */
+            new MP4Integer32Property(*this, "bytesPerSample"));
+    }
+    if (version == 2) {
+        AddReserved(*this, "reserved4", 20);
+    }
+}
+void MP4SoundAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(0);
+
+    // property reserved2 has non-zero fixed values
+    static const uint8_t reserved2[6] = {
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00,
+    };
+    m_pProperties[3]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[3])->
+    SetValue(reserved2, sizeof(reserved2));
+    m_pProperties[3]->SetReadOnly(true);
+    ((MP4Integer16Property*)m_pProperties[4])->SetValue(2);
+    ((MP4Integer16Property*)m_pProperties[5])->SetValue(0x0010);
+    ((MP4Integer16Property*)m_pProperties[6])->SetValue(0);
+
+}
+
+void MP4SoundAtom::Read()
+{
+    MP4Atom *parent = GetParentAtom();
+    if (ATOMID(parent->GetType()) != ATOMID("stsd")) {
+        // Quicktime has an interesting thing - they'll put an mp4a atom
+        // which is blank inside a wave atom, which is inside an mp4a atom
+        // we have a mp4a inside an wave inside an mp4a - delete all properties
+        for(int i = 0; i < 9; ++ i)
+            delete m_pProperties[i];	// make sure we delete the properties themselves, then remove from  m_pProperties
+
+        m_pProperties.Delete(8);
+        m_pProperties.Delete(7);
+        m_pProperties.Delete(6);
+        m_pProperties.Delete(5);
+        m_pProperties.Delete(4);
+        m_pProperties.Delete(3);
+        m_pProperties.Delete(2);
+        m_pProperties.Delete(1);
+        m_pProperties.Delete(0);
+
+        if (ATOMID(GetType()) == ATOMID("alac")) {
+            AddProperty(new MP4BytesProperty(*this, "decoderConfig", m_size));
+            ReadProperties();
+        }
+        if (m_pChildAtomInfos.Size() > 0) {
+            ReadChildAtoms();
+        }
+    } else {
+        ReadProperties(0, 3); // read first 3 properties
+        AddProperties(((MP4IntegerProperty *)m_pProperties[2])->GetValue());
+        ReadProperties(3); // continue
+        if (m_pChildAtomInfos.Size() > 0) {
+            ReadChildAtoms();
+        }
+    }
+    Skip();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_standard.cpp b/src/atom_standard.cpp
new file mode 100644
index 0000000..ba567fd
--- /dev/null
+++ b/src/atom_standard.cpp
@@ -0,0 +1,420 @@
+/*
+ * 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. 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Bill May (from others work).
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4StandardAtom::MP4StandardAtom (MP4File &file, const char *type) : MP4Atom(file, type)
+{
+    /*
+     * This is a big if else loop.  Make sure that you don't break it
+     * when adding new atoms, or you will set the unknown type flag
+     *
+     * Try to keep it in alphabetical order - it should only be called
+     * 1 time per atom, so it's not that urgent.
+     */
+    if (ATOMID(type) == ATOMID("bitr")) {
+        AddProperty( /* 0 */
+            new MP4Integer32Property(*this, "avgBitrate"));
+
+        AddProperty( /* 1 */
+            new MP4Integer32Property(*this, "maxBitrate"));
+
+    } else if (ATOMID(type) == ATOMID("btrt")) {
+        AddProperty( new MP4Integer32Property(*this, "bufferSizeDB")); /* 0 */
+        AddProperty( new MP4Integer32Property(*this, "avgBitrate"));   /* 1 */
+        AddProperty( new MP4Integer32Property(*this, "maxBitrate"));   /* 2 */
+    } else if (ATOMID(type) == ATOMID("burl")) {
+        AddProperty( new MP4StringProperty(*this, "base_url"));
+        /*
+         * c???
+         */
+    } else if (ATOMID(type) == ATOMID("co64")) {
+        AddVersionAndFlags();
+
+        MP4Integer32Property* pCount =
+            new MP4Integer32Property(*this, "entryCount");
+        AddProperty(pCount);
+
+        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+        AddProperty(pTable);
+
+        pTable->AddProperty(
+            new MP4Integer64Property(*this, "chunkOffset"));
+    } else if (ATOMID(type) == ATOMID("ctts")) {
+        AddVersionAndFlags();
+
+        MP4Integer32Property* pCount =
+            new MP4Integer32Property(*this, "entryCount");
+        AddProperty(pCount);
+
+        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+        AddProperty(pTable);
+
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleCount"));
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleOffset"));
+        /*
+         * d???
+         */
+    } else if (ATOMID(type) == ATOMID("dinf")) {
+        ExpectChildAtom("dref", Required, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("dimm")) {
+        AddProperty( // bytes of immediate data
+            new MP4Integer64Property(*this, "bytes"));
+
+    } else if (ATOMID(type) == ATOMID("dmax")) {
+        AddProperty( // max packet duration
+            new MP4Integer32Property(*this, "milliSecs"));
+
+    } else if (ATOMID(type) == ATOMID("dmed")) {
+        AddProperty( // bytes sent from media data
+            new MP4Integer64Property(*this, "bytes"));
+
+    } else if (ATOMID(type) == ATOMID("drep")) {
+        AddProperty( // bytes of repeated data
+            new MP4Integer64Property(*this, "bytes"));
+        /*
+         * e???
+         */
+    } else if (ATOMID(type) == ATOMID("edts")) {
+        ExpectChildAtom("elst", Required, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("esds")) {
+        AddVersionAndFlags();
+        AddProperty(
+            new MP4DescriptorProperty(*this, NULL, MP4ESDescrTag, 0,
+                                      Required, OnlyOne));
+        /*
+         * f???
+         */
+    } else if (ATOMID(type) == ATOMID("frma")) {
+        AddProperty( /* 0 */
+            new MP4Integer32Property(*this, "data-format"));
+        /*
+         * g???
+         */
+    } else if (ATOMID(type) == ATOMID("gmhd")) {
+        ExpectChildAtom("gmin", Required, OnlyOne);
+        ExpectChildAtom("tmcd", Optional, OnlyOne);
+        ExpectChildAtom("text", Optional, OnlyOne);
+    } else if (ATOMID(type) == ATOMID("hmhd")) {
+        AddVersionAndFlags();
+
+        AddProperty(new MP4Integer16Property(*this, "maxPduSize"));
+        AddProperty(new MP4Integer16Property(*this, "avgPduSize"));
+        AddProperty(new MP4Integer32Property(*this, "maxBitRate"));
+        AddProperty(new MP4Integer32Property(*this, "avgBitRate"));
+        AddProperty(new MP4Integer32Property(*this, "slidingAvgBitRate"));
+        /*
+         * i???
+         */
+    } else if (ATOMID(type) == ATOMID("iKMS")) {
+        AddVersionAndFlags(); /* 0, 1 */
+        MP4StringProperty* pProp = new MP4StringProperty(*this, "kms_URI");
+        AddProperty(pProp); /* 2 */
+
+    } else if (ATOMID(type) == ATOMID("iSFM")) {
+        AddVersionAndFlags(); /* 0, 1 */
+        AddProperty( /* 2 */
+            new MP4BitfieldProperty(*this, "selective-encryption", 1));
+        AddProperty( /* 3 */
+            new MP4BitfieldProperty(*this, "reserved", 7));
+        AddProperty( /* 4 */
+            new MP4Integer8Property(*this, "key-indicator-length"));
+        AddProperty( /* 5 */
+            new MP4Integer8Property(*this, "IV-length"));
+
+    } else if (ATOMID(type) == ATOMID("ilst")) {
+        ExpectChildAtom("\251nam", Optional, OnlyOne); /* name */
+        ExpectChildAtom("\251ART", Optional, OnlyOne); /* artist */
+        ExpectChildAtom("\251wrt", Optional, OnlyOne); /* writer */
+        ExpectChildAtom("\251alb", Optional, OnlyOne); /* album */
+        ExpectChildAtom("\251day", Optional, OnlyOne); /* date */
+        ExpectChildAtom("\251too", Optional, OnlyOne); /* tool */
+        ExpectChildAtom("\251cmt", Optional, OnlyOne); /* comment */
+        ExpectChildAtom("\251gen", Optional, OnlyOne); /* custom genre */
+        ExpectChildAtom("trkn", Optional, OnlyOne); /* tracknumber */
+        ExpectChildAtom("disk", Optional, OnlyOne); /* disknumber */
+        ExpectChildAtom("gnre", Optional, OnlyOne); /* genre (ID3v1 index + 1) */
+        ExpectChildAtom("cpil", Optional, OnlyOne); /* compilation */
+        ExpectChildAtom("tmpo", Optional, OnlyOne); /* BPM */
+        ExpectChildAtom("covr", Optional, OnlyOne); /* cover art */
+        ExpectChildAtom("aART", Optional, OnlyOne); /* album artist */
+        ExpectChildAtom("----", Optional, Many); /* ---- free form */
+        ExpectChildAtom("pgap", Optional, OnlyOne); /* part of gapless album */
+        ExpectChildAtom("tvsh", Optional, OnlyOne); /* TV show */
+        ExpectChildAtom("tvsn", Optional, OnlyOne); /* TV season */
+        ExpectChildAtom("tven", Optional, OnlyOne); /* TV episode number */
+        ExpectChildAtom("tvnn", Optional, OnlyOne); /* TV network name */
+        ExpectChildAtom("tves", Optional, OnlyOne); /* TV epsidoe */
+        ExpectChildAtom("desc", Optional, OnlyOne); /* description */
+        ExpectChildAtom("ldes", Optional, OnlyOne); /* long description */
+        ExpectChildAtom("soal", Optional, OnlyOne); /* sort album */
+        ExpectChildAtom("soar", Optional, OnlyOne); /* sort artist */
+        ExpectChildAtom("soaa", Optional, OnlyOne); /* sort album artist */
+        ExpectChildAtom("sonm", Optional, OnlyOne); /* sort name */
+        ExpectChildAtom("soco", Optional, OnlyOne); /* sort composer */
+        ExpectChildAtom("sosn", Optional, OnlyOne); /* sort show */
+        ExpectChildAtom("hdvd", Optional, OnlyOne); /* HD video */
+        ExpectChildAtom("©enc", Optional, OnlyOne); /* Encoded by */
+        ExpectChildAtom("pcst", Optional, OnlyOne); /* Podcast flag */
+        ExpectChildAtom("keyw", Optional, OnlyOne); /* Keywords (for podcasts?) */
+        ExpectChildAtom("catg", Optional, OnlyOne); /* Category (for podcasts?) */
+        ExpectChildAtom("purl", Optional, OnlyOne); /* Podcast URL */
+        ExpectChildAtom("egid", Optional, OnlyOne); /* Podcast episode global unique ID */
+        ExpectChildAtom("rtng", Optional, OnlyOne); /* Content Rating */
+        ExpectChildAtom("stik", Optional, OnlyOne); /* MediaType */
+        ExpectChildAtom("\251grp", Optional, OnlyOne); /* Grouping */
+        ExpectChildAtom("\251lyr", Optional, OnlyOne); /* Lyrics */
+        ExpectChildAtom("cprt", Optional, OnlyOne); /* Copyright */
+        ExpectChildAtom("apID", Optional, OnlyOne); /* iTunes Account */
+        ExpectChildAtom("akID", Optional, OnlyOne); /* iTunes Account Type */
+        ExpectChildAtom("sfID", Optional, OnlyOne); /* iTunes Country */
+        ExpectChildAtom("cnID", Optional, OnlyOne); /* Content ID */
+        ExpectChildAtom("atID", Optional, OnlyOne); /* Artist ID */
+        ExpectChildAtom("plID", Optional, OnlyOne); /* Playlist ID */
+        ExpectChildAtom("geID", Optional, OnlyOne); /* Genre ID */
+        ExpectChildAtom("cmID", Optional, OnlyOne); /* Composer ID */
+        ExpectChildAtom("xid ", Optional, OnlyOne); /* XID */
+
+    }  else if (ATOMID(type) == ATOMID("imif")) {
+        AddVersionAndFlags();
+        AddProperty(new MP4DescriptorProperty(*this, "ipmp_desc", MP4IPMPDescrTag,
+                                              MP4IPMPDescrTag, Required, Many));
+    } else if (ATOMID(type) == ATOMID("iods")) {
+        AddVersionAndFlags();
+        AddProperty(
+            new MP4DescriptorProperty(*this, NULL, MP4FileIODescrTag,
+                                      MP4FileODescrTag,
+                                      Required, OnlyOne));
+        /*
+         * m???
+         */
+    } else if (ATOMID(type) == ATOMID("maxr")) {
+        AddProperty(new MP4Integer32Property(*this, "granularity"));
+        AddProperty(new MP4Integer32Property(*this, "bytes"));
+
+    } else if (ATOMID(type) == ATOMID("mdia")) {
+        ExpectChildAtom("mdhd", Required, OnlyOne);
+        ExpectChildAtom("hdlr", Required, OnlyOne);
+        ExpectChildAtom("minf", Required, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("meta")) { // iTunes
+        AddVersionAndFlags(); /* 0, 1 */
+        ExpectChildAtom("hdlr", Required, OnlyOne);
+        ExpectChildAtom("ilst", Required, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("mfhd")) {
+        AddVersionAndFlags();   /* 0, 1 */
+        AddProperty( /* 2 */
+            new MP4Integer32Property(*this, "sequenceNumber"));
+
+    } else if (ATOMID(type) == ATOMID("minf")) {
+        ExpectChildAtom("vmhd", Optional, OnlyOne);
+        ExpectChildAtom("smhd", Optional, OnlyOne);
+        ExpectChildAtom("hmhd", Optional, OnlyOne);
+        ExpectChildAtom("nmhd", Optional, OnlyOne);
+        ExpectChildAtom("gmhd", Optional, OnlyOne);
+        ExpectChildAtom("dinf", Required, OnlyOne);
+        ExpectChildAtom("stbl", Required, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("moof")) {
+        ExpectChildAtom("mfhd", Required, OnlyOne);
+        ExpectChildAtom("traf", Optional, Many);
+
+    } else if (ATOMID(type) == ATOMID("moov")) {
+        ExpectChildAtom("mvhd", Required, OnlyOne);
+        ExpectChildAtom("iods", Optional, OnlyOne);
+        ExpectChildAtom("trak", Required, Many);
+        ExpectChildAtom("udta", Optional, Many);
+        ExpectChildAtom("mvex", Optional, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("mvex")) {
+        ExpectChildAtom("trex", Required, Many);
+
+        /*
+         * n???
+         */
+    } else if (ATOMID(type) == ATOMID("nmhd")) {
+        AddVersionAndFlags();
+
+    } else if (ATOMID(type) == ATOMID("nump")) {
+        AddProperty( // packets sent
+            new MP4Integer64Property(*this, "packets"));
+        /*
+          * o???
+          */
+    } else if (ATOMID(type) == ATOMID("odkm")) {
+        AddVersionAndFlags();
+        ExpectChildAtom("ohdr", Required, OnlyOne);
+        /*
+         * p???
+         */
+    } else if (ATOMID(type) == ATOMID("payt")) {
+        AddProperty(new MP4Integer32Property(*this, "payloadNumber"));
+        AddProperty(new MP4StringProperty(*this, "rtpMap", Counted));
+
+    } else if (ATOMID(type) == ATOMID("pinf")) {
+        ExpectChildAtom("frma", Required, OnlyOne);
+    } else if (ATOMID(type) == ATOMID("pmax")) {
+        AddProperty( // max packet size
+            new MP4Integer32Property(*this, "bytes"));
+    } else if (ATOMID(type) == ATOMID("schi")) {
+        // not sure if this is child atoms or table of boxes
+        // get clarification on spec 9.1.2.5
+        ExpectChildAtom("odkm", Optional, OnlyOne);
+        ExpectChildAtom("iKMS", Optional, OnlyOne);
+        ExpectChildAtom("iSFM", Optional, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("schm")) {
+        AddVersionAndFlags(); /* 0, 1 */
+        AddProperty( /* 2 */
+            new MP4Integer32Property(*this, "scheme_type"));
+        AddProperty( /* 3 */
+            new MP4Integer32Property(*this, "scheme_version"));
+        // browser URI if flags set, TODO
+
+    } else if (ATOMID(type) == ATOMID("sinf")) {
+        ExpectChildAtom("frma", Required, OnlyOne);
+        ExpectChildAtom("imif", Optional, OnlyOne);
+        ExpectChildAtom("schm", Optional, OnlyOne);
+        ExpectChildAtom("schi", Optional, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("smhd")) {
+        AddVersionAndFlags();
+        AddReserved(*this, "reserved", 4);
+
+    } else if (ATOMID(type) == ATOMID("snro")) {
+        AddProperty(new MP4Integer32Property(*this, "offset"));
+
+    } else if (ATOMID(type) == ATOMID("stco")) {
+        AddVersionAndFlags();
+
+        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
+        AddProperty(pCount);
+
+        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+        AddProperty(pTable);
+
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "chunkOffset"));
+
+    } else if (ATOMID(type) == ATOMID("stsh")) {
+        AddVersionAndFlags();
+
+        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
+        AddProperty(pCount);
+
+        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+        AddProperty(pTable);
+
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "shadowedSampleNumber"));
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "syncSampleNumber"));
+
+    } else if (ATOMID(type) == ATOMID("stss")) {
+        AddVersionAndFlags();
+
+        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
+        AddProperty(pCount);
+
+        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+        AddProperty(pTable);
+
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleNumber"));
+
+    } else if (ATOMID(type) == ATOMID("stts")) {
+        AddVersionAndFlags();
+        MP4Integer32Property* pCount = new MP4Integer32Property(*this, "entryCount");
+        AddProperty(pCount);
+
+        MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+        AddProperty(pTable);
+
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleCount"));
+        pTable->AddProperty(new MP4Integer32Property(pTable->GetParentAtom(), "sampleDelta"));
+    } else if (ATOMID(type) == ATOMID("tims")) {
+        AddProperty(
+            new MP4Integer32Property(*this, "timeScale"));
+
+    } else if (ATOMID(type) == ATOMID("tmin")) {
+        AddProperty( // min relative xmit time
+            new MP4Integer32Property(*this, "milliSecs"));
+
+    } else if (ATOMID(type) == ATOMID("tmax")) {
+        AddProperty( // max relative xmit time
+            new MP4Integer32Property(*this, "milliSecs"));
+
+    } else if (ATOMID(type) == ATOMID("traf")) {
+        ExpectChildAtom("tfhd", Required, OnlyOne);
+        ExpectChildAtom("trun", Optional, Many);
+
+    } else if (ATOMID(type) == ATOMID("trak")) {
+        ExpectChildAtom("tkhd", Required, OnlyOne);
+        ExpectChildAtom("tref", Optional, OnlyOne);
+        ExpectChildAtom("edts", Optional, OnlyOne);
+        ExpectChildAtom("mdia", Required, OnlyOne);
+        ExpectChildAtom("udta", Optional, Many);
+
+    } else if (ATOMID(type) == ATOMID("tref")) {
+        ExpectChildAtom("chap", Optional, OnlyOne);
+        ExpectChildAtom("dpnd", Optional, OnlyOne);
+        ExpectChildAtom("hint", Optional, OnlyOne);
+        ExpectChildAtom("ipir", Optional, OnlyOne);
+        ExpectChildAtom("mpod", Optional, OnlyOne);
+        ExpectChildAtom("sync", Optional, OnlyOne);
+
+    } else if (ATOMID(type) == ATOMID("trex")) {
+        AddVersionAndFlags();   /* 0, 1 */
+        AddProperty( /* 2 */
+            new MP4Integer32Property(*this, "trackId"));
+        AddProperty( /* 3 */
+            new MP4Integer32Property(*this, "defaultSampleDesriptionIndex"));
+        AddProperty( /* 4 */
+            new MP4Integer32Property(*this, "defaultSampleDuration"));
+        AddProperty( /* 5 */
+            new MP4Integer32Property(*this, "defaultSampleSize"));
+        AddProperty( /* 6 */
+            new MP4Integer32Property(*this, "defaultSampleFlags"));
+
+    } else if (ATOMID(type) == ATOMID("trpy") ||
+               ATOMID(type) == ATOMID("tpyl")) {
+        AddProperty( // bytes sent including RTP headers
+            new MP4Integer64Property(*this, "bytes"));
+
+    } else if (ATOMID(type) == ATOMID("tsro")) {
+        AddProperty(
+            new MP4Integer32Property(*this, "offset"));
+    } else if (ATOMID(type) == ATOMID("wave")) {
+        ExpectChildAtom("esds", Required, OnlyOne);
+    } else {
+        /*
+         * default - unknown type
+         */
+        SetUnknownType(true);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_stbl.cpp b/src/atom_stbl.cpp
new file mode 100644
index 0000000..ca63dd9
--- /dev/null
+++ b/src/atom_stbl.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4StblAtom::MP4StblAtom(MP4File &file)
+        : MP4Atom(file, "stbl")
+{
+    ExpectChildAtom("stsd", Required, OnlyOne);
+    ExpectChildAtom("stts", Required, OnlyOne);
+    ExpectChildAtom("ctts", Optional, OnlyOne);
+    ExpectChildAtom("stsz", Required, OnlyOne);
+    ExpectChildAtom("stz2", Optional, OnlyOne);
+    ExpectChildAtom("stsc", Required, OnlyOne);
+    ExpectChildAtom("stco", Optional, OnlyOne);
+    ExpectChildAtom("co64", Optional, OnlyOne);
+    ExpectChildAtom("stss", Optional, OnlyOne);
+    ExpectChildAtom("stsh", Optional, OnlyOne);
+    ExpectChildAtom("stdp", Optional, OnlyOne);
+    ExpectChildAtom("sdtp", Optional, OnlyOne);
+}
+
+void MP4StblAtom::Generate()
+{
+    // as usual
+    MP4Atom::Generate();
+
+    // but we also need one of the chunk offset atoms
+    MP4Atom* pChunkOffsetAtom;
+    if (m_File.Use64Bits(GetType())) {
+        pChunkOffsetAtom = CreateAtom(m_File, this, "co64");
+    } else {
+        pChunkOffsetAtom = CreateAtom(m_File, this, "stco");
+    }
+
+    AddChildAtom(pChunkOffsetAtom);
+
+    // and ask it to self generate
+    pChunkOffsetAtom->Generate();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/atom_stdp.cpp b/src/atom_stdp.cpp
new file mode 100644
index 0000000..daac9fe
--- /dev/null
+++ b/src/atom_stdp.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4StdpAtom::MP4StdpAtom(MP4File &file)
+        : MP4Atom(file, "stdp")
+{
+    AddVersionAndFlags();
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "entryCount");
+    pCount->SetImplicit();
+    AddProperty(pCount);
+
+    MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+    AddProperty(pTable);
+
+    pTable->AddProperty(
+        new MP4Integer16Property(pTable->GetParentAtom(), "priority"));
+}
+
+void MP4StdpAtom::Read()
+{
+    // table entry count computed from atom size
+    ((MP4Integer32Property*)m_pProperties[2])->SetReadOnly(false);
+    ((MP4Integer32Property*)m_pProperties[2])->SetValue((m_size - 4) / 2);
+    ((MP4Integer32Property*)m_pProperties[2])->SetReadOnly(true);
+
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_stsc.cpp b/src/atom_stsc.cpp
new file mode 100644
index 0000000..6dbecfd
--- /dev/null
+++ b/src/atom_stsc.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4StscAtom::MP4StscAtom(MP4File &file)
+        : MP4Atom(file, "stsc")
+{
+    AddVersionAndFlags();
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "entryCount");
+    AddProperty(pCount);
+
+    MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+    AddProperty(pTable);
+
+    pTable->AddProperty(
+        new MP4Integer32Property(pTable->GetParentAtom(), "firstChunk"));
+    pTable->AddProperty(
+        new MP4Integer32Property(pTable->GetParentAtom(), "samplesPerChunk"));
+    pTable->AddProperty(
+        new MP4Integer32Property(pTable->GetParentAtom(), "sampleDescriptionIndex"));
+
+    // As an optimization we add an implicit property to this table,
+    // "firstSample" that corresponds to the first sample of the firstChunk
+    MP4Integer32Property* pSample =
+        new MP4Integer32Property(*this, "firstSample");
+    pSample->SetImplicit();
+    pTable->AddProperty(pSample);
+}
+
+void MP4StscAtom::Read()
+{
+    // Read as usual
+    MP4Atom::Read();
+
+    // Compute the firstSample values for later use
+    uint32_t count =
+        ((MP4Integer32Property*)m_pProperties[2])->GetValue();
+
+    MP4Integer32Property* pFirstChunk = (MP4Integer32Property*)
+                                        ((MP4TableProperty*)m_pProperties[3])->GetProperty(0);
+    MP4Integer32Property* pSamplesPerChunk = (MP4Integer32Property*)
+            ((MP4TableProperty*)m_pProperties[3])->GetProperty(1);
+    MP4Integer32Property* pFirstSample = (MP4Integer32Property*)
+                                         ((MP4TableProperty*)m_pProperties[3])->GetProperty(3);
+
+    MP4SampleId sampleId = 1;
+
+    for (uint32_t i = 0; i < count; i++) {
+        pFirstSample->SetValue(sampleId, i);
+
+        if (i < count - 1) {
+            sampleId +=
+                (pFirstChunk->GetValue(i+1) - pFirstChunk->GetValue(i))
+                * pSamplesPerChunk->GetValue(i);
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_stsd.cpp b/src/atom_stsd.cpp
new file mode 100644
index 0000000..bf995d8
--- /dev/null
+++ b/src/atom_stsd.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ *
+ * Contributor(s):
+ *      Dave Mackie         dmackie@cisco.com
+ *      Alix Marchandise-Franquet   alix@cisco.com
+ *              Ximpo Group Ltd.                mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4StsdAtom::MP4StsdAtom(MP4File &file)
+        : MP4Atom(file, "stsd")
+{
+    AddVersionAndFlags();
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "entryCount");
+    pCount->SetReadOnly();
+    AddProperty(pCount);
+
+    ExpectChildAtom("mp4a", Optional, Many);
+    ExpectChildAtom("enca", Optional, Many);
+    ExpectChildAtom("mp4s", Optional, Many);
+    ExpectChildAtom("mp4v", Optional, Many);
+    ExpectChildAtom("encv", Optional, Many);
+    ExpectChildAtom("rtp ", Optional, Many);
+    ExpectChildAtom("samr", Optional, Many); // For AMR-NB
+    ExpectChildAtom("sawb", Optional, Many); // For AMR-WB
+    ExpectChildAtom("s263", Optional, Many); // For H.263
+    ExpectChildAtom("avc1", Optional, Many);
+    ExpectChildAtom("alac", Optional, Many);
+    ExpectChildAtom("text", Optional, Many);
+    ExpectChildAtom("ac-3", Optional, Many);
+}
+
+void MP4StsdAtom::Read()
+{
+    /* do the usual read */
+    MP4Atom::Read();
+
+    // check that number of children == entryCount
+    MP4Integer32Property* pCount =
+        (MP4Integer32Property*)m_pProperties[2];
+
+    if (m_pChildAtoms.Size() != pCount->GetValue()) {
+        log.warningf("%s: \"%s\": stsd inconsistency with number of entries",
+                     __FUNCTION__, GetFile().GetFilename().c_str() );
+
+        /* fix it */
+        pCount->SetReadOnly(false);
+        pCount->SetValue(m_pChildAtoms.Size());
+        pCount->SetReadOnly(true);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_stsz.cpp b/src/atom_stsz.cpp
new file mode 100644
index 0000000..2a054fa
--- /dev/null
+++ b/src/atom_stsz.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4StszAtom::MP4StszAtom(MP4File &file)
+        : MP4Atom(file, "stsz")
+{
+    AddVersionAndFlags(); /* 0, 1 */
+
+    AddProperty( /* 2 */
+        new MP4Integer32Property(*this, "sampleSize"));
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "sampleCount");
+    AddProperty(pCount); /* 3 */
+
+    MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+    AddProperty(pTable); /* 4 */
+
+    pTable->AddProperty( /* 4/0 */
+        new MP4Integer32Property(pTable->GetParentAtom(), "entrySize"));
+}
+
+void MP4StszAtom::Read()
+{
+    ReadProperties(0, 4);
+
+    uint32_t sampleSize =
+        ((MP4Integer32Property*)m_pProperties[2])->GetValue();
+
+    // only attempt to read entries table if sampleSize is zero
+    // i.e sample size is not constant
+    m_pProperties[4]->SetImplicit(sampleSize != 0);
+
+    ReadProperties(4);
+
+    Skip(); // to end of atom
+}
+
+void MP4StszAtom::Write()
+{
+    uint32_t sampleSize =
+        ((MP4Integer32Property*)m_pProperties[2])->GetValue();
+
+    // only attempt to write entries table if sampleSize is zero
+    // i.e sample size is not constant
+    m_pProperties[4]->SetImplicit(sampleSize != 0);
+
+    MP4Atom::Write();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_stz2.cpp b/src/atom_stz2.cpp
new file mode 100644
index 0000000..30a1946
--- /dev/null
+++ b/src/atom_stz2.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*
+ * This is used for the 4 bit sample size below.  We need the sampleCount
+ * to be correct for the number of samples, but the table size needs to
+ * be correct to read and write it.
+ */
+
+class MP4HalfSizeTableProperty : public MP4TableProperty
+{
+public:
+    MP4HalfSizeTableProperty(MP4Atom& parentAtom, const char *name, MP4IntegerProperty *pCountProperty) :
+            MP4TableProperty(parentAtom, name, pCountProperty) {};
+
+    // The count is half the actual size
+    uint32_t GetCount() {
+        return (m_pCountProperty->GetValue() + 1)/ 2;
+    };
+    void SetCount(uint32_t count) {
+        m_pCountProperty->SetValue(count * 2);
+    };
+private:
+    MP4HalfSizeTableProperty();
+    MP4HalfSizeTableProperty ( const MP4HalfSizeTableProperty &src );
+    MP4HalfSizeTableProperty &operator= ( const MP4HalfSizeTableProperty &src );
+};
+
+
+MP4Stz2Atom::MP4Stz2Atom(MP4File &file)
+        : MP4Atom(file, "stz2")
+{
+    AddVersionAndFlags(); /* 0, 1 */
+
+    AddReserved(*this, "reserved", 3); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer8Property(*this, "fieldSize"));
+
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "sampleCount");
+    AddProperty(pCount); /* 4 */
+
+}
+
+void MP4Stz2Atom::Read()
+{
+    ReadProperties(0, 4);
+
+    uint8_t fieldSize =
+        ((MP4Integer8Property *)m_pProperties[3])->GetValue();
+    //  uint32_t sampleCount = 0;
+
+    MP4Integer32Property* pCount =
+        (MP4Integer32Property *)m_pProperties[4];
+
+    MP4TableProperty *pTable;
+    if (fieldSize != 4) {
+        pTable = new MP4TableProperty(*this, "entries", pCount);
+    } else {
+        // 4 bit field size uses a special table.
+        pTable = new MP4HalfSizeTableProperty(*this, "entries", pCount);
+    }
+
+    AddProperty(pTable);
+
+    if (fieldSize == 16) {
+        pTable->AddProperty( /* 5/0 */
+            new MP4Integer16Property(*this, "entrySize"));
+    } else {
+        pTable->AddProperty( /* 5/0 */
+            new MP4Integer8Property(*this, "entrySize"));
+    }
+
+    ReadProperties(4);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_text.cpp b/src/atom_text.cpp
new file mode 100644
index 0000000..454d405
--- /dev/null
+++ b/src/atom_text.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ *
+ * Contributer has declined to give copyright information, and gives
+ * it freely to the world.
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4TextAtom::MP4TextAtom(MP4File &file)
+        : MP4Atom(file, "text")
+{
+    // The atom type "text" is used in two complete unrelated ways
+    // i.e. it's real two atoms with the same name
+    // To handle that we need to postpone property creation until
+    // we know who our parent atom is (stsd or gmhd) which gives us
+    // the context info we need to know who we are
+}
+
+void MP4TextAtom::AddPropertiesStsdType()
+{
+
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty(new MP4Integer16Property(*this, "dataReferenceIndex"));/* 1 */
+
+    AddProperty(new MP4Integer32Property(*this, "displayFlags")); /* 2 */
+    AddProperty(new MP4Integer32Property(*this, "textJustification")); /* 3 */
+
+    AddProperty(new MP4Integer16Property(*this, "bgColorRed")); /* 4 */
+    AddProperty(new MP4Integer16Property(*this, "bgColorGreen")); /* 5 */
+    AddProperty(new MP4Integer16Property(*this, "bgColorBlue")); /* 6 */
+
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxTop")); /* 7 */
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxLeft")); /* 8 */
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxBottom")); /* 9 */
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxRight")); /* 10 */
+
+    AddReserved(*this, "reserved2", 8); /* 11 */
+
+    AddProperty(new MP4Integer16Property(*this, "fontNumber")); /* 12 */
+    AddProperty(new MP4Integer16Property(*this, "fontFace")); /* 13 */
+
+    AddReserved(*this, "reserved3", 1); /* 14 */
+    AddReserved(*this, "reserved4", 2); /* 15 */
+
+    AddProperty(new MP4Integer16Property(*this, "foreColorRed")); /* 16 */
+    AddProperty(new MP4Integer16Property(*this, "foreColorGreen")); /* 17 */
+    AddProperty(new MP4Integer16Property(*this, "foreColorBlue")); /* 18 */
+
+}
+
+void MP4TextAtom::AddPropertiesGmhdType()
+{
+
+    AddProperty(new MP4BytesProperty(*this, "textData", 36)); /* 0 */
+
+}
+
+
+void MP4TextAtom::Generate()
+{
+    ASSERT(m_pParentAtom);
+    if (ATOMID(m_pParentAtom->GetType()) == ATOMID("stsd")) {
+        AddPropertiesStsdType();
+        GenerateStsdType();
+    } else if (ATOMID(m_pParentAtom->GetType()) == ATOMID("gmhd")) {
+        AddPropertiesGmhdType();
+        GenerateGmhdType();
+    } else {
+        log.warningf("%s: \"%s\": text atom in unexpected context, can not generate", __FUNCTION__,
+                     GetFile().GetFilename().c_str());
+    }
+
+}
+
+void MP4TextAtom::GenerateStsdType()
+{
+    // generate children
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    ((MP4Integer32Property*)m_pProperties[2])->SetValue(1);
+    ((MP4Integer32Property*)m_pProperties[3])->SetValue(1);
+
+}
+
+void MP4TextAtom::GenerateGmhdType()
+{
+    MP4Atom::Generate();
+
+    // property 0 has non-zero fixed values
+    static uint8_t textData[36] = {
+        0x00, 0x01,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x01,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x00, 0x00,
+        0x40, 0x00,
+        0x00, 0x00,
+    };
+    ((MP4BytesProperty*)m_pProperties[0])->SetValue(textData, sizeof(textData));
+
+}
+
+void MP4TextAtom::Read ()
+{
+    if (ATOMID(m_pParentAtom->GetType()) == ATOMID("stsd")) {
+        AddPropertiesStsdType();
+    } else if (ATOMID(m_pParentAtom->GetType()) == ATOMID("gmhd")) {
+        AddPropertiesGmhdType();
+    }
+
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_tfhd.cpp b/src/atom_tfhd.cpp
new file mode 100644
index 0000000..ddfc579
--- /dev/null
+++ b/src/atom_tfhd.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4TfhdAtom::MP4TfhdAtom(MP4File &file)
+        : MP4Atom(file, "tfhd")
+{
+    AddVersionAndFlags();   /* 0, 1 */
+    AddProperty( /* 2 */
+        new MP4Integer32Property(*this, "trackId"));
+}
+
+void MP4TfhdAtom::AddProperties(uint32_t flags)
+{
+    if (flags & 0x01) {
+        // note this property is signed 64!
+        AddProperty(
+            new MP4Integer64Property(*this, "baseDataOffset"));
+    }
+    if (flags & 0x02) {
+        AddProperty(
+            new MP4Integer32Property(*this, "sampleDescriptionIndex"));
+    }
+    if (flags & 0x08) {
+        AddProperty(
+            new MP4Integer32Property(*this, "defaultSampleDuration"));
+    }
+    if (flags & 0x10) {
+        AddProperty(
+            new MP4Integer32Property(*this, "defaultSampleSize"));
+    }
+    if (flags & 0x20) {
+        AddProperty(
+            new MP4Integer32Property(*this, "defaultSampleFlags"));
+    }
+}
+
+void MP4TfhdAtom::Read()
+{
+    /* read atom version, flags, and trackId */
+    ReadProperties(0, 3);
+
+    /* need to create the properties based on the atom flags */
+    AddProperties(GetFlags());
+
+    /* now we can read the remaining properties */
+    ReadProperties(3);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_tkhd.cpp b/src/atom_tkhd.cpp
new file mode 100644
index 0000000..3e9de35
--- /dev/null
+++ b/src/atom_tkhd.cpp
@@ -0,0 +1,145 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4TkhdAtom::MP4TkhdAtom(MP4File &file)
+        : MP4Atom(file, "tkhd")
+{
+    AddVersionAndFlags();
+}
+
+void MP4TkhdAtom::AddProperties(uint8_t version)
+{
+    if (version == 1) {
+        AddProperty( /* 2 */
+            new MP4Integer64Property(*this, "creationTime"));
+        AddProperty( /* 3 */
+            new MP4Integer64Property(*this, "modificationTime"));
+    } else { // version == 0
+        AddProperty( /* 2 */
+            new MP4Integer32Property(*this, "creationTime"));
+        AddProperty( /* 3 */
+            new MP4Integer32Property(*this, "modificationTime"));
+    }
+
+    AddProperty( /* 4 */
+        new MP4Integer32Property(*this, "trackId"));
+    AddReserved(*this, "reserved1", 4); /* 5 */
+
+    if (version == 1) {
+        AddProperty( /* 6 */
+            new MP4Integer64Property(*this, "duration"));
+    } else {
+        AddProperty( /* 6 */
+            new MP4Integer32Property(*this, "duration"));
+    }
+
+    AddReserved(*this, "reserved2", 8); /* 7 */
+
+    AddProperty( /* 8 */
+        new MP4Integer16Property(*this, "layer"));
+    AddProperty( /* 9 */
+        new MP4Integer16Property(*this, "alternate_group"));
+
+    MP4Float32Property* pProp;
+
+    pProp = new MP4Float32Property(*this, "volume");
+    pProp->SetFixed16Format();
+    AddProperty(pProp); /* 10 */
+
+    AddReserved(*this, "reserved3", 2); /* 11 */
+
+    AddProperty(new MP4BytesProperty(*this, "matrix", 36)); /* 12 */
+
+    pProp = new MP4Float32Property(*this, "width");
+    pProp->SetFixed32Format();
+    AddProperty(pProp); /* 13 */
+
+    pProp = new MP4Float32Property(*this, "height");
+    pProp->SetFixed32Format();
+    AddProperty(pProp); /* 14 */
+}
+
+void MP4TkhdAtom::Generate()
+{
+    uint8_t version = m_File.Use64Bits(GetType()) ? 1 : 0;
+    SetVersion(version);
+    AddProperties(version);
+
+    MP4Atom::Generate();
+
+    // set creation and modification times
+    MP4Timestamp now = MP4GetAbsTimestamp();
+    if (version == 1) {
+        ((MP4Integer64Property*)m_pProperties[2])->SetValue(now);
+        ((MP4Integer64Property*)m_pProperties[3])->SetValue(now);
+    } else {
+        ((MP4Integer32Property*)m_pProperties[2])->SetValue(now);
+        ((MP4Integer32Property*)m_pProperties[3])->SetValue(now);
+    }
+
+    // property "matrix" has non-zero fixed values
+    // this default identity matrix indicates no transformation, i.e.
+    // 1, 0, 0
+    // 0, 1, 0
+    // 0, 0, 1
+    // see http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap4/chapter_5_section_4.html
+
+    static uint8_t matrix[36] = {
+        0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x40, 0x00, 0x00, 0x00,
+    };
+
+    ((MP4BytesProperty*)m_pProperties[12])->
+    SetValue(matrix, sizeof(matrix));
+}
+
+void MP4TkhdAtom::Read()
+{
+    /* read atom version */
+    ReadProperties(0, 1);
+
+    /* need to create the properties based on the atom version */
+    AddProperties(GetVersion());
+
+    /* now we can read the remaining properties */
+    ReadProperties(1);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_treftype.cpp b/src/atom_treftype.cpp
new file mode 100644
index 0000000..6a7ff92
--- /dev/null
+++ b/src/atom_treftype.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4TrefTypeAtom::MP4TrefTypeAtom(MP4File &file, const char* type)
+        : MP4Atom(file, type)
+{
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(*this, "entryCount");
+    pCount->SetImplicit();
+    AddProperty(pCount); /* 0 */
+
+    MP4TableProperty* pTable = new MP4TableProperty(*this, "entries", pCount);
+    AddProperty(pTable); /* 1 */
+
+    pTable->AddProperty( /* 1, 0 */
+        new MP4Integer32Property(pTable->GetParentAtom(), "trackId"));
+}
+
+void MP4TrefTypeAtom::Read()
+{
+    // table entry count computed from atom size
+    ((MP4Integer32Property*)m_pProperties[0])->SetReadOnly(false);
+    ((MP4Integer32Property*)m_pProperties[0])->SetValue(m_size / 4);
+    ((MP4Integer32Property*)m_pProperties[0])->SetReadOnly(true);
+
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_trun.cpp b/src/atom_trun.cpp
new file mode 100644
index 0000000..765e417
--- /dev/null
+++ b/src/atom_trun.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4TrunAtom::MP4TrunAtom(MP4File &file)
+        : MP4Atom(file, "trun")
+{
+    AddVersionAndFlags();   /* 0, 1 */
+    AddProperty( /* 2 */
+        new MP4Integer32Property(*this, "sampleCount"));
+}
+
+void MP4TrunAtom::AddProperties(uint32_t flags)
+{
+    if (flags & 0x01) {
+        // Note this is a signed 32 value
+        AddProperty(
+            new MP4Integer32Property(*this, "dataOffset"));
+    }
+    if (flags & 0x04) {
+        AddProperty(
+            new MP4Integer32Property(*this, "firstSampleFlags"));
+    }
+
+    MP4TableProperty* pTable =
+        new MP4TableProperty(*this, "samples",
+                             (MP4Integer32Property *)m_pProperties[2]);
+    AddProperty(pTable);
+
+    if (flags & 0x100) {
+        pTable->AddProperty(
+            new MP4Integer32Property(*this, "sampleDuration"));
+    }
+    if (flags & 0x200) {
+        pTable->AddProperty(
+            new MP4Integer32Property(*this, "sampleSize"));
+    }
+    if (flags & 0x400) {
+        pTable->AddProperty(
+            new MP4Integer32Property(*this, "sampleFlags"));
+    }
+    if (flags & 0x800) {
+        pTable->AddProperty(
+            new MP4Integer32Property(*this, "sampleCompositionTimeOffset"));
+    }
+}
+
+void MP4TrunAtom::Read()
+{
+    /* read atom version, flags, and sampleCount */
+    ReadProperties(0, 3);
+
+    /* need to create the properties based on the atom flags */
+    AddProperties(GetFlags());
+
+    /* now we can read the remaining properties */
+    ReadProperties(3);
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_tx3g.cpp b/src/atom_tx3g.cpp
new file mode 100644
index 0000000..a54dd6b
--- /dev/null
+++ b/src/atom_tx3g.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.  All Rights Reserved.
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Tx3gAtom::MP4Tx3gAtom(MP4File &file)
+        : MP4Atom(file, "tx3g")
+{
+    AddReserved(*this, "reserved1", 4); /* 0 */
+    AddReserved(*this, "reserved2", 2); /* 1 */
+
+    AddProperty(new MP4Integer16Property(*this, "dataReferenceIndex"));/* 2 */
+
+    AddProperty(new MP4Integer32Property(*this, "displayFlags")); /* 3 */
+    AddProperty(new MP4Integer8Property(*this, "horizontalJustification")); /* 4 */
+    AddProperty(new MP4Integer8Property(*this, "verticalJustification")); /* 5 */
+
+    AddProperty(new MP4Integer8Property(*this, "bgColorRed")); /* 6 */
+    AddProperty(new MP4Integer8Property(*this, "bgColorGreen")); /* 7 */
+    AddProperty(new MP4Integer8Property(*this, "bgColorBlue")); /* 8 */
+    AddProperty(new MP4Integer8Property(*this, "bgColorAlpha")); /* 9 */
+
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxTop")); /* 10 */
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxLeft")); /* 11 */
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxBottom")); /* 12 */
+    AddProperty(new MP4Integer16Property(*this, "defTextBoxRight")); /* 13 */
+
+    AddProperty(new MP4Integer16Property(*this, "startChar")); /* 14 */
+    AddProperty(new MP4Integer16Property(*this, "endChar")); /* 15 */
+    AddProperty(new MP4Integer16Property(*this, "fontID")); /* 16 */
+    AddProperty(new MP4Integer8Property(*this, "fontFace")); /* 17 */
+    AddProperty(new MP4Integer8Property(*this, "fontSize")); /* 18 */
+
+    AddProperty(new MP4Integer8Property(*this, "fontColorRed")); /* 19 */
+    AddProperty(new MP4Integer8Property(*this, "fontColorGreen")); /* 20 */
+    AddProperty(new MP4Integer8Property(*this, "fontColorBlue")); /* 21 */
+    AddProperty(new MP4Integer8Property(*this, "fontColorAlpha")); /* 22 */
+
+    ExpectChildAtom("ftab", Optional, Many);
+}
+
+void MP4Tx3gAtom::Generate()
+{
+    // generate children
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_udta.cpp b/src/atom_udta.cpp
new file mode 100644
index 0000000..eff4dfa
--- /dev/null
+++ b/src/atom_udta.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4UdtaAtom::MP4UdtaAtom(MP4File &file)
+        : MP4Atom(file, "udta")
+{
+    ExpectChildAtom("chpl", Optional, OnlyOne);
+    ExpectChildAtom("cprt", Optional, Many);
+    ExpectChildAtom("hnti", Optional, OnlyOne);
+    ExpectChildAtom("meta", Optional, OnlyOne);
+    ExpectChildAtom("\251cpy", Optional, OnlyOne);
+    ExpectChildAtom("\251des", Optional, OnlyOne);
+    ExpectChildAtom("\251nam", Optional, OnlyOne);
+    ExpectChildAtom("\251cmt", Optional, OnlyOne);
+    ExpectChildAtom("\251prd", Optional, OnlyOne);
+}
+
+void MP4UdtaAtom::Read()
+{
+    if (ATOMID(m_pParentAtom->GetType()) == ATOMID("trak")) {
+        ExpectChildAtom("hinf", Optional, OnlyOne);
+        ExpectChildAtom("name", Optional, OnlyOne);
+    }
+
+    MP4Atom::Read();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_url.cpp b/src/atom_url.cpp
new file mode 100644
index 0000000..4d47d34
--- /dev/null
+++ b/src/atom_url.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4UrlAtom::MP4UrlAtom(MP4File &file, const char *type)
+        : MP4Atom(file, type)
+{
+    AddVersionAndFlags();
+    AddProperty(new MP4StringProperty(*this, "location"));
+}
+
+void MP4UrlAtom::Read()
+{
+    // read the version and flags properties
+    ReadProperties(0, 2);
+
+    // check if self-contained flag is set
+    if (!(GetFlags() & 1)) {
+        // if not then read url location
+        ReadProperties(2);
+    }
+
+    Skip(); // to end of atom
+}
+
+void MP4UrlAtom::Write()
+{
+    MP4StringProperty* pLocationProp =
+        (MP4StringProperty*)m_pProperties[2];
+
+    // if no url location has been set
+    // then set self-contained flag
+    // and don't attempt to write anything
+    if (pLocationProp->GetValue() == NULL) {
+        SetFlags(GetFlags() | 1);
+        pLocationProp->SetImplicit(true);
+    } else {
+        SetFlags(GetFlags() & 0xFFFFFE);
+        pLocationProp->SetImplicit(false);
+    }
+
+    // write atom as usual
+    MP4Atom::Write();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_urn.cpp b/src/atom_urn.cpp
new file mode 100644
index 0000000..f3da3e8
--- /dev/null
+++ b/src/atom_urn.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4UrnAtom::MP4UrnAtom(MP4File &file)
+        : MP4Atom(file, "urn ")
+{
+    AddVersionAndFlags();
+    AddProperty(new MP4StringProperty(*this, "name"));
+    AddProperty(new MP4StringProperty(*this, "location"));
+}
+
+void MP4UrnAtom::Read()
+{
+    // read the version, flags, and name properties
+    ReadProperties(0, 3);
+
+    // check if location is present
+    if (m_File.GetPosition() < GetEnd()) {
+        // read it
+        ReadProperties(3);
+    }
+
+    Skip(); // to end of atom
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_uuid.cpp b/src/atom_uuid.cpp
new file mode 100644
index 0000000..c283960
--- /dev/null
+++ b/src/atom_uuid.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+IPodUUIDAtom::IPodUUIDAtom(MP4File &file)
+        : MP4Atom(file, "uuid")
+{
+    //
+    // This is a hack, the contents of this atom need to be well defined.
+    //
+    static uint8_t ipod_magic[] = {
+        0x6b, 0x68, 0x40, 0xf2, 0x5f, 0x24, 0x4f, 0xc5,
+        0xba, 0x39, 0xa5, 0x1b, 0xcf, 0x03, 0x23, 0xf3
+    };
+
+    SetExtendedType(ipod_magic);
+
+    MP4Integer32Property* value = new MP4Integer32Property(*this, "value");
+    value->SetValue(1);
+    AddProperty(value);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_video.cpp b/src/atom_video.cpp
new file mode 100644
index 0000000..aeabd73
--- /dev/null
+++ b/src/atom_video.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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. 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Bill May  wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4VideoAtom::MP4VideoAtom (MP4File &file, const char *type)
+        : MP4Atom(file, type)
+{
+    AddReserved(*this, "reserved1", 6); /* 0 */
+
+    AddProperty( /* 1 */
+        new MP4Integer16Property(*this, "dataReferenceIndex"));
+
+    AddReserved(*this, "reserved2", 16); /* 2 */
+
+    AddProperty( /* 3 */
+        new MP4Integer16Property(*this, "width"));
+    AddProperty( /* 4 */
+        new MP4Integer16Property(*this, "height"));
+
+    AddReserved(*this, "reserved3", 14); /* 5 */
+
+    MP4StringProperty* pProp =
+        new MP4StringProperty(*this, "compressorName");
+    pProp->SetFixedLength(32);
+    pProp->SetCountedFormat(true);
+    pProp->SetValue("");
+    AddProperty(pProp); /* 6 */
+
+    AddProperty(/* 7 */
+        new MP4Integer16Property(*this, "depth"));
+    AddProperty(/* 8 */
+        new MP4Integer16Property(*this, "colorTableId"));
+    ExpectChildAtom("smi ", Optional, OnlyOne);
+}
+
+void MP4VideoAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    ((MP4Integer16Property*)m_pProperties[1])->SetValue(1);
+
+    // property reserved3 has non-zero fixed values
+    static uint8_t reserved3[14] = {
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x48, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x01,
+    };
+    m_pProperties[5]->SetReadOnly(false);
+    ((MP4BytesProperty*)m_pProperties[5])->
+    SetValue(reserved3, sizeof(reserved3));
+    m_pProperties[5]->SetReadOnly(true);
+
+    // depth and color table id values - should be set later
+    // as far as depth - color table is most likely 0xff
+    ((MP4IntegerProperty *)m_pProperties[7])->SetValue(0x18);
+    ((MP4IntegerProperty *)m_pProperties[8])->SetValue(0xffff);
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atom_vmhd.cpp b/src/atom_vmhd.cpp
new file mode 100644
index 0000000..eb06a26
--- /dev/null
+++ b/src/atom_vmhd.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4VmhdAtom::MP4VmhdAtom(MP4File &file)
+        : MP4Atom(file, "vmhd")
+{
+    AddVersionAndFlags();
+    AddReserved(*this, "reserved", 8);
+}
+
+void MP4VmhdAtom::Generate()
+{
+    MP4Atom::Generate();
+
+    SetFlags(1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/atoms.h b/src/atoms.h
new file mode 100644
index 0000000..382706f
--- /dev/null
+++ b/src/atoms.h
@@ -0,0 +1,829 @@
+/*
+ * 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 - 2005.  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.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ *              Ximpo Group Ltd.                mp4v2@ximpo.com
+ *              Bill May                wmay@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_ATOMS_H
+#define MP4V2_IMPL_ATOMS_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4FtypAtom;
+class MP4FreeAtom;
+
+/// ISO base media full-atom.
+class MP4FullAtom : public MP4Atom
+{
+public:
+    MP4FullAtom( MP4File &file, const char* type );
+
+    MP4Integer8Property&  version;
+    MP4Integer24Property& flags;
+private:
+    MP4FullAtom();
+    MP4FullAtom( const MP4FullAtom &src );
+    MP4FullAtom &operator= ( const MP4FullAtom &src );
+};
+
+// declare all the atom subclasses
+// i.e. spare us atom_xxxx.h for all the atoms
+//
+// The majority of atoms just need their own constructor declared
+// Some atoms have a few special needs
+// A small minority of atoms need lots of special handling
+
+class MP4RootAtom : public MP4Atom
+{
+public:
+    MP4RootAtom(MP4File &file);
+    void BeginWrite(bool use64 = false);
+    void Write();
+    void FinishWrite(bool use64 = false);
+
+    void BeginOptimalWrite();
+    void FinishOptimalWrite();
+
+protected:
+    uint32_t GetLastMdatIndex();
+    void WriteAtomType(const char* type, bool onlyOne);
+
+private:
+    MP4RootAtom();
+    MP4RootAtom( const MP4RootAtom &src );
+    MP4RootAtom &operator= ( const MP4RootAtom &src );
+
+    MP4FtypAtom* m_rewrite_ftyp;
+    uint64_t     m_rewrite_ftypPosition;
+    MP4FreeAtom* m_rewrite_free;
+    uint64_t     m_rewrite_freePosition;
+};
+
+/***********************************************************************
+ * Common atom classes - standard for anything that just contains atoms
+ * and non-maleable properties, treftype and url
+ ***********************************************************************/
+class MP4StandardAtom : public MP4Atom {
+public:
+    MP4StandardAtom(MP4File &file, const char *name);
+private:
+    MP4StandardAtom();
+    MP4StandardAtom( const MP4StandardAtom &src );
+    MP4StandardAtom &operator= ( const MP4StandardAtom &src );
+};
+
+class MP4TrefTypeAtom : public MP4Atom {
+public:
+    MP4TrefTypeAtom(MP4File &file, const char* type);
+    void Read();
+private:
+    MP4TrefTypeAtom();
+    MP4TrefTypeAtom( const MP4TrefTypeAtom &src );
+    MP4TrefTypeAtom &operator= ( const MP4TrefTypeAtom &src );
+};
+
+class MP4UrlAtom : public MP4Atom {
+public:
+    MP4UrlAtom(MP4File &file, const char *type="url ");
+    void Read();
+    void Write();
+private:
+    MP4UrlAtom();
+    MP4UrlAtom( const MP4UrlAtom &src );
+    MP4UrlAtom &operator= ( const MP4UrlAtom &src );
+};
+
+/***********************************************************************
+ * Sound and Video atoms - use the generic atoms when possible
+ * (MP4SoundAtom and MP4VideoAtom)
+ ***********************************************************************/
+class MP4SoundAtom : public MP4Atom {
+public:
+    MP4SoundAtom(MP4File &file, const char *atomid);
+    void Generate();
+    void Read();
+protected:
+    void AddProperties(uint8_t version);
+private:
+    MP4SoundAtom();
+    MP4SoundAtom( const MP4SoundAtom &src );
+    MP4SoundAtom &operator= ( const MP4SoundAtom &src );
+};
+
+class MP4VideoAtom : public MP4Atom {
+public:
+    MP4VideoAtom(MP4File &file, const char *atomid);
+    void Generate();
+private:
+    MP4VideoAtom();
+    MP4VideoAtom( const MP4VideoAtom &src );
+    MP4VideoAtom &operator= ( const MP4VideoAtom &src );
+};
+
+class MP4AmrAtom : public MP4Atom {
+public:
+    MP4AmrAtom(MP4File &file, const char *type);
+    void Generate();
+private:
+    MP4AmrAtom();
+    MP4AmrAtom( const MP4AmrAtom &src );
+    MP4AmrAtom &operator= ( const MP4AmrAtom &src );
+};
+
+// H.264 atoms
+
+class MP4Avc1Atom : public MP4Atom {
+public:
+    MP4Avc1Atom(MP4File &file);
+    void Generate();
+private:
+    MP4Avc1Atom();
+    MP4Avc1Atom( const MP4Avc1Atom &src );
+    MP4Avc1Atom &operator= ( const MP4Avc1Atom &src );
+};
+
+class MP4AvcCAtom : public MP4Atom {
+public:
+    MP4AvcCAtom(MP4File &file);
+    void Generate();
+    void Clone(MP4AvcCAtom *dstAtom);
+private:
+    MP4AvcCAtom();
+    MP4AvcCAtom( const MP4AvcCAtom &src );
+    MP4AvcCAtom &operator= ( const MP4AvcCAtom &src );
+};
+
+
+class MP4D263Atom : public MP4Atom {
+public:
+    MP4D263Atom(MP4File &file);
+    void Generate();
+    void Write();
+private:
+    MP4D263Atom();
+    MP4D263Atom( const MP4D263Atom &src );
+    MP4D263Atom &operator= ( const MP4D263Atom &src );
+};
+
+class MP4DamrAtom : public MP4Atom {
+public:
+    MP4DamrAtom(MP4File &file);
+    void Generate();
+private:
+    MP4DamrAtom();
+    MP4DamrAtom( const MP4DamrAtom &src );
+    MP4DamrAtom &operator= ( const MP4DamrAtom &src );
+};
+
+class MP4EncaAtom : public MP4Atom {
+public:
+    MP4EncaAtom(MP4File &file);
+    void Generate();
+private:
+    MP4EncaAtom();
+    MP4EncaAtom( const MP4EncaAtom &src );
+    MP4EncaAtom &operator= ( const MP4EncaAtom &src );
+};
+
+class MP4EncvAtom : public MP4Atom {
+public:
+    MP4EncvAtom(MP4File &file);
+    void Generate();
+private:
+    MP4EncvAtom();
+    MP4EncvAtom( const MP4EncvAtom &src );
+    MP4EncvAtom &operator= ( const MP4EncvAtom &src );
+};
+
+class MP4Mp4aAtom : public MP4Atom {
+public:
+    MP4Mp4aAtom(MP4File &file);
+    void Generate();
+private:
+    MP4Mp4aAtom();
+    MP4Mp4aAtom( const MP4Mp4aAtom &src );
+    MP4Mp4aAtom &operator= ( const MP4Mp4aAtom &src );
+};
+
+class MP4Ac3Atom : public MP4Atom {
+public:
+    MP4Ac3Atom(MP4File &file);
+    void Generate();
+private:
+    MP4Ac3Atom();
+    MP4Ac3Atom( const MP4Ac3Atom &src );
+    MP4Ac3Atom &operator= ( const MP4Ac3Atom &src );
+};
+
+class MP4DAc3Atom : public MP4Atom {
+public:
+    MP4DAc3Atom(MP4File &file);
+    void Generate();
+    void Dump(uint8_t indent, bool dumpImplicits);
+private:
+    MP4DAc3Atom();
+    MP4DAc3Atom( const MP4DAc3Atom &src );
+    MP4DAc3Atom &operator= ( const MP4DAc3Atom &src );
+};
+
+class MP4Mp4sAtom : public MP4Atom {
+public:
+    MP4Mp4sAtom(MP4File &file);
+    void Generate();
+private:
+    MP4Mp4sAtom();
+    MP4Mp4sAtom( const MP4Mp4sAtom &src );
+    MP4Mp4sAtom &operator= ( const MP4Mp4sAtom &src );
+};
+
+class MP4Mp4vAtom : public MP4Atom {
+public:
+    MP4Mp4vAtom(MP4File &file);
+    void Generate();
+private:
+    MP4Mp4vAtom();
+    MP4Mp4vAtom( const MP4Mp4vAtom &src );
+    MP4Mp4vAtom &operator= ( const MP4Mp4vAtom &src );
+};
+
+
+class MP4S263Atom : public MP4Atom {
+public:
+    MP4S263Atom(MP4File &file);
+    void Generate();
+private:
+    MP4S263Atom();
+    MP4S263Atom( const MP4S263Atom &src );
+    MP4S263Atom &operator= ( const MP4S263Atom &src );
+};
+
+
+
+/************************************************************************
+ * Specialized Atoms
+ ************************************************************************/
+
+class MP4DrefAtom : public MP4Atom {
+public:
+    MP4DrefAtom(MP4File &file);
+    void Read();
+private:
+    MP4DrefAtom();
+    MP4DrefAtom( const MP4DrefAtom &src );
+    MP4DrefAtom &operator= ( const MP4DrefAtom &src );
+};
+
+class MP4ElstAtom : public MP4Atom {
+public:
+    MP4ElstAtom(MP4File &file);
+    void Generate();
+    void Read();
+protected:
+    void AddProperties(uint8_t version);
+private:
+    MP4ElstAtom();
+    MP4ElstAtom( const MP4ElstAtom &src );
+    MP4ElstAtom &operator= ( const MP4ElstAtom &src );
+};
+
+class MP4FreeAtom : public MP4Atom {
+public:
+    MP4FreeAtom( MP4File &file, const char* = NULL );
+    void Read();
+    void Write();
+private:
+    MP4FreeAtom();
+    MP4FreeAtom( const MP4FreeAtom &src );
+    MP4FreeAtom &operator= ( const MP4FreeAtom &src );
+};
+
+class MP4FtypAtom : public MP4Atom {
+public:
+    MP4FtypAtom(MP4File &file);
+    void Generate();
+    void Read();
+
+    MP4StringProperty&    majorBrand;
+    MP4Integer32Property& minorVersion;
+    MP4StringProperty&    compatibleBrands;
+private:
+    MP4FtypAtom();
+    MP4FtypAtom( const MP4FtypAtom &src );
+    MP4FtypAtom &operator= ( const MP4FtypAtom &src );
+};
+
+class MP4GminAtom : public MP4Atom {
+public:
+    MP4GminAtom(MP4File &file);
+    void Generate();
+private:
+    MP4GminAtom();
+    MP4GminAtom( const MP4GminAtom &src );
+    MP4GminAtom &operator= ( const MP4GminAtom &src );
+};
+
+class MP4HdlrAtom : public MP4Atom {
+public:
+    MP4HdlrAtom(MP4File &file);
+    void Read();
+private:
+    MP4HdlrAtom();
+    MP4HdlrAtom( const MP4HdlrAtom &src );
+    MP4HdlrAtom &operator= ( const MP4HdlrAtom &src );
+};
+
+class MP4HinfAtom : public MP4Atom {
+public:
+    MP4HinfAtom(MP4File &file);
+    void Generate();
+private:
+    MP4HinfAtom();
+    MP4HinfAtom( const MP4HinfAtom &src );
+    MP4HinfAtom &operator= ( const MP4HinfAtom &src );
+};
+
+class MP4HntiAtom : public MP4Atom {
+public:
+    MP4HntiAtom(MP4File &file);
+    void Read();
+private:
+    MP4HntiAtom();
+    MP4HntiAtom( const MP4HntiAtom &src );
+    MP4HntiAtom &operator= ( const MP4HntiAtom &src );
+};
+
+
+class MP4MdatAtom : public MP4Atom {
+public:
+    MP4MdatAtom(MP4File &file);
+    void Read();
+    void Write();
+private:
+    MP4MdatAtom();
+    MP4MdatAtom( const MP4MdatAtom &src );
+    MP4MdatAtom &operator= ( const MP4MdatAtom &src );
+};
+
+class MP4MdhdAtom : public MP4Atom {
+public:
+    MP4MdhdAtom(MP4File &file);
+    void Generate();
+    void Read();
+protected:
+    void AddProperties(uint8_t version);
+private:
+    MP4MdhdAtom();
+    MP4MdhdAtom( const MP4MdhdAtom &src );
+    MP4MdhdAtom &operator= ( const MP4MdhdAtom &src );
+};
+
+class MP4MvhdAtom : public MP4Atom {
+public:
+    MP4MvhdAtom(MP4File &file);
+    void Generate();
+    void Read();
+protected:
+    void AddProperties(uint8_t version);
+private:
+    MP4MvhdAtom();
+    MP4MvhdAtom( const MP4MvhdAtom &src );
+    MP4MvhdAtom &operator= ( const MP4MvhdAtom &src );
+};
+
+class MP4OhdrAtom : public MP4Atom {
+public:
+    MP4OhdrAtom(MP4File &file);
+    ~MP4OhdrAtom();
+    void Read();
+private:
+    MP4OhdrAtom();
+    MP4OhdrAtom( const MP4OhdrAtom &src );
+    MP4OhdrAtom &operator= ( const MP4OhdrAtom &src );
+};
+
+class MP4RtpAtom : public MP4Atom {
+public:
+    MP4RtpAtom(MP4File &file);
+    void Generate();
+    void Read();
+    void Write();
+
+protected:
+    void AddPropertiesStsdType();
+    void AddPropertiesHntiType();
+
+    void GenerateStsdType();
+    void GenerateHntiType();
+
+    void ReadStsdType();
+    void ReadHntiType();
+
+    void WriteHntiType();
+
+private:
+    MP4RtpAtom();
+    MP4RtpAtom( const MP4RtpAtom &src );
+    MP4RtpAtom &operator= ( const MP4RtpAtom &src );
+};
+
+class MP4SdpAtom : public MP4Atom {
+public:
+    MP4SdpAtom(MP4File &file);
+    void Read();
+    void Write();
+private:
+    MP4SdpAtom();
+    MP4SdpAtom( const MP4SdpAtom &src );
+    MP4SdpAtom &operator= ( const MP4SdpAtom &src );
+};
+
+// sdtp - Independent and Disposable Samples Atom.
+class MP4SdtpAtom : public MP4FullAtom {
+public:
+    MP4SdtpAtom(MP4File &file);
+    void Read();
+
+    // raw bytes; one byte for each sample.
+    // number of bytes == stsz.sampleCount.
+    MP4BytesProperty& data;
+private:
+    MP4SdtpAtom();
+    MP4SdtpAtom( const MP4SdtpAtom &src );
+    MP4SdtpAtom &operator= ( const MP4SdtpAtom &src );
+};
+
+class MP4SmiAtom : public MP4Atom {
+public:
+    MP4SmiAtom(MP4File &file);
+    void Read();
+private:
+    MP4SmiAtom();
+    MP4SmiAtom( const MP4SmiAtom &src );
+    MP4SmiAtom &operator= ( const MP4SmiAtom &src );
+};
+
+class MP4StblAtom : public MP4Atom {
+public:
+    MP4StblAtom(MP4File &file);
+    void Generate();
+private:
+    MP4StblAtom();
+    MP4StblAtom( const MP4StblAtom &src );
+    MP4StblAtom &operator= ( const MP4StblAtom &src );
+};
+
+class MP4StdpAtom : public MP4Atom {
+public:
+    MP4StdpAtom(MP4File &file);
+    void Read();
+private:
+    MP4StdpAtom();
+    MP4StdpAtom( const MP4StdpAtom &src );
+    MP4StdpAtom &operator= ( const MP4StdpAtom &src );
+};
+
+class MP4StscAtom : public MP4Atom {
+public:
+    MP4StscAtom(MP4File &file);
+    void Read();
+private:
+    MP4StscAtom();
+    MP4StscAtom( const MP4StscAtom &src );
+    MP4StscAtom &operator= ( const MP4StscAtom &src );
+};
+
+class MP4StsdAtom : public MP4Atom {
+public:
+    MP4StsdAtom(MP4File &file);
+    void Read();
+private:
+    MP4StsdAtom();
+    MP4StsdAtom( const MP4StsdAtom &src );
+    MP4StsdAtom &operator= ( const MP4StsdAtom &src );
+};
+
+class MP4StszAtom : public MP4Atom {
+public:
+    MP4StszAtom(MP4File &file);
+    void Read();
+    void Write();
+private:
+    MP4StszAtom();
+    MP4StszAtom( const MP4StszAtom &src );
+    MP4StszAtom &operator= ( const MP4StszAtom &src );
+};
+
+class MP4Stz2Atom : public MP4Atom {
+public:
+    MP4Stz2Atom(MP4File &file);
+    void Read();
+private:
+    MP4Stz2Atom();
+    MP4Stz2Atom( const MP4Stz2Atom &src );
+    MP4Stz2Atom &operator= ( const MP4Stz2Atom &src );
+};
+
+class MP4TextAtom : public MP4Atom {
+public:
+    MP4TextAtom(MP4File &file);
+    void Generate();
+    void Read();
+protected:
+    void AddPropertiesStsdType();
+    void AddPropertiesGmhdType();
+
+    void GenerateStsdType();
+    void GenerateGmhdType();
+private:
+    MP4TextAtom();
+    MP4TextAtom( const MP4TextAtom &src );
+    MP4TextAtom &operator= ( const MP4TextAtom &src );
+};
+
+class MP4Tx3gAtom : public MP4Atom {
+public:
+    MP4Tx3gAtom(MP4File &file);
+    void Generate();
+private:
+    MP4Tx3gAtom();
+    MP4Tx3gAtom( const MP4Tx3gAtom &src );
+    MP4Tx3gAtom &operator= ( const MP4Tx3gAtom &src );
+};
+
+class MP4FtabAtom : public MP4Atom {
+public:
+    MP4FtabAtom(MP4File &file);
+private:
+    MP4FtabAtom();
+    MP4FtabAtom( const MP4FtabAtom &src );
+    MP4FtabAtom &operator= ( const MP4FtabAtom &src );
+};
+
+class MP4TfhdAtom : public MP4Atom {
+public:
+    MP4TfhdAtom(MP4File &file);
+    void Read();
+protected:
+    void AddProperties(uint32_t flags);
+private:
+    MP4TfhdAtom();
+    MP4TfhdAtom( const MP4TfhdAtom &src );
+    MP4TfhdAtom &operator= ( const MP4TfhdAtom &src );
+};
+
+class MP4TkhdAtom : public MP4Atom {
+public:
+    MP4TkhdAtom(MP4File &file);
+    void Generate();
+    void Read();
+protected:
+    void AddProperties(uint8_t version);
+private:
+    MP4TkhdAtom();
+    MP4TkhdAtom( const MP4TkhdAtom &src );
+    MP4TkhdAtom &operator= ( const MP4TkhdAtom &src );
+};
+
+class MP4TrunAtom : public MP4Atom {
+public:
+    MP4TrunAtom(MP4File &file);
+    void Read();
+protected:
+    void AddProperties(uint32_t flags);
+private:
+    MP4TrunAtom();
+    MP4TrunAtom( const MP4TrunAtom &src );
+    MP4TrunAtom &operator= ( const MP4TrunAtom &src );
+};
+
+class MP4UdtaAtom : public MP4Atom {
+public:
+    MP4UdtaAtom(MP4File &file);
+    void Read();
+private:
+    MP4UdtaAtom();
+    MP4UdtaAtom( const MP4UdtaAtom &src );
+    MP4UdtaAtom &operator= ( const MP4UdtaAtom &src );
+};
+
+class MP4UrnAtom : public MP4Atom {
+public:
+    MP4UrnAtom(MP4File &file);
+    void Read();
+private:
+    MP4UrnAtom();
+    MP4UrnAtom( const MP4UrnAtom &src );
+    MP4UrnAtom &operator= ( const MP4UrnAtom &src );
+};
+
+class MP4VmhdAtom : public MP4Atom {
+public:
+    MP4VmhdAtom(MP4File &file);
+    void Generate();
+private:
+    MP4VmhdAtom();
+    MP4VmhdAtom( const MP4VmhdAtom &src );
+    MP4VmhdAtom &operator= ( const MP4VmhdAtom &src );
+};
+
+class MP4HrefAtom : public MP4Atom {
+public:
+    MP4HrefAtom(MP4File &file);
+    void Generate(void);
+private:
+    MP4HrefAtom();
+    MP4HrefAtom( const MP4HrefAtom &src );
+    MP4HrefAtom &operator= ( const MP4HrefAtom &src );
+};
+
+class MP4PaspAtom : public MP4Atom {
+public:
+    MP4PaspAtom(MP4File &file);
+    void Generate();
+private:
+    MP4PaspAtom();
+    MP4PaspAtom( const MP4PaspAtom &src );
+    MP4PaspAtom &operator= ( const MP4PaspAtom &src );
+};
+
+class MP4ColrAtom : public MP4Atom {
+public:
+    MP4ColrAtom(MP4File &file);
+    void Generate();
+private:
+    MP4ColrAtom();
+    MP4ColrAtom( const MP4ColrAtom &src );
+    MP4ColrAtom &operator= ( const MP4ColrAtom &src );
+};
+
+class IPodUUIDAtom : public MP4Atom {
+public:
+    IPodUUIDAtom(MP4File &file);
+private:
+    IPodUUIDAtom();
+    IPodUUIDAtom( const IPodUUIDAtom &src );
+    IPodUUIDAtom &operator= ( const IPodUUIDAtom &src );
+};
+
+class MP4NmhdAtom : public MP4Atom {
+public:
+    MP4NmhdAtom(MP4File &file);
+private:
+    MP4NmhdAtom();
+    MP4NmhdAtom( const MP4NmhdAtom &src );
+    MP4NmhdAtom &operator= ( const MP4NmhdAtom &src );
+};
+
+/*! Nero Chapter List.
+ * This atom defines the structure of a Nero chapter list.
+ * Although it is not completely clear if this structure is
+ * correct it is complete enough to successfully read and write
+ * the chapter list so that even Nero's software accepts it.
+ *
+ * The assumed format is as follows:
+ * - MP4Integer8Property("version")
+ * - MP4Integer24Property("flags")
+ * - MP4BytesProperty("reserved", 1)
+ * - MP4Integer32Property("chaptercount")\n
+ * - MP4TableProperty("chapters", "ref to chaptercount");
+ *     - MP4Integer64Property("starttime")\n
+ *       The start time of the chapter expressed in 100 nanosecond units
+ *     - MP4StringProperty("title", true)\n
+ *       The title of the chapter encoded in UTF-8
+ *
+ * The chapter title only accepts strings of 255 bytes so if a string
+ * only contains simple (two-byte) UTF-8 chars the maximum length is
+ * 127 chars.
+ */
+class MP4ChplAtom : public MP4Atom {
+public:
+    MP4ChplAtom(MP4File &file);
+    void Generate();
+private:
+    MP4ChplAtom();
+    MP4ChplAtom( const MP4ChplAtom &src );
+    MP4ChplAtom &operator= ( const MP4ChplAtom &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// iTMF hdlr-atom.
+class MP4ItmfHdlrAtom : public MP4FullAtom
+{
+public:
+    MP4ItmfHdlrAtom(MP4File &file);
+    void Read();
+
+    MP4Integer32Property& reserved1;
+    MP4BytesProperty&     handlerType;
+    MP4BytesProperty&     reserved2;
+    MP4BytesProperty&     name;
+private:
+    MP4ItmfHdlrAtom();
+    MP4ItmfHdlrAtom( const MP4ItmfHdlrAtom &src );
+    MP4ItmfHdlrAtom &operator= ( const MP4ItmfHdlrAtom &src );
+};
+
+/// iTMF item-atom.
+class MP4ItemAtom : public MP4Atom
+{
+public:
+    MP4ItemAtom( MP4File &file, const char* type );
+private:
+    MP4ItemAtom();
+    MP4ItemAtom( const MP4ItemAtom &src );
+    MP4ItemAtom &operator= ( const MP4ItemAtom &src );
+};
+
+/// iTMF meaning-atom.
+class MP4MeanAtom : public MP4FullAtom
+{
+public:
+    MP4MeanAtom(MP4File &file);
+    void Read();
+
+    MP4BytesProperty& value;
+private:
+    MP4MeanAtom();
+    MP4MeanAtom( const MP4MeanAtom &src );
+    MP4MeanAtom &operator= ( const MP4MeanAtom &src );
+};
+
+/// iTMF name-atom.
+class MP4NameAtom : public MP4FullAtom
+{
+public:
+    MP4NameAtom(MP4File &file);
+    void Read();
+
+    MP4BytesProperty& value;
+private:
+    MP4NameAtom();
+    MP4NameAtom( const MP4NameAtom &src );
+    MP4NameAtom &operator= ( const MP4NameAtom &src );
+};
+
+/// iTMF data-atom.
+class MP4DataAtom : public MP4Atom
+{
+public:
+    MP4DataAtom(MP4File &file);
+    void Read();
+
+    MP4Integer16Property& typeReserved;
+    MP4Integer8Property&  typeSetIdentifier;
+    MP4BasicTypeProperty& typeCode;
+    MP4Integer32Property& locale;
+    MP4BytesProperty&     metadata;
+private:
+    MP4DataAtom();
+    MP4DataAtom( const MP4DataAtom &src );
+    MP4DataAtom &operator= ( const MP4DataAtom &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// QTFF udta data element-atom.
+class MP4UdtaElementAtom : public MP4Atom
+{
+public:
+    MP4UdtaElementAtom( MP4File &file, const char* type );
+    void Read();
+
+    MP4BytesProperty& value;
+private:
+    MP4UdtaElementAtom();
+    MP4UdtaElementAtom( const MP4UdtaElementAtom &src );
+    MP4UdtaElementAtom &operator= ( const MP4UdtaElementAtom &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_ATOMS_H
diff --git a/src/bmff/bmff.h b/src/bmff/bmff.h
new file mode 100644
index 0000000..8fa3020
--- /dev/null
+++ b/src/bmff/bmff.h
@@ -0,0 +1,43 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_BMFF_BMFF_H
+#define MP4V2_IMPL_BMFF_BMFF_H
+
+/// @namespace mp4v2::impl::bmff (private) ISO base media file format.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+///
+/// This namespace implements some features that are specified by
+/// ISO base media file format, ISO/IEC 14496-12:2005(E).
+///
+namespace mp4v2 { namespace impl { namespace bmff {
+    ;
+}}}
+
+///////////////////////////////////////////////////////////////////////////////
+
+//#include "type.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_BMFF_BMFF_H
diff --git a/src/bmff/impl.h b/src/bmff/impl.h
new file mode 100644
index 0000000..b247807
--- /dev/null
+++ b/src/bmff/impl.h
@@ -0,0 +1,34 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_BMFF_IMPL_H
+#define MP4V2_IMPL_BMFF_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+#include "bmff.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_BMFF_IMPL_H
diff --git a/src/bmff/typebmff.cpp b/src/bmff/typebmff.cpp
new file mode 100644
index 0000000..e027a45
--- /dev/null
+++ b/src/bmff/typebmff.cpp
@@ -0,0 +1,531 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const bmff::EnumLanguageCode::Entry bmff::EnumLanguageCode::data[] = {
+    { mp4v2::impl::bmff::ILC_AAR,  "aar",  "Afar" },
+    { mp4v2::impl::bmff::ILC_ABK,  "abk",  "Abkhazian" },
+    { mp4v2::impl::bmff::ILC_ACE,  "ace",  "Achinese" },
+    { mp4v2::impl::bmff::ILC_ACH,  "ach",  "Acoli" },
+    { mp4v2::impl::bmff::ILC_ADA,  "ada",  "Adangme" },
+    { mp4v2::impl::bmff::ILC_ADY,  "ady",  "Adyghe; Adygei" },
+    { mp4v2::impl::bmff::ILC_AFA,  "afa",  "Afro-Asiatic (Other)" },
+    { mp4v2::impl::bmff::ILC_AFH,  "afh",  "Afrihili" },
+    { mp4v2::impl::bmff::ILC_AFR,  "afr",  "Afrikaans" },
+    { mp4v2::impl::bmff::ILC_AIN,  "ain",  "Ainu" },
+    { mp4v2::impl::bmff::ILC_AKA,  "aka",  "Akan" },
+    { mp4v2::impl::bmff::ILC_AKK,  "akk",  "Akkadian" },
+    { mp4v2::impl::bmff::ILC_SQI,  "sqi",  "Albanian" },
+    { mp4v2::impl::bmff::ILC_ALE,  "ale",  "Aleut" },
+    { mp4v2::impl::bmff::ILC_ALG,  "alg",  "Algonquian languages" },
+    { mp4v2::impl::bmff::ILC_ALT,  "alt",  "Southern Altai" },
+    { mp4v2::impl::bmff::ILC_AMH,  "amh",  "Amharic" },
+    { mp4v2::impl::bmff::ILC_ANG,  "ang",  "English, Old (ca.450-1100)" },
+    { mp4v2::impl::bmff::ILC_ANP,  "anp",  "Angika" },
+    { mp4v2::impl::bmff::ILC_APA,  "apa",  "Apache languages" },
+    { mp4v2::impl::bmff::ILC_ARA,  "ara",  "Arabic" },
+    { mp4v2::impl::bmff::ILC_ARC,  "arc",  "Official Aramaic (700-300 BCE); Imperial Aramaic (700-300 BCE)" },
+    { mp4v2::impl::bmff::ILC_ARG,  "arg",  "Aragonese" },
+    { mp4v2::impl::bmff::ILC_HYE,  "hye",  "Armenian" },
+    { mp4v2::impl::bmff::ILC_ARN,  "arn",  "Mapudungun; Mapuche" },
+    { mp4v2::impl::bmff::ILC_ARP,  "arp",  "Arapaho" },
+    { mp4v2::impl::bmff::ILC_ART,  "art",  "Artificial (Other)" },
+    { mp4v2::impl::bmff::ILC_ARW,  "arw",  "Arawak" },
+    { mp4v2::impl::bmff::ILC_ASM,  "asm",  "Assamese" },
+    { mp4v2::impl::bmff::ILC_AST,  "ast",  "Asturian; Bable; Leonese; Asturleonese" },
+    { mp4v2::impl::bmff::ILC_ATH,  "ath",  "Athapascan languages" },
+    { mp4v2::impl::bmff::ILC_AUS,  "aus",  "Australian languages" },
+    { mp4v2::impl::bmff::ILC_AVA,  "ava",  "Avaric" },
+    { mp4v2::impl::bmff::ILC_AVE,  "ave",  "Avestan" },
+    { mp4v2::impl::bmff::ILC_AWA,  "awa",  "Awadhi" },
+    { mp4v2::impl::bmff::ILC_AYM,  "aym",  "Aymara" },
+    { mp4v2::impl::bmff::ILC_AZE,  "aze",  "Azerbaijani" },
+    { mp4v2::impl::bmff::ILC_BAD,  "bad",  "Banda languages" },
+    { mp4v2::impl::bmff::ILC_BAI,  "bai",  "Bamileke languages" },
+    { mp4v2::impl::bmff::ILC_BAK,  "bak",  "Bashkir" },
+    { mp4v2::impl::bmff::ILC_BAL,  "bal",  "Baluchi" },
+    { mp4v2::impl::bmff::ILC_BAM,  "bam",  "Bambara" },
+    { mp4v2::impl::bmff::ILC_BAN,  "ban",  "Balinese" },
+    { mp4v2::impl::bmff::ILC_EUS,  "eus",  "Basque" },
+    { mp4v2::impl::bmff::ILC_BAS,  "bas",  "Basa" },
+    { mp4v2::impl::bmff::ILC_BAT,  "bat",  "Baltic (Other)" },
+    { mp4v2::impl::bmff::ILC_BEJ,  "bej",  "Beja; Bedawiyet" },
+    { mp4v2::impl::bmff::ILC_BEL,  "bel",  "Belarusian" },
+    { mp4v2::impl::bmff::ILC_BEM,  "bem",  "Bemba" },
+    { mp4v2::impl::bmff::ILC_BEN,  "ben",  "Bengali" },
+    { mp4v2::impl::bmff::ILC_BER,  "ber",  "Berber (Other)" },
+    { mp4v2::impl::bmff::ILC_BHO,  "bho",  "Bhojpuri" },
+    { mp4v2::impl::bmff::ILC_BIH,  "bih",  "Bihari" },
+    { mp4v2::impl::bmff::ILC_BIK,  "bik",  "Bikol" },
+    { mp4v2::impl::bmff::ILC_BIN,  "bin",  "Bini; Edo" },
+    { mp4v2::impl::bmff::ILC_BIS,  "bis",  "Bislama" },
+    { mp4v2::impl::bmff::ILC_BLA,  "bla",  "Siksika" },
+    { mp4v2::impl::bmff::ILC_BNT,  "bnt",  "Bantu (Other)" },
+    { mp4v2::impl::bmff::ILC_BOS,  "bos",  "Bosnian" },
+    { mp4v2::impl::bmff::ILC_BRA,  "bra",  "Braj" },
+    { mp4v2::impl::bmff::ILC_BRE,  "bre",  "Breton" },
+    { mp4v2::impl::bmff::ILC_BTK,  "btk",  "Batak languages" },
+    { mp4v2::impl::bmff::ILC_BUA,  "bua",  "Buriat" },
+    { mp4v2::impl::bmff::ILC_BUG,  "bug",  "Buginese" },
+    { mp4v2::impl::bmff::ILC_BUL,  "bul",  "Bulgarian" },
+    { mp4v2::impl::bmff::ILC_MYA,  "mya",  "Burmese" },
+    { mp4v2::impl::bmff::ILC_BYN,  "byn",  "Blin; Bilin" },
+    { mp4v2::impl::bmff::ILC_CAD,  "cad",  "Caddo" },
+    { mp4v2::impl::bmff::ILC_CAI,  "cai",  "Central American Indian (Other)" },
+    { mp4v2::impl::bmff::ILC_CAR,  "car",  "Galibi Carib" },
+    { mp4v2::impl::bmff::ILC_CAT,  "cat",  "Catalan; Valencian" },
+    { mp4v2::impl::bmff::ILC_CAU,  "cau",  "Caucasian (Other)" },
+    { mp4v2::impl::bmff::ILC_CEB,  "ceb",  "Cebuano" },
+    { mp4v2::impl::bmff::ILC_CEL,  "cel",  "Celtic (Other)" },
+    { mp4v2::impl::bmff::ILC_CHA,  "cha",  "Chamorro" },
+    { mp4v2::impl::bmff::ILC_CHB,  "chb",  "Chibcha" },
+    { mp4v2::impl::bmff::ILC_CHE,  "che",  "Chechen" },
+    { mp4v2::impl::bmff::ILC_CHG,  "chg",  "Chagatai" },
+    { mp4v2::impl::bmff::ILC_ZHO,  "zho",  "Chinese" },
+    { mp4v2::impl::bmff::ILC_CHK,  "chk",  "Chuukese" },
+    { mp4v2::impl::bmff::ILC_CHM,  "chm",  "Mari" },
+    { mp4v2::impl::bmff::ILC_CHN,  "chn",  "Chinook jargon" },
+    { mp4v2::impl::bmff::ILC_CHO,  "cho",  "Choctaw" },
+    { mp4v2::impl::bmff::ILC_CHP,  "chp",  "Chipewyan; Dene Suline" },
+    { mp4v2::impl::bmff::ILC_CHR,  "chr",  "Cherokee" },
+    { mp4v2::impl::bmff::ILC_CHU,  "chu",  "Church Slavic; Old Slavonic; Church Slavonic; Old Bulgarian; Old Church Slavonic" },
+    { mp4v2::impl::bmff::ILC_CHV,  "chv",  "Chuvash" },
+    { mp4v2::impl::bmff::ILC_CHY,  "chy",  "Cheyenne" },
+    { mp4v2::impl::bmff::ILC_CMC,  "cmc",  "Chamic languages" },
+    { mp4v2::impl::bmff::ILC_COP,  "cop",  "Coptic" },
+    { mp4v2::impl::bmff::ILC_COR,  "cor",  "Cornish" },
+    { mp4v2::impl::bmff::ILC_COS,  "cos",  "Corsican" },
+    { mp4v2::impl::bmff::ILC_CPE,  "cpe",  "Creoles and pidgins, English based (Other)" },
+    { mp4v2::impl::bmff::ILC_CPF,  "cpf",  "Creoles and pidgins, French-based (Other)" },
+    { mp4v2::impl::bmff::ILC_CPP,  "cpp",  "Creoles and pidgins, Portuguese-based (Other)" },
+    { mp4v2::impl::bmff::ILC_CRE,  "cre",  "Cree" },
+    { mp4v2::impl::bmff::ILC_CRH,  "crh",  "Crimean Tatar; Crimean Turkish" },
+    { mp4v2::impl::bmff::ILC_CRP,  "crp",  "Creoles and pidgins (Other)" },
+    { mp4v2::impl::bmff::ILC_CSB,  "csb",  "Kashubian" },
+    { mp4v2::impl::bmff::ILC_CUS,  "cus",  "Cushitic (Other)" },
+    { mp4v2::impl::bmff::ILC_CES,  "ces",  "Czech" },
+    { mp4v2::impl::bmff::ILC_DAK,  "dak",  "Dakota" },
+    { mp4v2::impl::bmff::ILC_DAN,  "dan",  "Danish" },
+    { mp4v2::impl::bmff::ILC_DAR,  "dar",  "Dargwa" },
+    { mp4v2::impl::bmff::ILC_DAY,  "day",  "Land Dayak languages" },
+    { mp4v2::impl::bmff::ILC_DEL,  "del",  "Delaware" },
+    { mp4v2::impl::bmff::ILC_DEN,  "den",  "Slave (Athapascan)" },
+    { mp4v2::impl::bmff::ILC_DGR,  "dgr",  "Dogrib" },
+    { mp4v2::impl::bmff::ILC_DIN,  "din",  "Dinka" },
+    { mp4v2::impl::bmff::ILC_DIV,  "div",  "Divehi; Dhivehi; Maldivian" },
+    { mp4v2::impl::bmff::ILC_DOI,  "doi",  "Dogri" },
+    { mp4v2::impl::bmff::ILC_DRA,  "dra",  "Dravidian (Other)" },
+    { mp4v2::impl::bmff::ILC_DSB,  "dsb",  "Lower Sorbian" },
+    { mp4v2::impl::bmff::ILC_DUA,  "dua",  "Duala" },
+    { mp4v2::impl::bmff::ILC_DUM,  "dum",  "Dutch, Middle (ca.1050-1350)" },
+    { mp4v2::impl::bmff::ILC_NLD,  "nld",  "Dutch; Flemish" },
+    { mp4v2::impl::bmff::ILC_DYU,  "dyu",  "Dyula" },
+    { mp4v2::impl::bmff::ILC_DZO,  "dzo",  "Dzongkha" },
+    { mp4v2::impl::bmff::ILC_EFI,  "efi",  "Efik" },
+    { mp4v2::impl::bmff::ILC_EGY,  "egy",  "Egyptian (Ancient)" },
+    { mp4v2::impl::bmff::ILC_EKA,  "eka",  "Ekajuk" },
+    { mp4v2::impl::bmff::ILC_ELX,  "elx",  "Elamite" },
+    { mp4v2::impl::bmff::ILC_ENG,  "eng",  "English" },
+    { mp4v2::impl::bmff::ILC_ENM,  "enm",  "English, Middle (1100-1500)" },
+    { mp4v2::impl::bmff::ILC_EPO,  "epo",  "Esperanto" },
+    { mp4v2::impl::bmff::ILC_EST,  "est",  "Estonian" },
+    { mp4v2::impl::bmff::ILC_EWE,  "ewe",  "Ewe" },
+    { mp4v2::impl::bmff::ILC_EWO,  "ewo",  "Ewondo" },
+    { mp4v2::impl::bmff::ILC_FAN,  "fan",  "Fang" },
+    { mp4v2::impl::bmff::ILC_FAO,  "fao",  "Faroese" },
+    { mp4v2::impl::bmff::ILC_FAT,  "fat",  "Fanti" },
+    { mp4v2::impl::bmff::ILC_FIJ,  "fij",  "Fijian" },
+    { mp4v2::impl::bmff::ILC_FIL,  "fil",  "Filipino; Pilipino" },
+    { mp4v2::impl::bmff::ILC_FIN,  "fin",  "Finnish" },
+    { mp4v2::impl::bmff::ILC_FIU,  "fiu",  "Finno-Ugrian (Other)" },
+    { mp4v2::impl::bmff::ILC_FON,  "fon",  "Fon" },
+    { mp4v2::impl::bmff::ILC_FRA,  "fra",  "French" },
+    { mp4v2::impl::bmff::ILC_FRM,  "frm",  "French, Middle (ca.1400-1600)" },
+    { mp4v2::impl::bmff::ILC_FRO,  "fro",  "French, Old (842-ca.1400)" },
+    { mp4v2::impl::bmff::ILC_FRR,  "frr",  "Northern Frisian" },
+    { mp4v2::impl::bmff::ILC_FRS,  "frs",  "Eastern Frisian" },
+    { mp4v2::impl::bmff::ILC_FRY,  "fry",  "Western Frisian" },
+    { mp4v2::impl::bmff::ILC_FUL,  "ful",  "Fulah" },
+    { mp4v2::impl::bmff::ILC_FUR,  "fur",  "Friulian" },
+    { mp4v2::impl::bmff::ILC_GAA,  "gaa",  "Ga" },
+    { mp4v2::impl::bmff::ILC_GAY,  "gay",  "Gayo" },
+    { mp4v2::impl::bmff::ILC_GBA,  "gba",  "Gbaya" },
+    { mp4v2::impl::bmff::ILC_GEM,  "gem",  "Germanic (Other)" },
+    { mp4v2::impl::bmff::ILC_KAT,  "kat",  "Georgian" },
+    { mp4v2::impl::bmff::ILC_DEU,  "deu",  "German" },
+    { mp4v2::impl::bmff::ILC_GEZ,  "gez",  "Geez" },
+    { mp4v2::impl::bmff::ILC_GIL,  "gil",  "Gilbertese" },
+    { mp4v2::impl::bmff::ILC_GLA,  "gla",  "Gaelic; Scottish Gaelic" },
+    { mp4v2::impl::bmff::ILC_GLE,  "gle",  "Irish" },
+    { mp4v2::impl::bmff::ILC_GLG,  "glg",  "Galician" },
+    { mp4v2::impl::bmff::ILC_GLV,  "glv",  "Manx" },
+    { mp4v2::impl::bmff::ILC_GMH,  "gmh",  "German, Middle High (ca.1050-1500)" },
+    { mp4v2::impl::bmff::ILC_GOH,  "goh",  "German, Old High (ca.750-1050)" },
+    { mp4v2::impl::bmff::ILC_GON,  "gon",  "Gondi" },
+    { mp4v2::impl::bmff::ILC_GOR,  "gor",  "Gorontalo" },
+    { mp4v2::impl::bmff::ILC_GOT,  "got",  "Gothic" },
+    { mp4v2::impl::bmff::ILC_GRB,  "grb",  "Grebo" },
+    { mp4v2::impl::bmff::ILC_GRC,  "grc",  "Greek, Ancient (to 1453)" },
+    { mp4v2::impl::bmff::ILC_ELL,  "ell",  "Greek, Modern (1453-)" },
+    { mp4v2::impl::bmff::ILC_GRN,  "grn",  "Guarani" },
+    { mp4v2::impl::bmff::ILC_GSW,  "gsw",  "Swiss German; Alemannic; Alsatian" },
+    { mp4v2::impl::bmff::ILC_GUJ,  "guj",  "Gujarati" },
+    { mp4v2::impl::bmff::ILC_GWI,  "gwi",  "Gwich'in" },
+    { mp4v2::impl::bmff::ILC_HAI,  "hai",  "Haida" },
+    { mp4v2::impl::bmff::ILC_HAT,  "hat",  "Haitian; Haitian Creole" },
+    { mp4v2::impl::bmff::ILC_HAU,  "hau",  "Hausa" },
+    { mp4v2::impl::bmff::ILC_HAW,  "haw",  "Hawaiian" },
+    { mp4v2::impl::bmff::ILC_HEB,  "heb",  "Hebrew" },
+    { mp4v2::impl::bmff::ILC_HER,  "her",  "Herero" },
+    { mp4v2::impl::bmff::ILC_HIL,  "hil",  "Hiligaynon" },
+    { mp4v2::impl::bmff::ILC_HIM,  "him",  "Himachali" },
+    { mp4v2::impl::bmff::ILC_HIN,  "hin",  "Hindi" },
+    { mp4v2::impl::bmff::ILC_HIT,  "hit",  "Hittite" },
+    { mp4v2::impl::bmff::ILC_HMN,  "hmn",  "Hmong" },
+    { mp4v2::impl::bmff::ILC_HMO,  "hmo",  "Hiri Motu" },
+    { mp4v2::impl::bmff::ILC_HRV,  "hrv",  "Croatian" },
+    { mp4v2::impl::bmff::ILC_HSB,  "hsb",  "Upper Sorbian" },
+    { mp4v2::impl::bmff::ILC_HUN,  "hun",  "Hungarian" },
+    { mp4v2::impl::bmff::ILC_HUP,  "hup",  "Hupa" },
+    { mp4v2::impl::bmff::ILC_IBA,  "iba",  "Iban" },
+    { mp4v2::impl::bmff::ILC_IBO,  "ibo",  "Igbo" },
+    { mp4v2::impl::bmff::ILC_ISL,  "isl",  "Icelandic" },
+    { mp4v2::impl::bmff::ILC_IDO,  "ido",  "Ido" },
+    { mp4v2::impl::bmff::ILC_III,  "iii",  "Sichuan Yi; Nuosu" },
+    { mp4v2::impl::bmff::ILC_IJO,  "ijo",  "Ijo languages" },
+    { mp4v2::impl::bmff::ILC_IKU,  "iku",  "Inuktitut" },
+    { mp4v2::impl::bmff::ILC_ILE,  "ile",  "Interlingue; Occidental" },
+    { mp4v2::impl::bmff::ILC_ILO,  "ilo",  "Iloko" },
+    { mp4v2::impl::bmff::ILC_INA,  "ina",  "Interlingua (International Auxiliary Language Association)" },
+    { mp4v2::impl::bmff::ILC_INC,  "inc",  "Indic (Other)" },
+    { mp4v2::impl::bmff::ILC_IND,  "ind",  "Indonesian" },
+    { mp4v2::impl::bmff::ILC_INE,  "ine",  "Indo-European (Other)" },
+    { mp4v2::impl::bmff::ILC_INH,  "inh",  "Ingush" },
+    { mp4v2::impl::bmff::ILC_IPK,  "ipk",  "Inupiaq" },
+    { mp4v2::impl::bmff::ILC_IRA,  "ira",  "Iranian (Other)" },
+    { mp4v2::impl::bmff::ILC_IRO,  "iro",  "Iroquoian languages" },
+    { mp4v2::impl::bmff::ILC_ITA,  "ita",  "Italian" },
+    { mp4v2::impl::bmff::ILC_JAV,  "jav",  "Javanese" },
+    { mp4v2::impl::bmff::ILC_JBO,  "jbo",  "Lojban" },
+    { mp4v2::impl::bmff::ILC_JPN,  "jpn",  "Japanese" },
+    { mp4v2::impl::bmff::ILC_JPR,  "jpr",  "Judeo-Persian" },
+    { mp4v2::impl::bmff::ILC_JRB,  "jrb",  "Judeo-Arabic" },
+    { mp4v2::impl::bmff::ILC_KAA,  "kaa",  "Kara-Kalpak" },
+    { mp4v2::impl::bmff::ILC_KAB,  "kab",  "Kabyle" },
+    { mp4v2::impl::bmff::ILC_KAC,  "kac",  "Kachin; Jingpho" },
+    { mp4v2::impl::bmff::ILC_KAL,  "kal",  "Kalaallisut; Greenlandic" },
+    { mp4v2::impl::bmff::ILC_KAM,  "kam",  "Kamba" },
+    { mp4v2::impl::bmff::ILC_KAN,  "kan",  "Kannada" },
+    { mp4v2::impl::bmff::ILC_KAR,  "kar",  "Karen languages" },
+    { mp4v2::impl::bmff::ILC_KAS,  "kas",  "Kashmiri" },
+    { mp4v2::impl::bmff::ILC_KAU,  "kau",  "Kanuri" },
+    { mp4v2::impl::bmff::ILC_KAW,  "kaw",  "Kawi" },
+    { mp4v2::impl::bmff::ILC_KAZ,  "kaz",  "Kazakh" },
+    { mp4v2::impl::bmff::ILC_KBD,  "kbd",  "Kabardian" },
+    { mp4v2::impl::bmff::ILC_KHA,  "kha",  "Khasi" },
+    { mp4v2::impl::bmff::ILC_KHI,  "khi",  "Khoisan (Other)" },
+    { mp4v2::impl::bmff::ILC_KHM,  "khm",  "Central Khmer" },
+    { mp4v2::impl::bmff::ILC_KHO,  "kho",  "Khotanese; Sakan" },
+    { mp4v2::impl::bmff::ILC_KIK,  "kik",  "Kikuyu; Gikuyu" },
+    { mp4v2::impl::bmff::ILC_KIN,  "kin",  "Kinyarwanda" },
+    { mp4v2::impl::bmff::ILC_KIR,  "kir",  "Kirghiz; Kyrgyz" },
+    { mp4v2::impl::bmff::ILC_KMB,  "kmb",  "Kimbundu" },
+    { mp4v2::impl::bmff::ILC_KOK,  "kok",  "Konkani" },
+    { mp4v2::impl::bmff::ILC_KOM,  "kom",  "Komi" },
+    { mp4v2::impl::bmff::ILC_KON,  "kon",  "Kongo" },
+    { mp4v2::impl::bmff::ILC_KOR,  "kor",  "Korean" },
+    { mp4v2::impl::bmff::ILC_KOS,  "kos",  "Kosraean" },
+    { mp4v2::impl::bmff::ILC_KPE,  "kpe",  "Kpelle" },
+    { mp4v2::impl::bmff::ILC_KRC,  "krc",  "Karachay-Balkar" },
+    { mp4v2::impl::bmff::ILC_KRL,  "krl",  "Karelian" },
+    { mp4v2::impl::bmff::ILC_KRO,  "kro",  "Kru languages" },
+    { mp4v2::impl::bmff::ILC_KRU,  "kru",  "Kurukh" },
+    { mp4v2::impl::bmff::ILC_KUA,  "kua",  "Kuanyama; Kwanyama" },
+    { mp4v2::impl::bmff::ILC_KUM,  "kum",  "Kumyk" },
+    { mp4v2::impl::bmff::ILC_KUR,  "kur",  "Kurdish" },
+    { mp4v2::impl::bmff::ILC_KUT,  "kut",  "Kutenai" },
+    { mp4v2::impl::bmff::ILC_LAD,  "lad",  "Ladino" },
+    { mp4v2::impl::bmff::ILC_LAH,  "lah",  "Lahnda" },
+    { mp4v2::impl::bmff::ILC_LAM,  "lam",  "Lamba" },
+    { mp4v2::impl::bmff::ILC_LAO,  "lao",  "Lao" },
+    { mp4v2::impl::bmff::ILC_LAT,  "lat",  "Latin" },
+    { mp4v2::impl::bmff::ILC_LAV,  "lav",  "Latvian" },
+    { mp4v2::impl::bmff::ILC_LEZ,  "lez",  "Lezghian" },
+    { mp4v2::impl::bmff::ILC_LIM,  "lim",  "Limburgan; Limburger; Limburgish" },
+    { mp4v2::impl::bmff::ILC_LIN,  "lin",  "Lingala" },
+    { mp4v2::impl::bmff::ILC_LIT,  "lit",  "Lithuanian" },
+    { mp4v2::impl::bmff::ILC_LOL,  "lol",  "Mongo" },
+    { mp4v2::impl::bmff::ILC_LOZ,  "loz",  "Lozi" },
+    { mp4v2::impl::bmff::ILC_LTZ,  "ltz",  "Luxembourgish; Letzeburgesch" },
+    { mp4v2::impl::bmff::ILC_LUA,  "lua",  "Luba-Lulua" },
+    { mp4v2::impl::bmff::ILC_LUB,  "lub",  "Luba-Katanga" },
+    { mp4v2::impl::bmff::ILC_LUG,  "lug",  "Ganda" },
+    { mp4v2::impl::bmff::ILC_LUI,  "lui",  "Luiseno" },
+    { mp4v2::impl::bmff::ILC_LUN,  "lun",  "Lunda" },
+    { mp4v2::impl::bmff::ILC_LUO,  "luo",  "Luo (Kenya and Tanzania)" },
+    { mp4v2::impl::bmff::ILC_LUS,  "lus",  "Lushai" },
+    { mp4v2::impl::bmff::ILC_MKD,  "mkd",  "Macedonian" },
+    { mp4v2::impl::bmff::ILC_MAD,  "mad",  "Madurese" },
+    { mp4v2::impl::bmff::ILC_MAG,  "mag",  "Magahi" },
+    { mp4v2::impl::bmff::ILC_MAH,  "mah",  "Marshallese" },
+    { mp4v2::impl::bmff::ILC_MAI,  "mai",  "Maithili" },
+    { mp4v2::impl::bmff::ILC_MAK,  "mak",  "Makasar" },
+    { mp4v2::impl::bmff::ILC_MAL,  "mal",  "Malayalam" },
+    { mp4v2::impl::bmff::ILC_MAN,  "man",  "Mandingo" },
+    { mp4v2::impl::bmff::ILC_MRI,  "mri",  "Maori" },
+    { mp4v2::impl::bmff::ILC_MAP,  "map",  "Austronesian (Other)" },
+    { mp4v2::impl::bmff::ILC_MAR,  "mar",  "Marathi" },
+    { mp4v2::impl::bmff::ILC_MAS,  "mas",  "Masai" },
+    { mp4v2::impl::bmff::ILC_MSA,  "msa",  "Malay" },
+    { mp4v2::impl::bmff::ILC_MDF,  "mdf",  "Moksha" },
+    { mp4v2::impl::bmff::ILC_MDR,  "mdr",  "Mandar" },
+    { mp4v2::impl::bmff::ILC_MEN,  "men",  "Mende" },
+    { mp4v2::impl::bmff::ILC_MGA,  "mga",  "Irish, Middle (900-1200)" },
+    { mp4v2::impl::bmff::ILC_MIC,  "mic",  "Mi'kmaq; Micmac" },
+    { mp4v2::impl::bmff::ILC_MIN,  "min",  "Minangkabau" },
+    { mp4v2::impl::bmff::ILC_MIS,  "mis",  "Uncoded languages" },
+    { mp4v2::impl::bmff::ILC_MKH,  "mkh",  "Mon-Khmer (Other)" },
+    { mp4v2::impl::bmff::ILC_MLG,  "mlg",  "Malagasy" },
+    { mp4v2::impl::bmff::ILC_MLT,  "mlt",  "Maltese" },
+    { mp4v2::impl::bmff::ILC_MNC,  "mnc",  "Manchu" },
+    { mp4v2::impl::bmff::ILC_MNI,  "mni",  "Manipuri" },
+    { mp4v2::impl::bmff::ILC_MNO,  "mno",  "Manobo languages" },
+    { mp4v2::impl::bmff::ILC_MOH,  "moh",  "Mohawk" },
+    { mp4v2::impl::bmff::ILC_MON,  "mon",  "Mongolian" },
+    { mp4v2::impl::bmff::ILC_MOS,  "mos",  "Mossi" },
+    { mp4v2::impl::bmff::ILC_MUL,  "mul",  "Multiple languages" },
+    { mp4v2::impl::bmff::ILC_MUN,  "mun",  "Munda languages" },
+    { mp4v2::impl::bmff::ILC_MUS,  "mus",  "Creek" },
+    { mp4v2::impl::bmff::ILC_MWL,  "mwl",  "Mirandese" },
+    { mp4v2::impl::bmff::ILC_MWR,  "mwr",  "Marwari" },
+    { mp4v2::impl::bmff::ILC_MYN,  "myn",  "Mayan languages" },
+    { mp4v2::impl::bmff::ILC_MYV,  "myv",  "Erzya" },
+    { mp4v2::impl::bmff::ILC_NAH,  "nah",  "Nahuatl languages" },
+    { mp4v2::impl::bmff::ILC_NAI,  "nai",  "North American Indian" },
+    { mp4v2::impl::bmff::ILC_NAP,  "nap",  "Neapolitan" },
+    { mp4v2::impl::bmff::ILC_NAU,  "nau",  "Nauru" },
+    { mp4v2::impl::bmff::ILC_NAV,  "nav",  "Navajo; Navaho" },
+    { mp4v2::impl::bmff::ILC_NBL,  "nbl",  "Ndebele, South; South Ndebele" },
+    { mp4v2::impl::bmff::ILC_NDE,  "nde",  "Ndebele, North; North Ndebele" },
+    { mp4v2::impl::bmff::ILC_NDO,  "ndo",  "Ndonga" },
+    { mp4v2::impl::bmff::ILC_NDS,  "nds",  "Low German; Low Saxon; German, Low; Saxon, Low" },
+    { mp4v2::impl::bmff::ILC_NEP,  "nep",  "Nepali" },
+    { mp4v2::impl::bmff::ILC_NEW,  "new",  "Nepal Bhasa; Newari" },
+    { mp4v2::impl::bmff::ILC_NIA,  "nia",  "Nias" },
+    { mp4v2::impl::bmff::ILC_NIC,  "nic",  "Niger-Kordofanian (Other)" },
+    { mp4v2::impl::bmff::ILC_NIU,  "niu",  "Niuean" },
+    { mp4v2::impl::bmff::ILC_NNO,  "nno",  "Norwegian Nynorsk; Nynorsk, Norwegian" },
+    { mp4v2::impl::bmff::ILC_NOB,  "nob",  "Bokmål, Norwegian; Norwegian Bokmål" },
+    { mp4v2::impl::bmff::ILC_NOG,  "nog",  "Nogai" },
+    { mp4v2::impl::bmff::ILC_NON,  "non",  "Norse, Old" },
+    { mp4v2::impl::bmff::ILC_NOR,  "nor",  "Norwegian" },
+    { mp4v2::impl::bmff::ILC_NQO,  "nqo",  "N'Ko" },
+    { mp4v2::impl::bmff::ILC_NSO,  "nso",  "Pedi; Sepedi; Northern Sotho" },
+    { mp4v2::impl::bmff::ILC_NUB,  "nub",  "Nubian languages" },
+    { mp4v2::impl::bmff::ILC_NWC,  "nwc",  "Classical Newari; Old Newari; Classical Nepal Bhasa" },
+    { mp4v2::impl::bmff::ILC_NYA,  "nya",  "Chichewa; Chewa; Nyanja" },
+    { mp4v2::impl::bmff::ILC_NYM,  "nym",  "Nyamwezi" },
+    { mp4v2::impl::bmff::ILC_NYN,  "nyn",  "Nyankole" },
+    { mp4v2::impl::bmff::ILC_NYO,  "nyo",  "Nyoro" },
+    { mp4v2::impl::bmff::ILC_NZI,  "nzi",  "Nzima" },
+    { mp4v2::impl::bmff::ILC_OCI,  "oci",  "Occitan (post 1500); Provençal" },
+    { mp4v2::impl::bmff::ILC_OJI,  "oji",  "Ojibwa" },
+    { mp4v2::impl::bmff::ILC_ORI,  "ori",  "Oriya" },
+    { mp4v2::impl::bmff::ILC_ORM,  "orm",  "Oromo" },
+    { mp4v2::impl::bmff::ILC_OSA,  "osa",  "Osage" },
+    { mp4v2::impl::bmff::ILC_OSS,  "oss",  "Ossetian; Ossetic" },
+    { mp4v2::impl::bmff::ILC_OTA,  "ota",  "Turkish, Ottoman (1500-1928)" },
+    { mp4v2::impl::bmff::ILC_OTO,  "oto",  "Otomian languages" },
+    { mp4v2::impl::bmff::ILC_PAA,  "paa",  "Papuan (Other)" },
+    { mp4v2::impl::bmff::ILC_PAG,  "pag",  "Pangasinan" },
+    { mp4v2::impl::bmff::ILC_PAL,  "pal",  "Pahlavi" },
+    { mp4v2::impl::bmff::ILC_PAM,  "pam",  "Pampanga; Kapampangan" },
+    { mp4v2::impl::bmff::ILC_PAN,  "pan",  "Panjabi; Punjabi" },
+    { mp4v2::impl::bmff::ILC_PAP,  "pap",  "Papiamento" },
+    { mp4v2::impl::bmff::ILC_PAU,  "pau",  "Palauan" },
+    { mp4v2::impl::bmff::ILC_PEO,  "peo",  "Persian, Old (ca.600-400 B.C.)" },
+    { mp4v2::impl::bmff::ILC_FAS,  "fas",  "Persian" },
+    { mp4v2::impl::bmff::ILC_PHI,  "phi",  "Philippine (Other)" },
+    { mp4v2::impl::bmff::ILC_PHN,  "phn",  "Phoenician" },
+    { mp4v2::impl::bmff::ILC_PLI,  "pli",  "Pali" },
+    { mp4v2::impl::bmff::ILC_POL,  "pol",  "Polish" },
+    { mp4v2::impl::bmff::ILC_PON,  "pon",  "Pohnpeian" },
+    { mp4v2::impl::bmff::ILC_POR,  "por",  "Portuguese" },
+    { mp4v2::impl::bmff::ILC_PRA,  "pra",  "Prakrit languages" },
+    { mp4v2::impl::bmff::ILC_PRO,  "pro",  "Provençal, Old (to 1500)" },
+    { mp4v2::impl::bmff::ILC_PUS,  "pus",  "Pushto; Pashto" },
+    { mp4v2::impl::bmff::ILC_QUE,  "que",  "Quechua" },
+    { mp4v2::impl::bmff::ILC_RAJ,  "raj",  "Rajasthani" },
+    { mp4v2::impl::bmff::ILC_RAP,  "rap",  "Rapanui" },
+    { mp4v2::impl::bmff::ILC_RAR,  "rar",  "Rarotongan; Cook Islands Maori" },
+    { mp4v2::impl::bmff::ILC_ROA,  "roa",  "Romance (Other)" },
+    { mp4v2::impl::bmff::ILC_ROH,  "roh",  "Romansh" },
+    { mp4v2::impl::bmff::ILC_ROM,  "rom",  "Romany" },
+    { mp4v2::impl::bmff::ILC_RON,  "ron",  "Romanian; Moldavian; Moldovan" },
+    { mp4v2::impl::bmff::ILC_RUN,  "run",  "Rundi" },
+    { mp4v2::impl::bmff::ILC_RUP,  "rup",  "Aromanian; Arumanian; Macedo-Romanian" },
+    { mp4v2::impl::bmff::ILC_RUS,  "rus",  "Russian" },
+    { mp4v2::impl::bmff::ILC_SAD,  "sad",  "Sandawe" },
+    { mp4v2::impl::bmff::ILC_SAG,  "sag",  "Sango" },
+    { mp4v2::impl::bmff::ILC_SAH,  "sah",  "Yakut" },
+    { mp4v2::impl::bmff::ILC_SAI,  "sai",  "South American Indian (Other)" },
+    { mp4v2::impl::bmff::ILC_SAL,  "sal",  "Salishan languages" },
+    { mp4v2::impl::bmff::ILC_SAM,  "sam",  "Samaritan Aramaic" },
+    { mp4v2::impl::bmff::ILC_SAN,  "san",  "Sanskrit" },
+    { mp4v2::impl::bmff::ILC_SAS,  "sas",  "Sasak" },
+    { mp4v2::impl::bmff::ILC_SAT,  "sat",  "Santali" },
+    { mp4v2::impl::bmff::ILC_SCN,  "scn",  "Sicilian" },
+    { mp4v2::impl::bmff::ILC_SCO,  "sco",  "Scots" },
+    { mp4v2::impl::bmff::ILC_SEL,  "sel",  "Selkup" },
+    { mp4v2::impl::bmff::ILC_SEM,  "sem",  "Semitic (Other)" },
+    { mp4v2::impl::bmff::ILC_SGA,  "sga",  "Irish, Old (to 900)" },
+    { mp4v2::impl::bmff::ILC_SGN,  "sgn",  "Sign Languages" },
+    { mp4v2::impl::bmff::ILC_SHN,  "shn",  "Shan" },
+    { mp4v2::impl::bmff::ILC_SID,  "sid",  "Sidamo" },
+    { mp4v2::impl::bmff::ILC_SIN,  "sin",  "Sinhala; Sinhalese" },
+    { mp4v2::impl::bmff::ILC_SIO,  "sio",  "Siouan languages" },
+    { mp4v2::impl::bmff::ILC_SIT,  "sit",  "Sino-Tibetan (Other)" },
+    { mp4v2::impl::bmff::ILC_SLA,  "sla",  "Slavic (Other)" },
+    { mp4v2::impl::bmff::ILC_SLK,  "slk",  "Slovak" },
+    { mp4v2::impl::bmff::ILC_SLV,  "slv",  "Slovenian" },
+    { mp4v2::impl::bmff::ILC_SMA,  "sma",  "Southern Sami" },
+    { mp4v2::impl::bmff::ILC_SME,  "sme",  "Northern Sami" },
+    { mp4v2::impl::bmff::ILC_SMI,  "smi",  "Sami languages (Other)" },
+    { mp4v2::impl::bmff::ILC_SMJ,  "smj",  "Lule Sami" },
+    { mp4v2::impl::bmff::ILC_SMN,  "smn",  "Inari Sami" },
+    { mp4v2::impl::bmff::ILC_SMO,  "smo",  "Samoan" },
+    { mp4v2::impl::bmff::ILC_SMS,  "sms",  "Skolt Sami" },
+    { mp4v2::impl::bmff::ILC_SNA,  "sna",  "Shona" },
+    { mp4v2::impl::bmff::ILC_SND,  "snd",  "Sindhi" },
+    { mp4v2::impl::bmff::ILC_SNK,  "snk",  "Soninke" },
+    { mp4v2::impl::bmff::ILC_SOG,  "sog",  "Sogdian" },
+    { mp4v2::impl::bmff::ILC_SOM,  "som",  "Somali" },
+    { mp4v2::impl::bmff::ILC_SON,  "son",  "Songhai languages" },
+    { mp4v2::impl::bmff::ILC_SOT,  "sot",  "Sotho, Southern" },
+    { mp4v2::impl::bmff::ILC_SPA,  "spa",  "Spanish; Castilian" },
+    { mp4v2::impl::bmff::ILC_SRD,  "srd",  "Sardinian" },
+    { mp4v2::impl::bmff::ILC_SRN,  "srn",  "Sranan Tongo" },
+    { mp4v2::impl::bmff::ILC_SRP,  "srp",  "Serbian" },
+    { mp4v2::impl::bmff::ILC_SRR,  "srr",  "Serer" },
+    { mp4v2::impl::bmff::ILC_SSA,  "ssa",  "Nilo-Saharan (Other)" },
+    { mp4v2::impl::bmff::ILC_SSW,  "ssw",  "Swati" },
+    { mp4v2::impl::bmff::ILC_SUK,  "suk",  "Sukuma" },
+    { mp4v2::impl::bmff::ILC_SUN,  "sun",  "Sundanese" },
+    { mp4v2::impl::bmff::ILC_SUS,  "sus",  "Susu" },
+    { mp4v2::impl::bmff::ILC_SUX,  "sux",  "Sumerian" },
+    { mp4v2::impl::bmff::ILC_SWA,  "swa",  "Swahili" },
+    { mp4v2::impl::bmff::ILC_SWE,  "swe",  "Swedish" },
+    { mp4v2::impl::bmff::ILC_SYC,  "syc",  "Classical Syriac" },
+    { mp4v2::impl::bmff::ILC_SYR,  "syr",  "Syriac" },
+    { mp4v2::impl::bmff::ILC_TAH,  "tah",  "Tahitian" },
+    { mp4v2::impl::bmff::ILC_TAI,  "tai",  "Tai (Other)" },
+    { mp4v2::impl::bmff::ILC_TAM,  "tam",  "Tamil" },
+    { mp4v2::impl::bmff::ILC_TAT,  "tat",  "Tatar" },
+    { mp4v2::impl::bmff::ILC_TEL,  "tel",  "Telugu" },
+    { mp4v2::impl::bmff::ILC_TEM,  "tem",  "Timne" },
+    { mp4v2::impl::bmff::ILC_TER,  "ter",  "Tereno" },
+    { mp4v2::impl::bmff::ILC_TET,  "tet",  "Tetum" },
+    { mp4v2::impl::bmff::ILC_TGK,  "tgk",  "Tajik" },
+    { mp4v2::impl::bmff::ILC_TGL,  "tgl",  "Tagalog" },
+    { mp4v2::impl::bmff::ILC_THA,  "tha",  "Thai" },
+    { mp4v2::impl::bmff::ILC_BOD,  "bod",  "Tibetan" },
+    { mp4v2::impl::bmff::ILC_TIG,  "tig",  "Tigre" },
+    { mp4v2::impl::bmff::ILC_TIR,  "tir",  "Tigrinya" },
+    { mp4v2::impl::bmff::ILC_TIV,  "tiv",  "Tiv" },
+    { mp4v2::impl::bmff::ILC_TKL,  "tkl",  "Tokelau" },
+    { mp4v2::impl::bmff::ILC_TLH,  "tlh",  "Klingon; tlhIngan-Hol" },
+    { mp4v2::impl::bmff::ILC_TLI,  "tli",  "Tlingit" },
+    { mp4v2::impl::bmff::ILC_TMH,  "tmh",  "Tamashek" },
+    { mp4v2::impl::bmff::ILC_TOG,  "tog",  "Tonga (Nyasa)" },
+    { mp4v2::impl::bmff::ILC_TON,  "ton",  "Tonga (Tonga Islands)" },
+    { mp4v2::impl::bmff::ILC_TPI,  "tpi",  "Tok Pisin" },
+    { mp4v2::impl::bmff::ILC_TSI,  "tsi",  "Tsimshian" },
+    { mp4v2::impl::bmff::ILC_TSN,  "tsn",  "Tswana" },
+    { mp4v2::impl::bmff::ILC_TSO,  "tso",  "Tsonga" },
+    { mp4v2::impl::bmff::ILC_TUK,  "tuk",  "Turkmen" },
+    { mp4v2::impl::bmff::ILC_TUM,  "tum",  "Tumbuka" },
+    { mp4v2::impl::bmff::ILC_TUP,  "tup",  "Tupi languages" },
+    { mp4v2::impl::bmff::ILC_TUR,  "tur",  "Turkish" },
+    { mp4v2::impl::bmff::ILC_TUT,  "tut",  "Altaic (Other)" },
+    { mp4v2::impl::bmff::ILC_TVL,  "tvl",  "Tuvalu" },
+    { mp4v2::impl::bmff::ILC_TWI,  "twi",  "Twi" },
+    { mp4v2::impl::bmff::ILC_TYV,  "tyv",  "Tuvinian" },
+    { mp4v2::impl::bmff::ILC_UDM,  "udm",  "Udmurt" },
+    { mp4v2::impl::bmff::ILC_UGA,  "uga",  "Ugaritic" },
+    { mp4v2::impl::bmff::ILC_UIG,  "uig",  "Uighur; Uyghur" },
+    { mp4v2::impl::bmff::ILC_UKR,  "ukr",  "Ukrainian" },
+    { mp4v2::impl::bmff::ILC_UMB,  "umb",  "Umbundu" },
+    { mp4v2::impl::bmff::ILC_UND,  "und",  "Undetermined" },
+    { mp4v2::impl::bmff::ILC_URD,  "urd",  "Urdu" },
+    { mp4v2::impl::bmff::ILC_UZB,  "uzb",  "Uzbek" },
+    { mp4v2::impl::bmff::ILC_VAI,  "vai",  "Vai" },
+    { mp4v2::impl::bmff::ILC_VEN,  "ven",  "Venda" },
+    { mp4v2::impl::bmff::ILC_VIE,  "vie",  "Vietnamese" },
+    { mp4v2::impl::bmff::ILC_VOL,  "vol",  "Volapük" },
+    { mp4v2::impl::bmff::ILC_VOT,  "vot",  "Votic" },
+    { mp4v2::impl::bmff::ILC_WAK,  "wak",  "Wakashan languages" },
+    { mp4v2::impl::bmff::ILC_WAL,  "wal",  "Walamo" },
+    { mp4v2::impl::bmff::ILC_WAR,  "war",  "Waray" },
+    { mp4v2::impl::bmff::ILC_WAS,  "was",  "Washo" },
+    { mp4v2::impl::bmff::ILC_CYM,  "cym",  "Welsh" },
+    { mp4v2::impl::bmff::ILC_WEN,  "wen",  "Sorbian languages" },
+    { mp4v2::impl::bmff::ILC_WLN,  "wln",  "Walloon" },
+    { mp4v2::impl::bmff::ILC_WOL,  "wol",  "Wolof" },
+    { mp4v2::impl::bmff::ILC_XAL,  "xal",  "Kalmyk; Oirat" },
+    { mp4v2::impl::bmff::ILC_XHO,  "xho",  "Xhosa" },
+    { mp4v2::impl::bmff::ILC_YAO,  "yao",  "Yao" },
+    { mp4v2::impl::bmff::ILC_YAP,  "yap",  "Yapese" },
+    { mp4v2::impl::bmff::ILC_YID,  "yid",  "Yiddish" },
+    { mp4v2::impl::bmff::ILC_YOR,  "yor",  "Yoruba" },
+    { mp4v2::impl::bmff::ILC_YPK,  "ypk",  "Yupik languages" },
+    { mp4v2::impl::bmff::ILC_ZAP,  "zap",  "Zapotec" },
+    { mp4v2::impl::bmff::ILC_ZBL,  "zbl",  "Blissymbols; Blissymbolics; Bliss" },
+    { mp4v2::impl::bmff::ILC_ZEN,  "zen",  "Zenaga" },
+    { mp4v2::impl::bmff::ILC_ZHA,  "zha",  "Zhuang; Chuang" },
+    { mp4v2::impl::bmff::ILC_ZND,  "znd",  "Zande languages" },
+    { mp4v2::impl::bmff::ILC_ZUL,  "zul",  "Zulu" },
+    { mp4v2::impl::bmff::ILC_ZUN,  "zun",  "Zuni" },
+    { mp4v2::impl::bmff::ILC_ZXX,  "zxx",  "No linguistic content; Not applicable" },
+    { mp4v2::impl::bmff::ILC_ZZA,  "zza",  "Zaza; Dimili; Dimli; Kirdki; Kirmanjki; Zazaki" },
+
+    { mp4v2::impl::bmff::ILC_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace bmff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must come after static data init
+const EnumLanguageCode enumLanguageCode;
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::bmff
diff --git a/src/bmff/typebmff.h b/src/bmff/typebmff.h
new file mode 100644
index 0000000..4b69b7e
--- /dev/null
+++ b/src/bmff/typebmff.h
@@ -0,0 +1,528 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_BMFF_TYPE_H
+#define MP4V2_IMPL_BMFF_TYPE_H
+
+namespace mp4v2 { namespace impl { namespace bmff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// enumerated ISO-639-2/T language codes.
+enum LanguageCode {
+    ILC_UNDEFINED = 0,
+
+    ILC_AAR,
+    ILC_ABK,
+    ILC_ACE,
+    ILC_ACH,
+    ILC_ADA,
+    ILC_ADY,
+    ILC_AFA,
+    ILC_AFH,
+    ILC_AFR,
+    ILC_AIN,
+    ILC_AKA,
+    ILC_AKK,
+    ILC_SQI,
+    ILC_ALE,
+    ILC_ALG,
+    ILC_ALT,
+    ILC_AMH,
+    ILC_ANG,
+    ILC_ANP,
+    ILC_APA,
+    ILC_ARA,
+    ILC_ARC,
+    ILC_ARG,
+    ILC_HYE,
+    ILC_ARN,
+    ILC_ARP,
+    ILC_ART,
+    ILC_ARW,
+    ILC_ASM,
+    ILC_AST,
+    ILC_ATH,
+    ILC_AUS,
+    ILC_AVA,
+    ILC_AVE,
+    ILC_AWA,
+    ILC_AYM,
+    ILC_AZE,
+    ILC_BAD,
+    ILC_BAI,
+    ILC_BAK,
+    ILC_BAL,
+    ILC_BAM,
+    ILC_BAN,
+    ILC_EUS,
+    ILC_BAS,
+    ILC_BAT,
+    ILC_BEJ,
+    ILC_BEL,
+    ILC_BEM,
+    ILC_BEN,
+    ILC_BER,
+    ILC_BHO,
+    ILC_BIH,
+    ILC_BIK,
+    ILC_BIN,
+    ILC_BIS,
+    ILC_BLA,
+    ILC_BNT,
+    ILC_BOS,
+    ILC_BRA,
+    ILC_BRE,
+    ILC_BTK,
+    ILC_BUA,
+    ILC_BUG,
+    ILC_BUL,
+    ILC_MYA,
+    ILC_BYN,
+    ILC_CAD,
+    ILC_CAI,
+    ILC_CAR,
+    ILC_CAT,
+    ILC_CAU,
+    ILC_CEB,
+    ILC_CEL,
+    ILC_CHA,
+    ILC_CHB,
+    ILC_CHE,
+    ILC_CHG,
+    ILC_ZHO,
+    ILC_CHK,
+    ILC_CHM,
+    ILC_CHN,
+    ILC_CHO,
+    ILC_CHP,
+    ILC_CHR,
+    ILC_CHU,
+    ILC_CHV,
+    ILC_CHY,
+    ILC_CMC,
+    ILC_COP,
+    ILC_COR,
+    ILC_COS,
+    ILC_CPE,
+    ILC_CPF,
+    ILC_CPP,
+    ILC_CRE,
+    ILC_CRH,
+    ILC_CRP,
+    ILC_CSB,
+    ILC_CUS,
+    ILC_CES,
+    ILC_DAK,
+    ILC_DAN,
+    ILC_DAR,
+    ILC_DAY,
+    ILC_DEL,
+    ILC_DEN,
+    ILC_DGR,
+    ILC_DIN,
+    ILC_DIV,
+    ILC_DOI,
+    ILC_DRA,
+    ILC_DSB,
+    ILC_DUA,
+    ILC_DUM,
+    ILC_NLD,
+    ILC_DYU,
+    ILC_DZO,
+    ILC_EFI,
+    ILC_EGY,
+    ILC_EKA,
+    ILC_ELX,
+    ILC_ENG,
+    ILC_ENM,
+    ILC_EPO,
+    ILC_EST,
+    ILC_EWE,
+    ILC_EWO,
+    ILC_FAN,
+    ILC_FAO,
+    ILC_FAT,
+    ILC_FIJ,
+    ILC_FIL,
+    ILC_FIN,
+    ILC_FIU,
+    ILC_FON,
+    ILC_FRA,
+    ILC_FRM,
+    ILC_FRO,
+    ILC_FRR,
+    ILC_FRS,
+    ILC_FRY,
+    ILC_FUL,
+    ILC_FUR,
+    ILC_GAA,
+    ILC_GAY,
+    ILC_GBA,
+    ILC_GEM,
+    ILC_KAT,
+    ILC_DEU,
+    ILC_GEZ,
+    ILC_GIL,
+    ILC_GLA,
+    ILC_GLE,
+    ILC_GLG,
+    ILC_GLV,
+    ILC_GMH,
+    ILC_GOH,
+    ILC_GON,
+    ILC_GOR,
+    ILC_GOT,
+    ILC_GRB,
+    ILC_GRC,
+    ILC_ELL,
+    ILC_GRN,
+    ILC_GSW,
+    ILC_GUJ,
+    ILC_GWI,
+    ILC_HAI,
+    ILC_HAT,
+    ILC_HAU,
+    ILC_HAW,
+    ILC_HEB,
+    ILC_HER,
+    ILC_HIL,
+    ILC_HIM,
+    ILC_HIN,
+    ILC_HIT,
+    ILC_HMN,
+    ILC_HMO,
+    ILC_HRV,
+    ILC_HSB,
+    ILC_HUN,
+    ILC_HUP,
+    ILC_IBA,
+    ILC_IBO,
+    ILC_ISL,
+    ILC_IDO,
+    ILC_III,
+    ILC_IJO,
+    ILC_IKU,
+    ILC_ILE,
+    ILC_ILO,
+    ILC_INA,
+    ILC_INC,
+    ILC_IND,
+    ILC_INE,
+    ILC_INH,
+    ILC_IPK,
+    ILC_IRA,
+    ILC_IRO,
+    ILC_ITA,
+    ILC_JAV,
+    ILC_JBO,
+    ILC_JPN,
+    ILC_JPR,
+    ILC_JRB,
+    ILC_KAA,
+    ILC_KAB,
+    ILC_KAC,
+    ILC_KAL,
+    ILC_KAM,
+    ILC_KAN,
+    ILC_KAR,
+    ILC_KAS,
+    ILC_KAU,
+    ILC_KAW,
+    ILC_KAZ,
+    ILC_KBD,
+    ILC_KHA,
+    ILC_KHI,
+    ILC_KHM,
+    ILC_KHO,
+    ILC_KIK,
+    ILC_KIN,
+    ILC_KIR,
+    ILC_KMB,
+    ILC_KOK,
+    ILC_KOM,
+    ILC_KON,
+    ILC_KOR,
+    ILC_KOS,
+    ILC_KPE,
+    ILC_KRC,
+    ILC_KRL,
+    ILC_KRO,
+    ILC_KRU,
+    ILC_KUA,
+    ILC_KUM,
+    ILC_KUR,
+    ILC_KUT,
+    ILC_LAD,
+    ILC_LAH,
+    ILC_LAM,
+    ILC_LAO,
+    ILC_LAT,
+    ILC_LAV,
+    ILC_LEZ,
+    ILC_LIM,
+    ILC_LIN,
+    ILC_LIT,
+    ILC_LOL,
+    ILC_LOZ,
+    ILC_LTZ,
+    ILC_LUA,
+    ILC_LUB,
+    ILC_LUG,
+    ILC_LUI,
+    ILC_LUN,
+    ILC_LUO,
+    ILC_LUS,
+    ILC_MKD,
+    ILC_MAD,
+    ILC_MAG,
+    ILC_MAH,
+    ILC_MAI,
+    ILC_MAK,
+    ILC_MAL,
+    ILC_MAN,
+    ILC_MRI,
+    ILC_MAP,
+    ILC_MAR,
+    ILC_MAS,
+    ILC_MSA,
+    ILC_MDF,
+    ILC_MDR,
+    ILC_MEN,
+    ILC_MGA,
+    ILC_MIC,
+    ILC_MIN,
+    ILC_MIS,
+    ILC_MKH,
+    ILC_MLG,
+    ILC_MLT,
+    ILC_MNC,
+    ILC_MNI,
+    ILC_MNO,
+    ILC_MOH,
+    ILC_MON,
+    ILC_MOS,
+    ILC_MUL,
+    ILC_MUN,
+    ILC_MUS,
+    ILC_MWL,
+    ILC_MWR,
+    ILC_MYN,
+    ILC_MYV,
+    ILC_NAH,
+    ILC_NAI,
+    ILC_NAP,
+    ILC_NAU,
+    ILC_NAV,
+    ILC_NBL,
+    ILC_NDE,
+    ILC_NDO,
+    ILC_NDS,
+    ILC_NEP,
+    ILC_NEW,
+    ILC_NIA,
+    ILC_NIC,
+    ILC_NIU,
+    ILC_NNO,
+    ILC_NOB,
+    ILC_NOG,
+    ILC_NON,
+    ILC_NOR,
+    ILC_NQO,
+    ILC_NSO,
+    ILC_NUB,
+    ILC_NWC,
+    ILC_NYA,
+    ILC_NYM,
+    ILC_NYN,
+    ILC_NYO,
+    ILC_NZI,
+    ILC_OCI,
+    ILC_OJI,
+    ILC_ORI,
+    ILC_ORM,
+    ILC_OSA,
+    ILC_OSS,
+    ILC_OTA,
+    ILC_OTO,
+    ILC_PAA,
+    ILC_PAG,
+    ILC_PAL,
+    ILC_PAM,
+    ILC_PAN,
+    ILC_PAP,
+    ILC_PAU,
+    ILC_PEO,
+    ILC_FAS,
+    ILC_PHI,
+    ILC_PHN,
+    ILC_PLI,
+    ILC_POL,
+    ILC_PON,
+    ILC_POR,
+    ILC_PRA,
+    ILC_PRO,
+    ILC_PUS,
+    ILC_QUE,
+    ILC_RAJ,
+    ILC_RAP,
+    ILC_RAR,
+    ILC_ROA,
+    ILC_ROH,
+    ILC_ROM,
+    ILC_RON,
+    ILC_RUN,
+    ILC_RUP,
+    ILC_RUS,
+    ILC_SAD,
+    ILC_SAG,
+    ILC_SAH,
+    ILC_SAI,
+    ILC_SAL,
+    ILC_SAM,
+    ILC_SAN,
+    ILC_SAS,
+    ILC_SAT,
+    ILC_SCN,
+    ILC_SCO,
+    ILC_SEL,
+    ILC_SEM,
+    ILC_SGA,
+    ILC_SGN,
+    ILC_SHN,
+    ILC_SID,
+    ILC_SIN,
+    ILC_SIO,
+    ILC_SIT,
+    ILC_SLA,
+    ILC_SLK,
+    ILC_SLV,
+    ILC_SMA,
+    ILC_SME,
+    ILC_SMI,
+    ILC_SMJ,
+    ILC_SMN,
+    ILC_SMO,
+    ILC_SMS,
+    ILC_SNA,
+    ILC_SND,
+    ILC_SNK,
+    ILC_SOG,
+    ILC_SOM,
+    ILC_SON,
+    ILC_SOT,
+    ILC_SPA,
+    ILC_SRD,
+    ILC_SRN,
+    ILC_SRP,
+    ILC_SRR,
+    ILC_SSA,
+    ILC_SSW,
+    ILC_SUK,
+    ILC_SUN,
+    ILC_SUS,
+    ILC_SUX,
+    ILC_SWA,
+    ILC_SWE,
+    ILC_SYC,
+    ILC_SYR,
+    ILC_TAH,
+    ILC_TAI,
+    ILC_TAM,
+    ILC_TAT,
+    ILC_TEL,
+    ILC_TEM,
+    ILC_TER,
+    ILC_TET,
+    ILC_TGK,
+    ILC_TGL,
+    ILC_THA,
+    ILC_BOD,
+    ILC_TIG,
+    ILC_TIR,
+    ILC_TIV,
+    ILC_TKL,
+    ILC_TLH,
+    ILC_TLI,
+    ILC_TMH,
+    ILC_TOG,
+    ILC_TON,
+    ILC_TPI,
+    ILC_TSI,
+    ILC_TSN,
+    ILC_TSO,
+    ILC_TUK,
+    ILC_TUM,
+    ILC_TUP,
+    ILC_TUR,
+    ILC_TUT,
+    ILC_TVL,
+    ILC_TWI,
+    ILC_TYV,
+    ILC_UDM,
+    ILC_UGA,
+    ILC_UIG,
+    ILC_UKR,
+    ILC_UMB,
+    ILC_UND,
+    ILC_URD,
+    ILC_UZB,
+    ILC_VAI,
+    ILC_VEN,
+    ILC_VIE,
+    ILC_VOL,
+    ILC_VOT,
+    ILC_WAK,
+    ILC_WAL,
+    ILC_WAR,
+    ILC_WAS,
+    ILC_CYM,
+    ILC_WEN,
+    ILC_WLN,
+    ILC_WOL,
+    ILC_XAL,
+    ILC_XHO,
+    ILC_YAO,
+    ILC_YAP,
+    ILC_YID,
+    ILC_YOR,
+    ILC_YPK,
+    ILC_ZAP,
+    ILC_ZBL,
+    ILC_ZEN,
+    ILC_ZHA,
+    ILC_ZND,
+    ILC_ZUL,
+    ILC_ZUN,
+    ILC_ZXX,
+    ILC_ZZA,
+};
+
+typedef Enum<LanguageCode,ILC_UNDEFINED> EnumLanguageCode;
+MP4V2_EXPORT extern const EnumLanguageCode enumLanguageCode;
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::bmff
+
+#endif // MP4V2_IMPL_BMFF_TYPE_H
diff --git a/src/cmeta.cpp b/src/cmeta.cpp
new file mode 100644
index 0000000..b6a7d57
--- /dev/null
+++ b/src/cmeta.cpp
@@ -0,0 +1,1558 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2009, 2010, 2011.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      Rouven Wessling, mp4v2@rouvenwessling.de
+//      David Byron, dbyron0@gmail.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+
+using namespace mp4v2::impl;
+
+extern "C" {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsAddArtwork( const MP4Tags* tags, MP4TagArtwork* artwork )
+{
+    if( !tags || !tags->__handle || !artwork )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(tags->__handle);
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    try {
+        cpp.c_addArtwork( c, *artwork );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__);
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const MP4Tags*
+MP4TagsAlloc()
+{
+    MP4Tags* result = NULL;
+    itmf::Tags* m = NULL;
+
+    try {
+        m = new itmf::Tags();
+        m->c_alloc( result );
+        return result;
+    }
+    catch( std::bad_alloc ) {
+        // This could be a failure to allocate itmf::Tags or
+        // a failure to allocate inside c_alloc.
+        mp4v2::impl::log.errorf("%s: memory allocation error", __FUNCTION__);
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    if( result )
+        delete result;
+
+    if( m )
+        delete m;
+
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+MP4TagsFree( const MP4Tags* tags )
+{
+    if( !tags || !tags->__handle )
+        return;
+
+    itmf::Tags* cpp = static_cast<itmf::Tags*>(tags->__handle);
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    try {
+        cpp->c_free( c );
+        delete cpp;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsFetch( const MP4Tags* tags, MP4FileHandle hFile )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    if( !tags || !tags->__handle )
+        return false;
+
+    itmf::Tags* cpp = static_cast<itmf::Tags*>(tags->__handle);
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    try {
+        cpp->c_fetch( c, hFile );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsHasMetadata ( const MP4Tags* tags, bool *hasMetadata )
+{
+    if( !tags || !tags->__handle || !hasMetadata )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(tags->__handle);
+
+    (*hasMetadata) = cpp.hasMetadata;
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsRemoveArtwork( const MP4Tags* tags, uint32_t index )
+{
+    if( !tags || !tags->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(tags->__handle);
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    try {
+        cpp.c_removeArtwork( c, index );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsSetArtwork( const MP4Tags* tags, uint32_t index, MP4TagArtwork* artwork )
+{
+    if( !tags || !tags->__handle || !artwork)
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(tags->__handle);
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    try {
+        cpp.c_setArtwork( c, index, *artwork );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsStore( const MP4Tags* tags, MP4FileHandle hFile )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    if( !tags || !tags->__handle )
+        return false;
+
+    itmf::Tags* cpp = static_cast<itmf::Tags*>(tags->__handle);
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    try {
+        cpp->c_store( c, hFile );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4TagsSetName( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.name, c.name );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetArtist( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.artist, c.artist );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetAlbumArtist( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.albumArtist, c.albumArtist );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetAlbum( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.album, c.album );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetGrouping( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.grouping, c.grouping );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetComposer( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.composer, c.composer );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetComments( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.comments, c.comments );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetGenre( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.genre, c.genre );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetGenreType( const MP4Tags* m, const uint16_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setInteger( value, cpp.genreType, c.genreType );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetReleaseDate( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setString( value, cpp.releaseDate, c.releaseDate );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTrack( const MP4Tags* m, const MP4TagTrack* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setTrack( value, cpp.track, c.track );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetDisk( const MP4Tags* m, const MP4TagDisk* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setDisk( value, cpp.disk, c.disk );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTempo( const MP4Tags* m, const uint16_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.tempo, c.tempo );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetCompilation( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.compilation, c.compilation );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTVShow( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.tvShow, c.tvShow );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTVNetwork( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.tvNetwork, c.tvNetwork );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTVEpisodeID( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.tvEpisodeID, c.tvEpisodeID );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTVSeason( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.tvSeason, c.tvSeason );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetTVEpisode( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.tvEpisode, c.tvEpisode );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetSortName( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.sortName, c.sortName );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetSortArtist( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.sortArtist, c.sortArtist );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetSortAlbumArtist( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.sortAlbumArtist, c.sortAlbumArtist );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetSortAlbum( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.sortAlbum, c.sortAlbum );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetSortComposer( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.sortComposer, c.sortComposer );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetSortTVShow( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.sortTVShow, c.sortTVShow );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetDescription( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.description, c.description );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetLongDescription( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.longDescription, c.longDescription );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetLyrics( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.lyrics, c.lyrics );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetCopyright( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.copyright, c.copyright );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetEncodingTool( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.encodingTool, c.encodingTool );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetEncodedBy( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.encodedBy, c.encodedBy );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetPurchaseDate( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.purchaseDate, c.purchaseDate );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetPodcast( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.podcast, c.podcast );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetKeywords( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.keywords, c.keywords );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetCategory( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.category, c.category );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetHDVideo( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.hdVideo, c.hdVideo );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetMediaType( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.mediaType, c.mediaType );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetContentRating( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.contentRating, c.contentRating );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetGapless( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.gapless, c.gapless );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetITunesAccount( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.iTunesAccount, c.iTunesAccount );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetITunesAccountType( const MP4Tags* m, const uint8_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.iTunesAccountType, c.iTunesAccountType );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetITunesCountry( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.iTunesCountry, c.iTunesCountry );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetContentID( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.contentID, c.contentID );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetArtistID( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.artistID, c.artistID );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetPlaylistID( const MP4Tags* m, const uint64_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setInteger( value, cpp.playlistID, c.playlistID );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetGenreID( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setInteger( value, cpp.genreID, c.genreID );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetComposerID( const MP4Tags* m, const uint32_t* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+
+    try {
+        cpp.c_setInteger( value, cpp.composerID, c.composerID );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+bool
+MP4TagsSetXID( const MP4Tags* m, const char* value )
+{
+    if( !m || !m->__handle )
+        return false;
+
+    itmf::Tags& cpp = *static_cast<itmf::Tags*>(m->__handle);
+    MP4Tags& c = *const_cast<MP4Tags*>(m);
+    
+    try {
+        cpp.c_setString( value, cpp.xid, c.xid );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfItem*
+MP4ItmfItemAlloc( const char* code, uint32_t numData )
+{
+    return itmf::genericItemAlloc( code, numData );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+MP4ItmfItemFree( MP4ItmfItem* item )
+{
+    itmf::genericItemFree( item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+MP4ItmfItemListFree( MP4ItmfItemList* list )
+{
+    itmf::genericItemListFree( list );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfItemList*
+MP4ItmfGetItems( MP4FileHandle hFile )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return NULL;
+
+    try {
+        return itmf::genericGetItems( *(MP4File*)hFile );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfItemList*
+MP4ItmfGetItemsByCode( MP4FileHandle hFile, const char* code )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return NULL;
+
+    try {
+        return itmf::genericGetItemsByCode( *(MP4File*)hFile, code );
+    }   
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed",__FUNCTION__);
+    }
+
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfItemList*
+MP4ItmfGetItemsByMeaning( MP4FileHandle hFile, const char* meaning, const char* name )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return NULL;
+
+    if( !meaning )
+        return NULL;
+
+    try {
+        return itmf::genericGetItemsByMeaning( *(MP4File*)hFile, meaning, name ? name : "" );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4ItmfAddItem( MP4FileHandle hFile, const MP4ItmfItem* item )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return itmf::genericAddItem( *(MP4File*)hFile, item );
+    }
+    catch( Exception* x) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4ItmfSetItem( MP4FileHandle hFile, const MP4ItmfItem* item )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return itmf::genericSetItem( *(MP4File*)hFile, item );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+MP4ItmfRemoveItem( MP4FileHandle hFile, const MP4ItmfItem* item )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return itmf::genericRemoveItem( *(MP4File*)hFile, item );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // extern "C"
diff --git a/src/descriptors.cpp b/src/descriptors.cpp
new file mode 100644
index 0000000..e83fc66
--- /dev/null
+++ b/src/descriptors.cpp
@@ -0,0 +1,616 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4BaseDescriptor::MP4BaseDescriptor (MP4Atom& parentAtom, uint8_t tag) : MP4Descriptor(parentAtom, tag)
+{
+    switch (tag) {
+    case MP4ESIDIncDescrTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "id"));
+        break;
+    case MP4ESIDRefDescrTag:
+        AddProperty( /* 0 */
+            new MP4Integer16Property(parentAtom, "refIndex"));
+        break;
+    case MP4IPIPtrDescrTag:
+        AddProperty( /* 0 */
+            new MP4Integer16Property(parentAtom, "IPIESId"));
+        break;
+    case MP4SupplContentIdDescrTag:
+        AddProperty( /* 0 */
+            new MP4BytesProperty(parentAtom, "languageCode", 3));
+        AddProperty( /* 1 */
+            new MP4StringProperty(parentAtom, "title", Counted));
+        AddProperty( /* 2 */
+            new MP4StringProperty(parentAtom, "value", Counted));
+        break;
+    case MP4IPMPPtrDescrTag:
+        AddProperty( /* 0 */
+            new MP4Integer8Property(parentAtom, "IPMPDescriptorId"));
+        break;
+    case MP4ExtProfileLevelDescrTag:
+        AddProperty( /* 0 */
+            new MP4Integer8Property(parentAtom, "profileLevelIndicationIndex"));
+        AddProperty( /* 1 */
+            new MP4Integer8Property(parentAtom, "ODProfileLevelIndication"));
+        AddProperty( /* 2 */
+            new MP4Integer8Property(parentAtom, "sceneProfileLevelIndication"));
+        AddProperty( /* 3 */
+            new MP4Integer8Property(parentAtom, "audioProfileLevelIndication"));
+        AddProperty( /* 4 */
+            new MP4Integer8Property(parentAtom, "visualProfileLevelIndication"));
+        AddProperty( /* 5 */
+            new MP4Integer8Property(parentAtom, "graphicsProfileLevelIndication"));
+        AddProperty( /* 6 */
+            new MP4Integer8Property(parentAtom, "MPEGJProfileLevelIndication"));
+        break;
+    default:
+        log.errorf("%s: \"%s\": error in base descriptor - tag %u", __FUNCTION__,
+                   m_parentAtom.GetFile().GetFilename().c_str(), tag);
+        break;
+
+    }
+}
+
+MP4BytesDescriptor::MP4BytesDescriptor (MP4Atom& parentAtom, uint8_t tag) : MP4Descriptor(parentAtom, tag)
+{
+    m_size_offset = 0;
+    m_bytes_index = 0;
+    if (tag >= MP4ExtDescrTagsStart && tag <= MP4ExtDescrTagsEnd) {
+        AddProperty( /* 0 */
+            new MP4BytesProperty(parentAtom, "data"));
+    } else {
+        switch (tag) {
+        case MP4DecSpecificDescrTag:
+            AddProperty( /* 0 */
+                new MP4BytesProperty(parentAtom, "info"));
+            // no change to m_size
+            break;
+        case MP4IPMPDescrTag:
+            AddProperty( /* 0 */
+                new MP4Integer8Property(parentAtom, "IPMPDescriptorId"));
+            AddProperty( /* 1 */
+                new MP4Integer16Property(parentAtom, "IPMPSType"));
+            AddProperty( /* 2 */
+                new MP4BytesProperty(parentAtom, "IPMPData"));
+            /* note: if IPMPSType == 0, IPMPData is an URL */
+            m_size_offset = 3;
+            m_bytes_index = 2;
+            break;
+        case MP4RegistrationDescrTag:
+            AddProperty( /* 0 */
+                new MP4Integer32Property(parentAtom, "formatIdentifier"));
+            AddProperty( /* 1 */
+                new MP4BytesProperty(parentAtom, "additionalIdentificationInfo"));
+            m_size_offset = 4;
+            m_bytes_index = 1;
+            break;
+        default:
+            log.errorf("%s: \"%s\": error in bytes descriptor - tag %u", __FUNCTION__,
+                       m_parentAtom.GetFile().GetFilename().c_str(), tag);
+            break;
+        }
+    }
+}
+
+void MP4BytesDescriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    /* byte properties need to know how long they are before reading */
+    ((MP4BytesProperty*)m_pProperties[m_bytes_index])->SetValueSize(m_size - m_size_offset);
+
+    ReadProperties(file);
+}
+MP4IODescriptor::MP4IODescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4FileIODescrTag)
+{
+    /* N.B. other member functions depend on the property indicies */
+    AddProperty( /* 0 */
+        new MP4BitfieldProperty(parentAtom, "objectDescriptorId", 10));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "URLFlag", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "includeInlineProfileLevelFlag", 1));
+    AddProperty( /* 3 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 4));
+    AddProperty( /* 4 */
+        new MP4StringProperty(parentAtom, "URL", Counted));
+    AddProperty( /* 5 */
+        new MP4Integer8Property(parentAtom, "ODProfileLevelId"));
+    AddProperty( /* 6 */
+        new MP4Integer8Property(parentAtom, "sceneProfileLevelId"));
+    AddProperty( /* 7 */
+        new MP4Integer8Property(parentAtom, "audioProfileLevelId"));
+    AddProperty( /* 8 */
+        new MP4Integer8Property(parentAtom, "visualProfileLevelId"));
+    AddProperty( /* 9 */
+        new MP4Integer8Property(parentAtom, "graphicsProfileLevelId"));
+    AddProperty( /* 10 */
+        new MP4DescriptorProperty(parentAtom, "esIds",
+                                  MP4ESIDIncDescrTag, 0, Required, Many));
+    AddProperty( /* 11 */
+        new MP4DescriptorProperty(parentAtom, "ociDescr",
+                                  MP4OCIDescrTagsStart, MP4OCIDescrTagsEnd, Optional, Many));
+    AddProperty( /* 12 */
+        new MP4DescriptorProperty(parentAtom, "ipmpDescrPtr",
+                                  MP4IPMPPtrDescrTag, 0, Optional, Many));
+    AddProperty( /* 13 */
+        new MP4DescriptorProperty(parentAtom, "extDescr",
+                                  MP4ExtDescrTagsStart, MP4ExtDescrTagsEnd, Optional, Many));
+
+    SetReadMutate(2);
+}
+
+void MP4IODescriptor::Generate()
+{
+    ((MP4BitfieldProperty*)m_pProperties[0])->SetValue(1);
+    ((MP4BitfieldProperty*)m_pProperties[3])->SetValue(0xF);
+    for (uint32_t i = 5; i <= 9; i++) {
+        ((MP4Integer8Property*)m_pProperties[i])->SetValue(0xFF);
+    }
+}
+
+void MP4IODescriptor::Mutate()
+{
+    bool urlFlag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+
+    m_pProperties[4]->SetImplicit(!urlFlag);
+    for (uint32_t i = 5; i <= 12; i++) {
+        m_pProperties[i]->SetImplicit(urlFlag);
+    }
+}
+
+MP4ODescriptor::MP4ODescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4FileODescrTag)
+{
+    /* N.B. other member functions depend on the property indicies */
+    AddProperty( /* 0 */
+        new MP4BitfieldProperty(parentAtom, "objectDescriptorId", 10));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "URLFlag", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 5));
+    AddProperty( /* 3 */
+        new MP4StringProperty(parentAtom, "URL", Counted));
+    AddProperty( /* 4 */
+        new MP4DescriptorProperty(parentAtom, "esIds",
+                                  MP4ESIDRefDescrTag, 0, Required, Many));
+    AddProperty( /* 5 */
+        new MP4DescriptorProperty(parentAtom, "ociDescr",
+                                  MP4OCIDescrTagsStart, MP4OCIDescrTagsEnd, Optional, Many));
+    AddProperty( /* 6 */
+        new MP4DescriptorProperty(parentAtom, "ipmpDescrPtr",
+                                  MP4IPMPPtrDescrTag, 0, Optional, Many));
+    AddProperty( /* 7 */
+        new MP4DescriptorProperty(parentAtom, "extDescr",
+                                  MP4ExtDescrTagsStart, MP4ExtDescrTagsEnd, Optional, Many));
+
+    SetReadMutate(2);
+}
+
+void MP4ODescriptor::Generate()
+{
+    ((MP4BitfieldProperty*)m_pProperties[2])->SetValue(0x1F);
+}
+
+void MP4ODescriptor::Mutate()
+{
+    bool urlFlag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+
+    m_pProperties[3]->SetImplicit(!urlFlag);
+    for (uint32_t i = 4; i <= 6; i++) {
+        m_pProperties[i]->SetImplicit(urlFlag);
+    }
+}
+
+MP4ESDescriptor::MP4ESDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4ESDescrTag)
+{
+    /* N.B. other class functions depend on the property indicies */
+    AddProperty( /* 0 */
+        new MP4Integer16Property(parentAtom, "ESID"));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "streamDependenceFlag", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "URLFlag", 1));
+    AddProperty( /* 3 */
+        new MP4BitfieldProperty(parentAtom, "OCRstreamFlag", 1));
+    AddProperty( /* 4 */
+        new MP4BitfieldProperty(parentAtom, "streamPriority", 5));
+    AddProperty( /* 5 */
+        new MP4Integer16Property(parentAtom, "dependsOnESID"));
+    AddProperty( /* 6 */
+        new MP4StringProperty(parentAtom, "URL", Counted));
+    AddProperty( /* 7 */
+        new MP4Integer16Property(parentAtom, "OCRESID"));
+    AddProperty( /* 8 */
+        new MP4DescriptorProperty(parentAtom, "decConfigDescr",
+                                  MP4DecConfigDescrTag, 0, Required, OnlyOne));
+    AddProperty( /* 9 */
+        new MP4DescriptorProperty(parentAtom, "slConfigDescr",
+                                  MP4SLConfigDescrTag, 0, Required, OnlyOne));
+    AddProperty( /* 10 */
+        new MP4DescriptorProperty(parentAtom, "ipiPtr",
+                                  MP4IPIPtrDescrTag, 0, Optional, OnlyOne));
+    AddProperty( /* 11 */
+        new MP4DescriptorProperty(parentAtom, "ipIds",
+                                  MP4ContentIdDescrTag, MP4SupplContentIdDescrTag, Optional, Many));
+    AddProperty( /* 12 */
+        new MP4DescriptorProperty(parentAtom, "ipmpDescrPtr",
+                                  MP4IPMPPtrDescrTag, 0, Optional, Many));
+    AddProperty( /* 13 */
+        new MP4DescriptorProperty(parentAtom, "langDescr",
+                                  MP4LanguageDescrTag, 0, Optional, Many));
+    AddProperty( /* 14 */
+        new MP4DescriptorProperty(parentAtom, "qosDescr",
+                                  MP4QosDescrTag, 0, Optional, OnlyOne));
+    AddProperty( /* 15 */
+        new MP4DescriptorProperty(parentAtom, "regDescr",
+                                  MP4RegistrationDescrTag, 0, Optional, OnlyOne));
+    AddProperty( /* 16 */
+        new MP4DescriptorProperty(parentAtom, "extDescr",
+                                  MP4ExtDescrTagsStart, MP4ExtDescrTagsEnd, Optional, Many));
+
+    SetReadMutate(5);
+}
+
+void MP4ESDescriptor::Mutate()
+{
+    bool streamDependFlag =
+        ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+    m_pProperties[5]->SetImplicit(!streamDependFlag);
+
+    bool urlFlag =
+        ((MP4BitfieldProperty*)m_pProperties[2])->GetValue();
+    m_pProperties[6]->SetImplicit(!urlFlag);
+
+    bool ocrFlag =
+        ((MP4BitfieldProperty*)m_pProperties[3])->GetValue();
+    m_pProperties[7]->SetImplicit(!ocrFlag);
+}
+
+MP4DecConfigDescriptor::MP4DecConfigDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4DecConfigDescrTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer8Property(parentAtom, "objectTypeId"));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "streamType", 6));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "upStream", 1));
+    AddProperty( /* 3 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 1));
+    AddProperty( /* 4 */
+        new MP4BitfieldProperty(parentAtom, "bufferSizeDB", 24));
+    AddProperty( /* 5 */
+        new MP4Integer32Property(parentAtom, "maxBitrate"));
+    AddProperty( /* 6 */
+        new MP4Integer32Property(parentAtom, "avgBitrate"));
+    AddProperty( /* 7 */
+        new MP4DescriptorProperty(parentAtom, "decSpecificInfo",
+                                  MP4DecSpecificDescrTag, 0, Optional, OnlyOne));
+    AddProperty( /* 8 */
+        new MP4DescriptorProperty(parentAtom, "profileLevelIndicationIndexDescr",
+                                  MP4ExtProfileLevelDescrTag, 0, Optional, Many));
+}
+
+void MP4DecConfigDescriptor::Generate()
+{
+    ((MP4BitfieldProperty*)m_pProperties[3])->SetValue(1);
+}
+
+MP4SLConfigDescriptor::MP4SLConfigDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4SLConfigDescrTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer8Property(parentAtom, "predefined"));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "useAccessUnitStartFlag", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "useAccessUnitEndFlag", 1));
+    AddProperty( /* 3 */
+        new MP4BitfieldProperty(parentAtom, "useRandomAccessPointFlag", 1));
+    AddProperty( /* 4 */
+        new MP4BitfieldProperty(parentAtom, "hasRandomAccessUnitsOnlyFlag", 1));
+    AddProperty( /* 5 */
+        new MP4BitfieldProperty(parentAtom, "usePaddingFlag", 1));
+    AddProperty( /* 6 */
+        new MP4BitfieldProperty(parentAtom, "useTimeStampsFlag", 1));
+    AddProperty( /* 7 */
+        new MP4BitfieldProperty(parentAtom, "useIdleFlag", 1));
+    AddProperty( /* 8 */
+        new MP4BitfieldProperty(parentAtom, "durationFlag", 1));
+    AddProperty( /* 9 */
+        new MP4Integer32Property(parentAtom, "timeStampResolution"));
+    AddProperty( /* 10 */
+        new MP4Integer32Property(parentAtom, "OCRResolution"));
+    AddProperty( /* 11 */
+        new MP4Integer8Property(parentAtom, "timeStampLength"));
+    AddProperty( /* 12 */
+        new MP4Integer8Property(parentAtom, "OCRLength"));
+    AddProperty( /* 13 */
+        new MP4Integer8Property(parentAtom, "AULength"));
+    AddProperty( /* 14 */
+        new MP4Integer8Property(parentAtom, "instantBitrateLength"));
+    AddProperty( /* 15 */
+        new MP4BitfieldProperty(parentAtom, "degradationPriortyLength", 4));
+    AddProperty( /* 16 */
+        new MP4BitfieldProperty(parentAtom, "AUSeqNumLength", 5));
+    AddProperty( /* 17 */
+        new MP4BitfieldProperty(parentAtom, "packetSeqNumLength", 5));
+    AddProperty( /* 18 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 2));
+
+    // if durationFlag
+    AddProperty( /* 19 */
+        new MP4Integer32Property(parentAtom, "timeScale"));
+    AddProperty( /* 20 */
+        new MP4Integer16Property(parentAtom, "accessUnitDuration"));
+    AddProperty( /* 21 */
+        new MP4Integer16Property(parentAtom, "compositionUnitDuration"));
+
+    // if !useTimeStampsFlag
+    AddProperty( /* 22 */
+        new MP4BitfieldProperty(parentAtom, "startDecodingTimeStamp", 64));
+    AddProperty( /* 23 */
+        new MP4BitfieldProperty(parentAtom, "startCompositionTimeStamp", 64));
+}
+
+void MP4SLConfigDescriptor::Generate()
+{
+    // by default all tracks in an mp4 file
+    // use predefined SLConfig descriptor == 2
+    ((MP4Integer8Property*)m_pProperties[0])->SetValue(2);
+
+    // which implies UseTimestampsFlag = 1
+    ((MP4BitfieldProperty*)m_pProperties[6])->SetValue(1);
+
+    // reserved = 3
+    ((MP4BitfieldProperty*)m_pProperties[18])->SetValue(3);
+}
+
+void MP4SLConfigDescriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    // read the first property, 'predefined'
+    ReadProperties(file, 0, 1);
+
+    // if predefined == 0
+    if (((MP4Integer8Property*)m_pProperties[0])->GetValue() == 0) {
+
+        /* read the next 18 properties */
+        ReadProperties(file, 1, 18);
+    }
+
+    // now mutate
+    Mutate();
+
+    // and read the remaining properties
+    ReadProperties(file, 19);
+}
+
+void MP4SLConfigDescriptor::Mutate()
+{
+    uint32_t i;
+    uint8_t predefined =
+        ((MP4Integer8Property*)m_pProperties[0])->GetValue();
+
+    if (predefined) {
+        // properties 1-18 are implicit
+        for (i = 1; i < m_pProperties.Size(); i++) {
+            m_pProperties[i]->SetImplicit(true);
+        }
+
+        if (predefined == 1) {
+            // UseTimestampsFlag = 0
+            ((MP4BitfieldProperty*)m_pProperties[6])->SetValue(0);
+
+            // TimestampResolution = 1000
+            ((MP4Integer32Property*)m_pProperties[9])->SetValue(1000);
+
+            // TimeStampLength = 32
+            ((MP4Integer8Property*)m_pProperties[11])->SetValue(32);
+
+        } else if (predefined == 2) {
+            // UseTimestampsFlag = 1
+            ((MP4BitfieldProperty*)m_pProperties[6])->SetValue(1);
+        }
+    } else {
+#if 1
+        for (i = 1; i <= 18; i++) {
+            m_pProperties[i]->SetImplicit(false);
+        }
+        ((MP4BitfieldProperty*)m_pProperties[18])->SetValue(3);
+#endif
+    }
+
+    bool durationFlag =
+        ((MP4BitfieldProperty*)m_pProperties[8])->GetValue();
+
+    for (i = 19; i <= 21; i++) {
+        m_pProperties[i]->SetImplicit(!durationFlag);
+    }
+
+    bool useTimeStampsFlag =
+        ((MP4BitfieldProperty*)m_pProperties[6])->GetValue();
+
+    for (i = 22; i <= 23; i++) {
+        m_pProperties[i]->SetImplicit(useTimeStampsFlag);
+
+        uint8_t timeStampLength = min((uint8_t)64,
+                                      ((MP4Integer8Property*)m_pProperties[11])->GetValue());
+
+        ((MP4BitfieldProperty*)m_pProperties[i])->SetNumBits(timeStampLength);
+
+        // handle a nonsensical situation gracefully
+        if (timeStampLength == 0) {
+            m_pProperties[i]->SetImplicit(true);
+        }
+    }
+}
+
+MP4ContentIdDescriptor::MP4ContentIdDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4ContentIdDescrTag)
+{
+    AddProperty( /* 0 */
+        new MP4BitfieldProperty(parentAtom, "compatibility", 2));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "contentTypeFlag", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "contentIdFlag", 1));
+    AddProperty( /* 3 */
+        new MP4BitfieldProperty(parentAtom, "protectedContent", 1));
+    AddProperty( /* 4 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 3));
+    AddProperty( /* 5 */
+        new MP4Integer8Property(parentAtom, "contentType"));
+    AddProperty( /* 6 */
+        new MP4Integer8Property(parentAtom, "contentIdType"));
+    AddProperty( /* 7 */
+        new MP4BytesProperty(parentAtom, "contentId"));
+}
+
+void MP4ContentIdDescriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    /* read the first property, 'compatiblity' */
+    ReadProperties(file, 0, 1);
+
+    /* if compatiblity != 0 */
+    if (((MP4Integer8Property*)m_pProperties[0])->GetValue() != 0) {
+        /* we don't understand it */
+        log.verbose1f("incompatible content id descriptor");
+        return;
+    }
+
+    /* read the next four properties */
+    ReadProperties(file, 1, 4);
+
+    /* which allows us to reconfigure ourselves */
+    Mutate();
+
+    bool contentTypeFlag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+
+    bool contentIdFlag = ((MP4BitfieldProperty*)m_pProperties[2])->GetValue();
+
+    if (contentIdFlag) {
+
+        uint32_t cIdOffset = 2;
+
+        if (contentTypeFlag) {
+
+            cIdOffset++;
+
+        }
+
+        ((MP4BytesProperty*)m_pProperties[7])->SetValueSize(m_size - cIdOffset);
+
+    }
+
+
+
+    /* read the remaining properties */
+    ReadProperties(file, 5);
+}
+
+void MP4ContentIdDescriptor::Mutate()
+{
+    bool contentTypeFlag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+    m_pProperties[5]->SetImplicit(!contentTypeFlag);
+
+    bool contentIdFlag = ((MP4BitfieldProperty*)m_pProperties[2])->GetValue();
+    m_pProperties[6]->SetImplicit(!contentIdFlag);
+    m_pProperties[7]->SetImplicit(!contentIdFlag);
+
+}
+
+MP4Descriptor* MP4DescriptorProperty::CreateDescriptor(MP4Atom& parentAtom, uint8_t tag)
+{
+    MP4Descriptor* pDescriptor = NULL;
+
+    switch (tag) {
+    case MP4ESDescrTag:
+        pDescriptor = new MP4ESDescriptor(parentAtom);
+        break;
+    case MP4DecConfigDescrTag:
+        pDescriptor = new MP4DecConfigDescriptor(parentAtom);
+        break;
+    case MP4DecSpecificDescrTag:
+    case MP4IPMPDescrTag:
+    case MP4RegistrationDescrTag:
+        pDescriptor = new MP4BytesDescriptor(parentAtom, tag);
+        break;
+    case MP4SLConfigDescrTag:
+        pDescriptor = new MP4SLConfigDescriptor(parentAtom);
+        break;
+    case MP4ContentIdDescrTag:
+        pDescriptor = new MP4ContentIdDescriptor(parentAtom);
+        break;
+    case MP4ESIDIncDescrTag:
+    case MP4ESIDRefDescrTag:
+    case MP4IPIPtrDescrTag:
+    case MP4SupplContentIdDescrTag:
+    case MP4IPMPPtrDescrTag:
+    case MP4ExtProfileLevelDescrTag:
+        pDescriptor = new MP4BaseDescriptor(parentAtom, tag);
+        break;
+    case MP4QosDescrTag:
+        pDescriptor = new MP4QosDescriptorBase(parentAtom, MP4QosDescrTag);
+        break;
+    case MP4IODescrTag:
+    case MP4FileIODescrTag:
+        pDescriptor = new MP4IODescriptor(parentAtom);
+        pDescriptor->SetTag(tag);
+        break;
+    case MP4ODescrTag:
+    case MP4FileODescrTag:
+        pDescriptor = new MP4ODescriptor(parentAtom);
+        pDescriptor->SetTag(tag);
+        break;
+    }
+
+    if (pDescriptor == NULL) {
+        if (tag >= MP4OCIDescrTagsStart && tag <= MP4OCIDescrTagsEnd) {
+            pDescriptor = CreateOCIDescriptor(parentAtom, tag);
+        }
+
+        if (tag >= MP4ExtDescrTagsStart && tag <= MP4ExtDescrTagsEnd) {
+            pDescriptor = new MP4BytesDescriptor(parentAtom, tag);
+        }
+    }
+
+    return pDescriptor;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/descriptors.h b/src/descriptors.h
new file mode 100644
index 0000000..6f1cf2e
--- /dev/null
+++ b/src/descriptors.h
@@ -0,0 +1,178 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_DESCRIPTORS_H
+#define MP4V2_IMPL_DESCRIPTORS_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint8_t MP4ODescrTag                  = 0x01;
+const uint8_t MP4IODescrTag                 = 0x02;
+const uint8_t MP4ESDescrTag                 = 0x03;
+const uint8_t MP4DecConfigDescrTag          = 0x04;
+const uint8_t MP4DecSpecificDescrTag        = 0x05;
+const uint8_t MP4SLConfigDescrTag           = 0x06;
+const uint8_t MP4ContentIdDescrTag          = 0x07;
+const uint8_t MP4SupplContentIdDescrTag     = 0x08;
+const uint8_t MP4IPIPtrDescrTag             = 0x09;
+const uint8_t MP4IPMPPtrDescrTag            = 0x0A;
+const uint8_t MP4IPMPDescrTag               = 0x0B;
+const uint8_t MP4RegistrationDescrTag       = 0x0D;
+const uint8_t MP4ESIDIncDescrTag            = 0x0E;
+const uint8_t MP4ESIDRefDescrTag            = 0x0F;
+const uint8_t MP4FileIODescrTag             = 0x10;
+const uint8_t MP4FileODescrTag              = 0x11;
+const uint8_t MP4ExtProfileLevelDescrTag    = 0x13;
+const uint8_t MP4ExtDescrTagsStart          = 0x80;
+const uint8_t MP4ExtDescrTagsEnd            = 0xFE;
+
+class MP4BaseDescriptor : public MP4Descriptor {
+public:
+    MP4BaseDescriptor(MP4Atom& parentAtom, uint8_t tag);
+private:
+    MP4BaseDescriptor();
+    MP4BaseDescriptor ( const MP4BaseDescriptor &src );
+    MP4BaseDescriptor &operator= ( const MP4BaseDescriptor &src );
+};
+
+class MP4BytesDescriptor : public MP4Descriptor {
+public:
+    MP4BytesDescriptor(MP4Atom& parentAtom, uint8_t tag);
+    void Read(MP4File& file);
+protected:
+    uint32_t m_size_offset; // size to adjust the size for the bytes property
+    uint32_t m_bytes_index; // index into properties for bytes property
+private:
+    MP4BytesDescriptor();
+    MP4BytesDescriptor ( const MP4BytesDescriptor &src );
+    MP4BytesDescriptor &operator= ( const MP4BytesDescriptor &src );
+};
+
+class MP4IODescriptor : public MP4Descriptor {
+public:
+    MP4IODescriptor(MP4Atom& parentAtom);
+    void Generate();
+protected:
+    void Mutate();
+private:
+    MP4IODescriptor();
+    MP4IODescriptor ( const MP4IODescriptor &src );
+    MP4IODescriptor &operator= ( const MP4IODescriptor &src );
+};
+
+class MP4ODescriptor : public MP4Descriptor {
+public:
+    MP4ODescriptor(MP4Atom& parentAtom);
+    void Generate();
+protected:
+    void Mutate();
+private:
+    MP4ODescriptor();
+    MP4ODescriptor ( const MP4ODescriptor &src );
+    MP4ODescriptor &operator= ( const MP4ODescriptor &src );
+};
+
+
+class MP4ESDescriptor : public MP4Descriptor {
+public:
+    MP4ESDescriptor(MP4Atom& parentAtom);
+protected:
+    void Mutate();
+private:
+    MP4ESDescriptor();
+    MP4ESDescriptor ( const MP4ESDescriptor &src );
+    MP4ESDescriptor &operator= ( const MP4ESDescriptor &src );
+};
+
+class MP4DecConfigDescriptor : public MP4Descriptor {
+public:
+    MP4DecConfigDescriptor(MP4Atom& parentAtom);
+    void Generate();
+private:
+    MP4DecConfigDescriptor();
+    MP4DecConfigDescriptor ( const MP4DecConfigDescriptor &src );
+    MP4DecConfigDescriptor &operator= ( const MP4DecConfigDescriptor &src );
+};
+
+
+class MP4SLConfigDescriptor : public MP4Descriptor {
+public:
+    MP4SLConfigDescriptor(MP4Atom& parentAtom);
+    void Generate();
+    void Read(MP4File& file);
+protected:
+    void Mutate();
+private:
+    MP4SLConfigDescriptor();
+    MP4SLConfigDescriptor ( const MP4SLConfigDescriptor &src );
+    MP4SLConfigDescriptor &operator= ( const MP4SLConfigDescriptor &src );
+};
+
+class MP4IPIPtrDescriptor : public MP4Descriptor {
+public:
+    MP4IPIPtrDescriptor(MP4Atom& parentAtom);
+private:
+    MP4IPIPtrDescriptor();
+    MP4IPIPtrDescriptor ( const MP4IPIPtrDescriptor &src );
+    MP4IPIPtrDescriptor &operator= ( const MP4IPIPtrDescriptor &src );
+};
+
+class MP4ContentIdDescriptor : public MP4Descriptor {
+public:
+    MP4ContentIdDescriptor(MP4Atom& parentAtom);
+    void Read(MP4File& file);
+protected:
+    void Mutate();
+private:
+    MP4ContentIdDescriptor();
+    MP4ContentIdDescriptor ( const MP4ContentIdDescriptor &src );
+    MP4ContentIdDescriptor &operator= ( const MP4ContentIdDescriptor &src );
+};
+
+// associated values in descriptors
+
+// ES objectTypeId
+const uint8_t MP4SystemsV1ObjectType            = 0x01;
+const uint8_t MP4SystemsV2ObjectType            = 0x02;
+const uint8_t MP4SubpicObjectType               = 0xe0;
+
+// ES streamType
+const uint8_t MP4ObjectDescriptionStreamType    = 0x01;
+const uint8_t MP4ClockReferenceStreamType       = 0x02;
+const uint8_t MP4SceneDescriptionStreamType     = 0x03;
+const uint8_t MP4VisualStreamType               = 0x04;
+const uint8_t MP4AudioStreamType                = 0x05;
+const uint8_t MP4Mpeg7StreamType                = 0x06;
+const uint8_t MP4IPMPStreamType                 = 0x07;
+const uint8_t MP4OCIStreamType                  = 0x08;
+const uint8_t MP4MPEGJStreamType                = 0x09;
+const uint8_t MP4UserPrivateStreamType          = 0x20;
+const uint8_t MP4NeroSubpicStreamType           = 0x38;
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_DESCRIPTORS_H
diff --git a/src/enum.h b/src/enum.h
new file mode 100644
index 0000000..af5b6d4
--- /dev/null
+++ b/src/enum.h
@@ -0,0 +1,110 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ENUM_H
+#define MP4V2_IMPL_ENUM_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// class Template to support enums with to/from string conversions.
+///
+/// This class template is meant only to add support for enums which have
+/// useful string equivalents. The model is that each enum value has
+/// two string equivalents: compact and formal. <b>compact</b> is a short,
+/// compact string which usually excludes spaces and punctuation and makes it
+/// suitable for use with command-line arguments or any situation where
+/// superfluous characters make parsing needlessly complicated. <b>formal</b>
+/// is a string suitable for use in human-readable situations where spaces,
+/// punctuation and even case is desirable.
+///
+/// For end usability, enums will have the full list of enums available
+/// which is suitable for help-usage scenerios. And all values will be
+/// convertable from enum to string, or from string to enum. When converting
+/// from enum to string, you may optionally specify a boolean value which
+/// will return the <b>formal</b> string value; otherwise a <b>compact</b>
+/// value is returned.
+///
+/// Conversion from string to enum (integral) value will always assume
+/// <b>compact</b> string is used as it makes little sense to convert formal
+/// strings to enum. Furthermore, the string conversion is optimized to
+/// ignore case, and in the case an exact full-string match is not found,
+/// a <b>best-match</b> is then checked for. Basically this means that if
+/// enough beginning characters are used to match exactly 1 string-enum,
+/// it is considered a match.
+///
+/// The template has 2 strict requirements. First, the enum must be a true
+/// enum type; ie: not just some integer constants. Second, the enum must have
+/// a value which indicates an undefined or illegal value; which is used as
+/// a return value by string-to-enum conversion to indicate the string did
+/// not match.
+///
+/// This template implementation itself should never be exposed. That is
+/// to say, the .tcc file must not be used by code outside this library.
+///
+/// WARNING: since enum types are typically made static file scope,
+/// care must be taken to make sure Entry data[] initialization occurs
+/// in the <b>same file</b> and <b>before</b> instantiation.
+///
+template <typename T, T UNDEFINED>
+class Enum
+{
+public:
+    struct MP4V2_EXPORT Entry
+    {
+        T type;
+        const string compact;
+        const string formal;
+    };
+
+    typedef map<string,const Entry*,LessIgnoreCase> MapToType;
+    typedef map<T,const Entry*> MapToString;
+
+public:
+    static const Entry data[];
+
+private:
+    MapToType   _mapToType;
+    MapToString _mapToString;
+
+public:
+    const MapToType&   mapToType;
+    const MapToString& mapToString;
+
+public:
+    Enum();
+    ~Enum();
+
+    T       toType   ( const string& ) const;
+    string  toString ( T, bool = false ) const;
+    string& toString ( T, string&, bool = false ) const;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#include "enum.tcc"
+
+#endif // MP4V2_IMPL_ENUM_H
diff --git a/src/enum.tcc b/src/enum.tcc
new file mode 100644
index 0000000..1f3395a
--- /dev/null
+++ b/src/enum.tcc
@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ENUM_TCC
+#define MP4V2_IMPL_ENUM_TCC
+
+#include <sstream>
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T, T UNDEFINED>
+Enum<T,UNDEFINED>::Enum()
+    : mapToType   ( _mapToType )
+    , mapToString ( _mapToString )
+{
+    for( const Entry* p = data; p->type != UNDEFINED; p++ ) {
+        _mapToType.insert( typename MapToType::value_type( p->compact, p ));
+        _mapToString.insert( typename MapToString::value_type( p->type, p ));
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T, T UNDEFINED>
+Enum<T,UNDEFINED>::~Enum()
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T, T UNDEFINED>
+string
+Enum<T,UNDEFINED>::toString( T value, bool formal ) const
+{
+    string buffer;
+    return toString( value, buffer, formal );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T, T UNDEFINED>
+string&
+Enum<T,UNDEFINED>::toString( T value, string& buffer, bool formal ) const
+{
+    const typename MapToString::const_iterator found = _mapToString.find( value );
+    if( found != _mapToString.end() ) {
+        const Entry& entry = *(found->second);
+        buffer = formal ? entry.formal : entry.compact;
+        return buffer;
+    }
+
+    ostringstream oss;
+    oss << "UNDEFINED(" << value << ")";
+    buffer = oss.str();
+    return buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+template <typename T, T UNDEFINED>
+T
+Enum<T,UNDEFINED>::toType( const string& value ) const
+{
+    // if number perform enum lookup
+    int ivalue;
+    istringstream iss( value );
+    iss >> ivalue;
+    if( iss.rdstate() == ios::eofbit ) {
+        const typename MapToString::const_iterator found = _mapToString.find( static_cast<T>(ivalue) );
+        if( found != _mapToString.end() )
+            return found->second->type;
+    }
+
+    // exact match
+    const typename MapToType::const_iterator found = _mapToType.find( value );
+    if( found != _mapToType.end() )
+        return found->second->type;
+
+    // partial match
+    int matches = 0;
+    T matched = static_cast<T>( 0 );
+
+    const typename MapToType::const_iterator ie = _mapToType.end();
+    for( typename MapToType::const_iterator it = _mapToType.begin(); it != ie; it++ ) {
+        const Entry& entry = *(it->second);
+        if( entry.compact.find( value ) == 0 ) {
+            matches++;
+            matched = entry.type;
+        }
+    }
+
+    return (matches == 1) ? matched : UNDEFINED;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_ENUM_TCC
diff --git a/src/exception.cpp b/src/exception.cpp
new file mode 100644
index 0000000..ddc60d7
--- /dev/null
+++ b/src/exception.cpp
@@ -0,0 +1,96 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2009.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      David Byron, dbyron0@gmail.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+Exception::Exception( const string&     what_,
+                      const char        *file_,
+                      int               line_,
+                      const char        *function_ )
+    : what(what_)
+    , file(file_)
+    , line(line_)
+    , function(function_)
+{
+    ASSERT(file_);
+    ASSERT(function_);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Exception::~Exception()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+Exception::msg() const
+{
+    ostringstream retval;
+
+    retval << function << ": " << what << " (" << file << "," << line << ")";
+
+    return retval.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+PlatformException::PlatformException( const string&     what_,
+                                      int               errno_,
+                                      const char        *file_,
+                                      int               line_,
+                                      const char        *function_ )
+    : Exception(what_,file_,line_,function_)
+    , m_errno(errno_)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+PlatformException::~PlatformException()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+PlatformException::msg() const
+{
+    ostringstream retval;
+
+    retval << function << ": " << what << ": errno: " << m_errno << " (" <<
+        file << "," << line << ")";
+
+    return retval.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/exception.h b/src/exception.h
new file mode 100644
index 0000000..f0e2e4e
--- /dev/null
+++ b/src/exception.h
@@ -0,0 +1,70 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//      David Byron, dbyron0@gmail.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_MP4EXCEPTION_H
+#define MP4V2_IMPL_MP4EXCEPTION_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4V2_EXPORT Exception
+{
+public:
+    explicit Exception( const string&   what_,
+                        const char      *file_,
+                        int             line_,
+                        const char      *function_ );
+    virtual ~Exception();
+
+    virtual string      msg() const;
+
+public:
+    const string        what;
+    const string        file;
+    const int           line;
+    const string        function;
+};
+
+class MP4V2_EXPORT PlatformException : public Exception
+{
+public:
+    explicit PlatformException( const string&   what_,
+                                int             errno_,
+                                const char      *file_,
+                                int             line_,
+                                const char      *function_ );
+    virtual ~PlatformException();
+
+    virtual string      msg() const;
+
+public:
+    const int   m_errno;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4EXCEPTION_H
diff --git a/src/impl.h b/src/impl.h
new file mode 100644
index 0000000..f35e3fe
--- /dev/null
+++ b/src/impl.h
@@ -0,0 +1,10 @@
+#ifndef MP4V2_IMPL_IMPL_H
+#define MP4V2_IMPL_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_IMPL_H
diff --git a/src/isma.cpp b/src/isma.cpp
new file mode 100644
index 0000000..007455d
--- /dev/null
+++ b/src/isma.cpp
@@ -0,0 +1,967 @@
+/*
+ * 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.
+ *
+ * Contributor(s):
+ *      Dave Mackie       dmackie@cisco.com
+ *              Alix Marchandise-Franquet alix@cisco.com
+ *              Ximpo Group Ltd.                mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const uint8_t BifsV2Config[3] = {
+    0x00, 0x00, 0x60 // IsCommandStream = 1, PixelMetric = 1
+};
+
+void MP4File::MakeIsmaCompliant(bool addIsmaComplianceSdp)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    if (m_useIsma) {
+        // already done
+        return;
+    }
+
+    // find first audio and/or video tracks
+
+    MP4TrackId audioTrackId = MP4_INVALID_TRACK_ID;
+    try {
+        audioTrackId = FindTrackId(0, MP4_AUDIO_TRACK_TYPE);
+    }
+    catch ( Exception *x ) {
+        log.errorf(*x);
+        delete x;
+    }
+    MP4TrackId videoTrackId = MP4_INVALID_TRACK_ID;
+    try {
+        videoTrackId = FindTrackId(0, MP4_VIDEO_TRACK_TYPE);
+    }
+    catch (Exception* x) {
+        log.errorf(*x);
+        delete x;
+    }
+    if (audioTrackId == MP4_INVALID_TRACK_ID &&
+            videoTrackId == MP4_INVALID_TRACK_ID) return;
+
+    const char *audio_media_data_name, *video_media_data_name;
+    uint8_t videoProfile = 0xff;
+    if (audioTrackId != MP4_INVALID_TRACK_ID) {
+        audio_media_data_name = MP4GetTrackMediaDataName(this, audioTrackId);
+        if (!(ATOMID(audio_media_data_name) == ATOMID("mp4a") ||
+                ATOMID(audio_media_data_name) == ATOMID("enca"))) {
+            log.errorf("%s: \"%s\": can't make ISMA compliant when file contains an %s track", 
+                       __FUNCTION__, GetFilename().c_str(), audio_media_data_name);
+            return;
+        }
+    }
+    //
+    // Note - might have to check for avc1 here...
+    if (videoTrackId != MP4_INVALID_TRACK_ID) {
+        video_media_data_name = MP4GetTrackMediaDataName(this, videoTrackId);
+        if (!(ATOMID(video_media_data_name) == ATOMID("mp4v") ||
+                ATOMID(video_media_data_name) == ATOMID("encv"))) {
+            log.errorf("%s: \"%s\": can't make ISMA compliant when file contains an %s track", __FUNCTION__, 
+                       GetFilename().c_str(), video_media_data_name);
+            return;
+        }
+        MP4LogLevel verb = log.verbosity;
+        log.setVerbosity(MP4_LOG_NONE);
+        videoProfile = MP4GetVideoProfileLevel(this, videoTrackId);
+        log.setVerbosity(verb);
+    }
+
+    m_useIsma = true;
+
+    uint64_t fileMsDuration = 0;
+    fileMsDuration =
+        ConvertFromMovieDuration(GetDuration(), MP4_MSECS_TIME_SCALE);
+
+    // delete any existing OD track
+    if (m_odTrackId != MP4_INVALID_TRACK_ID) {
+        DeleteTrack(m_odTrackId);
+    }
+
+    if (m_pRootAtom->FindAtom("moov.iods") == NULL) {
+        (void)AddChildAtom("moov", "iods");
+    }
+    (void)AddODTrack();
+    SetODProfileLevel(0xFF);
+
+    if (audioTrackId != MP4_INVALID_TRACK_ID) {
+        AddTrackToOd(audioTrackId);
+        MP4SetAudioProfileLevel(this, 0xf);
+    }
+    if (videoTrackId != MP4_INVALID_TRACK_ID) {
+        AddTrackToOd(videoTrackId);
+        MP4SetVideoProfileLevel(this, videoProfile);
+    }
+
+    // delete any existing scene track
+    MP4TrackId sceneTrackId = MP4_INVALID_TRACK_ID;
+    try {
+        sceneTrackId = FindTrackId(0, MP4_SCENE_TRACK_TYPE);
+    }
+    catch (Exception *x) {
+        log.errorf(*x);
+        delete x;
+    }
+    if (sceneTrackId != MP4_INVALID_TRACK_ID) {
+        DeleteTrack(sceneTrackId);
+    }
+
+    // add scene track
+    sceneTrackId = AddSceneTrack();
+    SetSceneProfileLevel(0xFF);
+    SetGraphicsProfileLevel(0xFF);
+    SetTrackIntegerProperty(sceneTrackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId",
+                            MP4SystemsV2ObjectType);
+
+    SetTrackESConfiguration(sceneTrackId,
+                            BifsV2Config, sizeof(BifsV2Config));
+
+    uint8_t* pBytes = NULL;
+    uint64_t numBytes = 0;
+
+    // write OD Update Command
+    CreateIsmaODUpdateCommandFromFileForFile(
+        m_odTrackId,
+        audioTrackId,
+        videoTrackId,
+        &pBytes,
+        &numBytes);
+
+    WriteSample(m_odTrackId, pBytes, numBytes, fileMsDuration);
+
+    MP4Free(pBytes);
+    pBytes = NULL;
+
+    // write BIFS Scene Replace Command
+    CreateIsmaSceneCommand(
+        MP4_IS_VALID_TRACK_ID(audioTrackId),
+        MP4_IS_VALID_TRACK_ID(videoTrackId),
+        &pBytes,
+        &numBytes);
+
+    WriteSample(sceneTrackId, pBytes, numBytes, fileMsDuration);
+
+    MP4Free(pBytes);
+    pBytes = NULL;
+
+    // add session level sdp
+    CreateIsmaIodFromFile(
+        m_odTrackId,
+        sceneTrackId,
+        audioTrackId,
+        videoTrackId,
+        &pBytes,
+        &numBytes);
+
+    char* iodBase64 = MP4ToBase64(pBytes, numBytes);
+
+    uint32_t sdpBufLen = (uint32_t)strlen(iodBase64) + 256;
+    uint32_t used;
+    char* sdpBuf = (char*)MP4Calloc(sdpBufLen);
+
+    if (addIsmaComplianceSdp) {
+        strncpy(sdpBuf, "a=isma-compliance:1,1.0,1\015\012", sdpBufLen);
+    }
+
+    used = (uint32_t)strlen(sdpBuf);
+    sdpBufLen -= used;
+    snprintf(&sdpBuf[used], sdpBufLen,
+             "a=mpeg4-iod: \042data:application/mpeg4-iod;base64,%s\042\015\012",
+             iodBase64);
+
+    SetSessionSdp(sdpBuf);
+
+    log.verbose1f("\"%s\": IOD SDP = %s", GetFilename().c_str(), sdpBuf);
+
+    MP4Free(iodBase64);
+    iodBase64 = NULL;
+    MP4Free(pBytes);
+    pBytes = NULL;
+    MP4Free(sdpBuf);
+    sdpBuf = NULL;
+}
+
+static void CloneIntegerProperty(
+    MP4Descriptor* pDest,
+    MP4DescriptorProperty* pSrc,
+    const char* name)
+{
+    MP4IntegerProperty* pGetProperty;
+    MP4IntegerProperty* pSetProperty;
+
+    if (!pSrc->FindProperty(name, (MP4Property**)&pGetProperty)) return;
+    if (!pDest->FindProperty(name, (MP4Property**)&pSetProperty)) return;
+    pSetProperty->SetValue(pGetProperty->GetValue());
+}
+
+void MP4File::CreateIsmaIodFromFile(
+    MP4TrackId odTrackId,
+    MP4TrackId sceneTrackId,
+    MP4TrackId audioTrackId,
+    MP4TrackId videoTrackId,
+    uint8_t** ppBytes,
+    uint64_t* pNumBytes)
+{
+    MP4Atom* pIodsAtom = FindAtom("moov.iods");
+    ASSERT(pIodsAtom);
+    MP4DescriptorProperty* pSrcIod =
+        (MP4DescriptorProperty*)pIodsAtom->GetProperty(2);
+
+    MP4Descriptor* pIod = new MP4IODescriptor(*pIodsAtom);
+    pIod->SetTag(MP4IODescrTag);
+    pIod->Generate();
+
+    CloneIntegerProperty(pIod, pSrcIod, "objectDescriptorId");
+    CloneIntegerProperty(pIod, pSrcIod, "ODProfileLevelId");
+    CloneIntegerProperty(pIod, pSrcIod, "sceneProfileLevelId");
+    CloneIntegerProperty(pIod, pSrcIod, "audioProfileLevelId");
+    CloneIntegerProperty(pIod, pSrcIod, "visualProfileLevelId");
+    CloneIntegerProperty(pIod, pSrcIod, "graphicsProfileLevelId");
+
+    // mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag
+    MP4DescriptorProperty* pEsProperty;
+    if (!pIod->FindProperty("esIds", (MP4Property**)&pEsProperty)) return;
+    pEsProperty->SetTags(MP4ESDescrTag);
+
+    MP4IntegerProperty* pSetProperty;
+    MP4IntegerProperty* pSceneESID;
+    MP4IntegerProperty* pOdESID;
+
+    // OD
+    MP4Descriptor* pOdEsd =
+        pEsProperty->AddDescriptor(MP4ESDescrTag);
+    pOdEsd->Generate();
+
+    if (!pOdEsd->FindProperty("ESID", (MP4Property**)&pOdESID)) return;
+
+    // we set the OD ESID to a non-zero unique value
+    pOdESID->SetValue(m_odTrackId);
+
+    if (pOdEsd->FindProperty("URLFlag",
+                             (MP4Property**)&pSetProperty))
+        pSetProperty->SetValue(1);
+
+    uint8_t* pBytes;
+    uint64_t numBytes;
+
+    CreateIsmaODUpdateCommandFromFileForStream(
+        audioTrackId,
+        videoTrackId,
+        &pBytes,
+        &numBytes);
+
+    log.hexDump(0, MP4_LOG_VERBOSE1, pBytes, numBytes, "\"%s\": OD data",
+                GetFilename().c_str() );
+
+    char* odCmdBase64 = MP4ToBase64(pBytes, numBytes);
+
+    uint32_t urlBufLen = (uint32_t)strlen(odCmdBase64) + 64;
+    char* urlBuf = (char*)MP4Malloc(urlBufLen);
+
+    snprintf(urlBuf, urlBufLen,
+             "data:application/mpeg4-od-au;base64,%s",
+             odCmdBase64);
+
+    MP4StringProperty* pUrlProperty;
+    if (pOdEsd->FindProperty("URL",
+                             (MP4Property**)&pUrlProperty))
+        pUrlProperty->SetValue(urlBuf);
+
+    log.verbose1f("\"%s\": OD data URL = \042%s\042", GetFilename().c_str(),
+                  urlBuf);
+
+    MP4Free(odCmdBase64);
+    odCmdBase64 = NULL;
+    MP4Free(pBytes);
+    pBytes = NULL;
+    MP4Free(urlBuf);
+    urlBuf = NULL;
+
+    MP4DescriptorProperty* pSrcDcd = NULL;
+
+    // HACK temporarily point to scene decoder config
+    (void)FindProperty(MakeTrackName(odTrackId,
+                                     "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr"),
+                       (MP4Property**)&pSrcDcd);
+    ASSERT(pSrcDcd);
+    MP4Property* pOrgOdEsdProperty =
+        pOdEsd->GetProperty(8);
+    pOdEsd->SetProperty(8, pSrcDcd);
+
+    // bufferSizeDB needs to be set appropriately
+    MP4BitfieldProperty* pBufferSizeProperty = NULL;
+    if (pOdEsd->FindProperty("decConfigDescr.bufferSizeDB",
+                             (MP4Property**)&pBufferSizeProperty)) {
+        ASSERT(pBufferSizeProperty);
+        pBufferSizeProperty->SetValue(numBytes);
+    }
+
+    // SL config needs to change from 2 (file) to 1 (null)
+    if (pOdEsd->FindProperty("slConfigDescr.predefined",
+                             (MP4Property**)&pSetProperty))
+        pSetProperty->SetValue(1);
+
+
+    // Scene
+    MP4Descriptor* pSceneEsd =
+        pEsProperty->AddDescriptor(MP4ESDescrTag);
+    pSceneEsd->Generate();
+
+    if (pSceneEsd->FindProperty("ESID",
+                                (MP4Property**)&pSceneESID)) {
+        // we set the Scene ESID to a non-zero unique value
+        pSceneESID->SetValue(sceneTrackId);
+    }
+
+    if (pSceneEsd->FindProperty("URLFlag",
+                                (MP4Property**)&pSetProperty))
+        pSetProperty->SetValue(1);
+
+    CreateIsmaSceneCommand(
+        MP4_IS_VALID_TRACK_ID(audioTrackId),
+        MP4_IS_VALID_TRACK_ID(videoTrackId),
+        &pBytes,
+        &numBytes);
+
+    log.hexDump(0, MP4_LOG_VERBOSE1, pBytes, numBytes, "\"%s\": Scene data",
+                GetFilename().c_str() );
+
+    char *sceneCmdBase64 = MP4ToBase64(pBytes, numBytes);
+
+    urlBuf = (char*)MP4Malloc(strlen(sceneCmdBase64) + 64);
+    snprintf(urlBuf, strlen(sceneCmdBase64) + 64,
+             "data:application/mpeg4-bifs-au;base64,%s",
+             sceneCmdBase64);
+
+    if (pSceneEsd->FindProperty("URL",
+                                (MP4Property**)&pUrlProperty))
+        pUrlProperty->SetValue(urlBuf);
+
+    log.verbose1f("\"%s\": Scene data URL = \042%s\042",
+                  GetFilename().c_str(), urlBuf);
+
+    MP4Free(sceneCmdBase64);
+    sceneCmdBase64 = NULL;
+    MP4Free(urlBuf);
+    urlBuf = NULL;
+    MP4Free(pBytes);
+    pBytes = NULL;
+
+    // HACK temporarily point to scene decoder config
+    ASSERT(FindProperty(MakeTrackName(sceneTrackId,
+                                      "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr"),
+                        (MP4Property**)&pSrcDcd));
+    ASSERT(pSrcDcd);
+    MP4Property* pOrgSceneEsdProperty =
+        pSceneEsd->GetProperty(8);
+    pSceneEsd->SetProperty(8, pSrcDcd);
+
+    // bufferSizeDB needs to be set
+    pBufferSizeProperty = NULL;
+    if (pSceneEsd->FindProperty("decConfigDescr.bufferSizeDB",
+                                (MP4Property**)&pBufferSizeProperty)) {
+        ASSERT(pBufferSizeProperty);
+        pBufferSizeProperty->SetValue(numBytes);
+    }
+
+    // SL config needs to change from 2 (file) to 1 (null)
+    if (pSceneEsd->FindProperty("slConfigDescr.predefined",
+                                (MP4Property**)&pSetProperty))
+        pSetProperty->SetValue(1);
+
+
+    // finally get the whole thing written to a memory
+    pIod->WriteToMemory(*this, ppBytes, pNumBytes);
+
+
+    // now carefully replace esd properties before destroying
+    pOdEsd->SetProperty(8, pOrgOdEsdProperty);
+    pSceneEsd->SetProperty(8, pOrgSceneEsdProperty);
+    pSceneESID->SetValue(0); // restore 0 value
+    pOdESID->SetValue(0);
+
+    delete pIod;
+
+    log.hexDump(0, MP4_LOG_VERBOSE1, *ppBytes, *pNumBytes, "\"%s\": IOD data",
+                GetFilename().c_str() );
+}
+
+void MP4File::CreateIsmaIodFromParams(
+    uint8_t videoProfile,
+    uint32_t videoBitrate,
+    uint8_t* videoConfig,
+    uint32_t videoConfigLength,
+    uint8_t audioProfile,
+    uint32_t audioBitrate,
+    uint8_t* audioConfig,
+    uint32_t audioConfigLength,
+    uint8_t** ppIodBytes,
+    uint64_t* pIodNumBytes)
+{
+    MP4IntegerProperty* pInt;
+    uint8_t* pBytes = NULL;
+    uint64_t numBytes;
+
+    // Descriptor constructors need a parent atom.  In this
+    // case we don't have one so create a dummy one.  This
+    // is OK as we only build the descriptor and then dump
+    // its contents to a buffer
+    MP4Atom     dummyParent(*this);
+
+    // Create the IOD
+    MP4Descriptor* pIod = new MP4IODescriptor(dummyParent);
+    pIod->SetTag(MP4IODescrTag);
+    pIod->Generate();
+
+    // Set audio and video profileLevels
+    if (pIod->FindProperty("audioProfileLevelId",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(audioProfile);
+
+    if (pIod->FindProperty("visualProfileLevelId",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(videoProfile);
+
+    // Mutate esIds from MP4ESIDIncDescrTag to MP4ESDescrTag
+    MP4DescriptorProperty* pEsProperty;
+    if (!pIod->FindProperty("esIds", (MP4Property**)&pEsProperty)) return;
+    pEsProperty->SetTags(MP4ESDescrTag);
+
+    // Add ES Descriptors
+
+    // Scene
+    CreateIsmaSceneCommand(
+        (audioProfile != 0xFF),
+        (videoProfile != 0xFF),
+        &pBytes,
+        &numBytes);
+
+    log.hexDump(0, MP4_LOG_VERBOSE1, pBytes, numBytes, "\"%s\": Scene data",
+                GetFilename().c_str() );
+
+    char* sceneCmdBase64 = MP4ToBase64(pBytes, numBytes);
+
+    char* urlBuf =
+        (char*)MP4Malloc(strlen(sceneCmdBase64) + 64);
+    snprintf(urlBuf, strlen(sceneCmdBase64) + 64,
+             "data:application/mpeg4-bifs-au;base64,%s",
+             sceneCmdBase64);
+
+    log.verbose1f("\"%s\": Scene data URL = \042%s\042", GetFilename().c_str(),
+                  urlBuf);
+
+    /* MP4Descriptor* pSceneEsd = */
+    CreateESD(
+        pEsProperty,
+        201,                // esid
+        MP4SystemsV2ObjectType,
+        MP4SceneDescriptionStreamType,
+        numBytes,           // bufferSize
+        numBytes * 8,       // bitrate
+        BifsV2Config,
+        sizeof(BifsV2Config),
+        urlBuf);
+    MP4Free(urlBuf);
+    urlBuf = NULL;
+
+    MP4Free(sceneCmdBase64);
+    sceneCmdBase64 = NULL;
+    MP4Free(pBytes);
+    pBytes = NULL;
+
+    // OD
+
+    // Video
+    MP4DescriptorProperty* pVideoEsdProperty =
+        new MP4DescriptorProperty(dummyParent);
+    pVideoEsdProperty->SetTags(MP4ESDescrTag);
+
+    /* MP4Descriptor* pVideoEsd = */
+    CreateESD(
+        pVideoEsdProperty,
+        20,                 // esid
+        MP4_MPEG4_VIDEO_TYPE,
+        MP4VisualStreamType,
+        videoBitrate / 8,   // bufferSize
+        videoBitrate,
+        videoConfig,
+        videoConfigLength,
+        NULL);
+
+    // Audio
+    MP4DescriptorProperty* pAudioEsdProperty =
+        new MP4DescriptorProperty(dummyParent);
+    pAudioEsdProperty->SetTags(MP4ESDescrTag);
+
+    /* MP4Descriptor* pAudioEsd = */
+    CreateESD(
+        pAudioEsdProperty,
+        10,                 // esid
+        MP4_MPEG4_AUDIO_TYPE,
+        MP4AudioStreamType,
+        audioBitrate / 8,   // bufferSize
+        audioBitrate,
+        audioConfig,
+        audioConfigLength,
+        NULL);
+
+    CreateIsmaODUpdateCommandForStream(
+        pAudioEsdProperty,
+        pVideoEsdProperty,
+        &pBytes,
+        &numBytes);
+
+    // cleanup temporary descriptor properties
+    delete pAudioEsdProperty;
+    delete pVideoEsdProperty;
+
+    log.hexDump(0, MP4_LOG_VERBOSE1,pBytes, numBytes,"\"%s\": OD data = %" PRIu64 " bytes",
+                GetFilename().c_str(), numBytes);
+
+    char* odCmdBase64 = MP4ToBase64(pBytes, numBytes);
+
+    urlBuf = (char*)MP4Malloc(strlen(odCmdBase64) + 64);
+    if (urlBuf != NULL) {
+        snprintf(urlBuf, strlen(odCmdBase64) + 64,
+                 "data:application/mpeg4-od-au;base64,%s",
+                 odCmdBase64);
+
+        log.verbose1f("\"%s\": OD data URL = \042%s\042", GetFilename().c_str(), urlBuf);
+
+        /* MP4Descriptor* pOdEsd = */
+        CreateESD(
+            pEsProperty,
+            101,
+            MP4SystemsV1ObjectType,
+            MP4ObjectDescriptionStreamType,
+            numBytes,       // bufferSize
+            numBytes * 8,   // bitrate
+            NULL,           // config
+            0,              // configLength
+            urlBuf);
+
+        MP4Free(urlBuf);
+        urlBuf = NULL;
+    }
+    MP4Free(odCmdBase64);
+    odCmdBase64 = NULL;
+    MP4Free(pBytes);
+    pBytes = NULL;
+
+    // finally get the whole thing written to a memory
+    pIod->WriteToMemory(*this, ppIodBytes, pIodNumBytes);
+
+    delete pIod;
+
+    log.hexDump(0, MP4_LOG_VERBOSE1, *ppIodBytes, *pIodNumBytes,"\"%s\": IOD data",
+                GetFilename().c_str() );
+}
+
+void MP4File::CreateESD(
+    MP4DescriptorProperty* pEsProperty,
+    uint32_t esid,
+    uint8_t objectType,
+    uint8_t streamType,
+    uint32_t bufferSize,
+    uint32_t bitrate,
+    const uint8_t* pConfig,
+    uint32_t configLength,
+    char* url)
+{
+    MP4IntegerProperty* pInt;
+    MP4StringProperty* pString;
+    MP4BytesProperty* pBytes;
+    MP4BitfieldProperty* pBits;
+
+    MP4Descriptor* pEsd =
+        pEsProperty->AddDescriptor(MP4ESDescrTag);
+    pEsd->Generate();
+
+    if (pEsd->FindProperty("ESID",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(esid);
+
+    if (pEsd->FindProperty("decConfigDescr.objectTypeId",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(objectType);
+
+    if (pEsd->FindProperty("decConfigDescr.streamType",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(streamType);
+
+    if (pEsd->FindProperty("decConfigDescr.bufferSizeDB",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(bufferSize);
+
+    if (pEsd->FindProperty("decConfigDescr.maxBitrate",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(bitrate);
+
+    if (pEsd->FindProperty("decConfigDescr.avgBitrate",
+                           (MP4Property**)&pInt))
+        pInt->SetValue(bitrate);
+
+    MP4DescriptorProperty* pConfigDescrProperty;
+    if (pEsd->FindProperty("decConfigDescr.decSpecificInfo",
+                           (MP4Property**)&pConfigDescrProperty)) {
+
+        MP4Descriptor* pConfigDescr =
+            pConfigDescrProperty->AddDescriptor(MP4DecSpecificDescrTag);
+        pConfigDescr->Generate();
+
+        if (pConfigDescrProperty->FindProperty("decSpecificInfo[0].info",
+                                               (MP4Property**)&pBytes))
+            pBytes->SetValue(pConfig, configLength);
+    }
+
+    if (pEsd->FindProperty("slConfigDescr.predefined",
+                           (MP4Property**)&pInt))
+        // changed 12/5/02 from plugfest to value 0
+        pInt->SetValue(0);
+
+    if (pEsd->FindProperty("slConfig.useAccessUnitEndFlag",
+                           (MP4Property **)&pBits))
+        pBits->SetValue(1);
+
+    if (url) {
+        if (pEsd->FindProperty("URLFlag",
+                               (MP4Property**)&pInt))
+            pInt->SetValue(1);
+
+        if (pEsd->FindProperty("URL",
+                               (MP4Property**)&pString))
+            pString->SetValue(url);
+    }
+
+    //return pEsd;
+}
+
+void MP4File::CreateIsmaODUpdateCommandFromFileForFile(
+    MP4TrackId odTrackId,
+    MP4TrackId audioTrackId,
+    MP4TrackId videoTrackId,
+    uint8_t** ppBytes,
+    uint64_t* pNumBytes)
+{
+    // Descriptor constructors need a parent atom.  In this
+    // case we don't have one so create a dummy one.  This
+    // is OK as we only build the descriptor and then dump
+    // its contents to a buffer
+    MP4Atom     dummyParent(*this);
+    MP4Descriptor* pCommand = CreateODCommand(dummyParent, MP4ODUpdateODCommandTag);
+    pCommand->Generate();
+
+    for (uint8_t i = 0; i < 2; i++) {
+        MP4TrackId trackId;
+        uint16_t odId;
+
+        if (i == 0) {
+            trackId = audioTrackId;
+            odId = 10;
+        } else {
+            trackId = videoTrackId;
+            odId = 20;
+        }
+
+        if (trackId == MP4_INVALID_TRACK_ID) {
+            continue;
+        }
+
+        MP4DescriptorProperty* pOdDescrProperty =
+            (MP4DescriptorProperty*)(pCommand->GetProperty(0));
+
+        pOdDescrProperty->SetTags(MP4FileODescrTag);
+
+        MP4Descriptor* pOd =
+            pOdDescrProperty->AddDescriptor(MP4FileODescrTag);
+
+        pOd->Generate();
+
+        MP4BitfieldProperty* pOdIdProperty = NULL;
+        if (pOd->FindProperty("objectDescriptorId",
+                              (MP4Property**)&pOdIdProperty))
+            pOdIdProperty->SetValue(odId);
+
+        MP4DescriptorProperty* pEsIdsDescriptorProperty = NULL;
+        ASSERT(pOd->FindProperty("esIds",
+                                 (MP4Property**)&pEsIdsDescriptorProperty));
+        ASSERT(pEsIdsDescriptorProperty);
+
+        pEsIdsDescriptorProperty->SetTags(MP4ESIDRefDescrTag);
+
+        MP4Descriptor *pRefDescriptor =
+            pEsIdsDescriptorProperty->AddDescriptor(MP4ESIDRefDescrTag);
+        pRefDescriptor->Generate();
+
+        MP4Integer16Property* pRefIndexProperty = NULL;
+        ASSERT(pRefDescriptor->FindProperty("refIndex",
+                                            (MP4Property**)&pRefIndexProperty));
+        ASSERT(pRefIndexProperty);
+
+        uint32_t mpodIndex = FindTrackReference(
+                                 MakeTrackName(odTrackId, "tref.mpod"), trackId);
+        ASSERT(mpodIndex != 0);
+
+        pRefIndexProperty->SetValue(mpodIndex);
+    }
+
+    pCommand->WriteToMemory(*this, ppBytes, pNumBytes);
+
+    delete pCommand;
+}
+
+void MP4File::CreateIsmaODUpdateCommandFromFileForStream(
+    MP4TrackId audioTrackId,
+    MP4TrackId videoTrackId,
+    uint8_t** ppBytes,
+    uint64_t* pNumBytes)
+{
+    MP4DescriptorProperty* pAudioEsd = NULL;
+    MP4Integer8Property* pAudioSLConfigPredef = NULL;
+    MP4BitfieldProperty* pAudioAccessUnitEndFlag = NULL;
+    int oldAudioUnitEndFlagValue = 0;
+    MP4DescriptorProperty* pVideoEsd = NULL;
+    MP4Integer8Property* pVideoSLConfigPredef = NULL;
+    MP4BitfieldProperty* pVideoAccessUnitEndFlag = NULL;
+    int oldVideoUnitEndFlagValue = 0;
+    MP4IntegerProperty* pAudioEsdId = NULL;
+    MP4IntegerProperty* pVideoEsdId = NULL;
+
+    if (audioTrackId != MP4_INVALID_TRACK_ID) {
+        // changed mp4a to * to handle enca case
+        MP4Atom* pEsdsAtom =
+            FindAtom(MakeTrackName(audioTrackId,
+                                   "mdia.minf.stbl.stsd.*.esds"));
+        ASSERT(pEsdsAtom);
+
+        pAudioEsd = (MP4DescriptorProperty*)(pEsdsAtom->GetProperty(2));
+        // ESID is 0 for file, stream needs to be non-ze
+        ASSERT(pAudioEsd->FindProperty("ESID",
+                                       (MP4Property**)&pAudioEsdId));
+
+        ASSERT(pAudioEsdId);
+        pAudioEsdId->SetValue(audioTrackId);
+
+        // SL config needs to change from 2 (file) to 1 (null)
+        if (pAudioEsd->FindProperty("slConfigDescr.predefined",
+                                    (MP4Property**)&pAudioSLConfigPredef)) {
+            ASSERT(pAudioSLConfigPredef);
+            pAudioSLConfigPredef->SetValue(0);
+        }
+
+        if (pAudioEsd->FindProperty("slConfigDescr.useAccessUnitEndFlag",
+                                    (MP4Property **)&pAudioAccessUnitEndFlag)) {
+            oldAudioUnitEndFlagValue =
+                pAudioAccessUnitEndFlag->GetValue();
+            pAudioAccessUnitEndFlag->SetValue(1);
+        }
+    }
+
+    if (videoTrackId != MP4_INVALID_TRACK_ID) {
+        // changed mp4v to * to handle encv case
+        MP4Atom* pEsdsAtom =
+            FindAtom(MakeTrackName(videoTrackId,
+                                   "mdia.minf.stbl.stsd.*.esds"));
+        ASSERT(pEsdsAtom);
+
+        pVideoEsd = (MP4DescriptorProperty*)(pEsdsAtom->GetProperty(2));
+        ASSERT(pVideoEsd->FindProperty("ESID",
+                                       (MP4Property**)&pVideoEsdId));
+
+        ASSERT(pVideoEsdId);
+        pVideoEsdId->SetValue(videoTrackId);
+
+        // SL config needs to change from 2 (file) to 1 (null)
+        ASSERT(pVideoEsd->FindProperty("slConfigDescr.predefined",
+                                       (MP4Property **)&pVideoSLConfigPredef));
+        ASSERT(pVideoSLConfigPredef);
+        pVideoSLConfigPredef->SetValue(0);
+
+        if (pVideoEsd->FindProperty("slConfigDescr.useAccessUnitEndFlag",
+                                    (MP4Property **)&pVideoAccessUnitEndFlag)) {
+            oldVideoUnitEndFlagValue =
+                pVideoAccessUnitEndFlag->GetValue();
+            pVideoAccessUnitEndFlag->SetValue(1);
+        }
+    }
+
+    CreateIsmaODUpdateCommandForStream(
+        pAudioEsd, pVideoEsd, ppBytes, pNumBytes);
+    log.hexDump(0, MP4_LOG_VERBOSE1, *ppBytes, *pNumBytes,
+                "\"%s\": After CreateImsaODUpdateCommandForStream len %" PRIu64,
+                GetFilename().c_str(), *pNumBytes);
+
+    // return SL config values to 2 (file)
+    // return ESID values to 0
+    if (pAudioSLConfigPredef) {
+        pAudioSLConfigPredef->SetValue(2);
+    }
+    if (pAudioEsdId) {
+        pAudioEsdId->SetValue(0);
+    }
+    if (pAudioAccessUnitEndFlag) {
+        pAudioAccessUnitEndFlag->SetValue(oldAudioUnitEndFlagValue );
+    }
+    if (pVideoEsdId) {
+        pVideoEsdId->SetValue(0);
+    }
+    if (pVideoSLConfigPredef) {
+        pVideoSLConfigPredef->SetValue(2);
+    }
+    if (pVideoAccessUnitEndFlag) {
+        pVideoAccessUnitEndFlag->SetValue(oldVideoUnitEndFlagValue );
+    }
+}
+
+void MP4File::CreateIsmaODUpdateCommandForStream(
+    MP4DescriptorProperty* pAudioEsdProperty,
+    MP4DescriptorProperty* pVideoEsdProperty,
+    uint8_t** ppBytes,
+    uint64_t* pNumBytes)
+{
+    MP4Descriptor* pAudioOd = NULL;
+    MP4Descriptor* pVideoOd = NULL;
+
+    // Descriptor constructors need a parent atom.  In this
+    // case we don't have one so create a dummy one.  This
+    // is OK as we only build the descriptor and then dump
+    // its contents to a buffer
+    MP4Atom     dummyParent(*this);
+    MP4Descriptor* pCommand =
+        CreateODCommand(dummyParent, MP4ODUpdateODCommandTag);
+    pCommand->Generate();
+
+    for (uint8_t i = 0; i < 2; i++) {
+        uint16_t odId;
+        MP4DescriptorProperty* pEsdProperty = NULL;
+
+        if (i == 0) {
+            odId = 10;
+            pEsdProperty = pAudioEsdProperty;
+        } else {
+            odId = 20;
+            pEsdProperty = pVideoEsdProperty;
+        }
+
+        if (pEsdProperty == NULL) {
+            continue;
+        }
+
+        MP4DescriptorProperty* pOdDescrProperty =
+            (MP4DescriptorProperty*)(pCommand->GetProperty(0));
+
+        pOdDescrProperty->SetTags(MP4ODescrTag);
+
+        MP4Descriptor* pOd =
+            pOdDescrProperty->AddDescriptor(MP4ODescrTag);
+        pOd->Generate();
+
+        if (i == 0) {
+            pAudioOd = pOd;
+        } else {
+            pVideoOd = pOd;
+        }
+
+        MP4BitfieldProperty* pOdIdProperty = NULL;
+        if (pOd->FindProperty("objectDescriptorId",
+                              (MP4Property**)&pOdIdProperty)) {
+            pOdIdProperty->SetValue(odId);
+        }
+
+        delete (MP4DescriptorProperty*)pOd->GetProperty(4);
+        pOd->SetProperty(4, pEsdProperty);
+    }
+
+    // serialize OD command
+    pCommand->WriteToMemory(*this, ppBytes, pNumBytes);
+
+    // detach from esd descriptor params
+    if (pAudioOd) {
+        pAudioOd->SetProperty(4, NULL);
+    }
+    if (pVideoOd) {
+        pVideoOd->SetProperty(4, NULL);
+    }
+
+    // then destroy
+    delete pCommand;
+}
+
+void MP4File::CreateIsmaSceneCommand(
+    bool hasAudio,
+    bool hasVideo,
+    uint8_t** ppBytes,
+    uint64_t* pNumBytes)
+{
+    // from ISMA 1.0 Tech Spec Appendix E
+    static const uint8_t bifsAudioOnly[] = {
+        0xC0, 0x10, 0x12,
+        0x81, 0x30, 0x2A, 0x05, 0x6D, 0xC0
+    };
+    static const uint8_t bifsVideoOnly[] = {
+        0xC0, 0x10, 0x12,
+        0x61, 0x04,
+        0x1F, 0xC0, 0x00, 0x00,
+        0x1F, 0xC0, 0x00, 0x00,
+        0x44, 0x28, 0x22, 0x82, 0x9F, 0x80
+    };
+    static const uint8_t bifsAudioVideo[] = {
+        0xC0, 0x10, 0x12,
+        0x81, 0x30, 0x2A, 0x05, 0x6D, 0x26,
+        0x10, 0x41, 0xFC, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00,
+        0x04, 0x42, 0x82, 0x28, 0x29, 0xF8
+    };
+
+    if (hasAudio && hasVideo) {
+        *pNumBytes = sizeof(bifsAudioVideo);
+        *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes);
+        memcpy(*ppBytes, bifsAudioVideo, sizeof(bifsAudioVideo));
+
+    } else if (hasAudio) {
+        *pNumBytes = sizeof(bifsAudioOnly);
+        *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes);
+        memcpy(*ppBytes, bifsAudioOnly, sizeof(bifsAudioOnly));
+
+    } else if (hasVideo) {
+        *pNumBytes = sizeof(bifsVideoOnly);
+        *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes);
+        memcpy(*ppBytes, bifsVideoOnly, sizeof(bifsVideoOnly));
+    } else {
+        *pNumBytes = 0;
+        *ppBytes = NULL;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/itmf/CoverArtBox.cpp b/src/itmf/CoverArtBox.cpp
new file mode 100644
index 0000000..0125362
--- /dev/null
+++ b/src/itmf/CoverArtBox.cpp
@@ -0,0 +1,261 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 {
+
+///////////////////////////////////////////////////////////////////////////////
+
+CoverArtBox::Item::Item()
+    : type     ( BT_UNDEFINED )
+    , buffer   ( NULL )
+    , size     ( 0 )
+    , autofree ( false )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CoverArtBox::Item::Item( const Item& rhs )
+    : type     ( BT_UNDEFINED )
+    , buffer   ( NULL )
+    , size     ( 0 )
+    , autofree ( false )
+{
+    operator=( rhs );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CoverArtBox::Item::~Item()
+{
+    reset();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+CoverArtBox::Item&
+CoverArtBox::Item::operator=( const Item& rhs )
+{
+    type     = rhs.type;
+    size     = rhs.size;
+    autofree = rhs.autofree;
+
+    if( rhs.autofree ) {
+        buffer = (uint8_t*)MP4Malloc(rhs.size);
+        memcpy( buffer, rhs.buffer, rhs.size );
+    }
+    else {
+        buffer = rhs.buffer;
+    }
+
+    return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+CoverArtBox::Item::reset()
+{
+    if( autofree && buffer )
+        MP4Free( buffer );
+
+    type     = BT_UNDEFINED;
+    buffer   = NULL;
+    size     = 0;
+    autofree = false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+CoverArtBox::add( MP4FileHandle hFile, const Item& item )
+{
+    MP4File& file = *((MP4File*)hFile);
+
+    const char* const covr_name = "moov.udta.meta.ilst.covr";
+    MP4Atom* covr = file.FindAtom( covr_name );
+    if( !covr ) {
+        file.AddDescendantAtoms( "moov", "udta.meta.ilst.covr" );
+
+        covr = file.FindAtom( covr_name );
+        if( !covr )
+            return true;
+    }
+
+    // use empty data atom if one exists
+    MP4Atom* data = NULL;
+    uint32_t index = 0;
+    const uint32_t atomc = covr->GetNumberOfChildAtoms();
+    for( uint32_t i = 0; i < atomc; i++ ) {
+        MP4Atom* atom = covr->GetChildAtom( i );
+
+        MP4BytesProperty* metadata = NULL;
+        if( !atom->FindProperty( "data.metadata", (MP4Property**)&metadata ))
+            continue;
+
+        if( metadata->GetCount() )
+            continue;
+
+        data = atom;
+        index = i;
+        break;
+    }
+
+    // no empty atom found, create one.
+    if( !data ) {
+        data = MP4Atom::CreateAtom( file, covr, "data" );
+        covr->AddChildAtom( data );
+        data->Generate();
+        index = covr->GetNumberOfChildAtoms() - 1;
+    }
+
+    return set( hFile, item, index );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+CoverArtBox::get( MP4FileHandle hFile, Item& item, uint32_t index )
+{
+    item.reset();
+    MP4File& file = *((MP4File*)hFile);
+
+    MP4Atom* covr = file.FindAtom( "moov.udta.meta.ilst.covr" );
+    if( !covr )
+        return true;
+
+    if( !(index < covr->GetNumberOfChildAtoms()) )
+        return true;
+
+    MP4DataAtom* data = static_cast<MP4DataAtom*>( covr->GetChildAtom( index ));
+    if( !data )
+        return true;
+
+    MP4BytesProperty* metadata = NULL;
+    if ( !data->FindProperty( "data.metadata", (MP4Property**)&metadata ))
+        return true;
+
+    metadata->GetValue( &item.buffer, &item.size );
+    item.autofree = true;
+    item.type = data->typeCode.GetValue();
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+CoverArtBox::list( MP4FileHandle hFile, ItemList& out )
+{
+    out.clear();
+    MP4File& file = *((MP4File*)hFile);
+    MP4ItmfItemList* itemList = genericGetItemsByCode( file, "covr" ); // alloc
+
+    if( itemList->size ) {
+        MP4ItmfDataList& dataList = itemList->elements[0].dataList;
+        out.resize( dataList.size );
+        for( uint32_t i = 0; i < dataList.size; i++ )
+            get( hFile, out[i], i );
+    }
+
+    genericItemListFree( itemList ); // free
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+CoverArtBox::remove( MP4FileHandle hFile, uint32_t index )
+{
+    MP4File& file = *((MP4File*)hFile);
+
+    MP4Atom* covr = file.FindAtom( "moov.udta.meta.ilst.covr" );
+    if( !covr )
+        return true;
+
+    // wildcard mode: delete covr and all images
+    if( index == numeric_limits<uint32_t>::max() ) {
+        covr->GetParentAtom()->DeleteChildAtom( covr );
+        delete covr;
+        return false;
+    }
+
+    if( !(index < covr->GetNumberOfChildAtoms()) )
+        return true;
+
+    MP4Atom* data = covr->GetChildAtom( index );
+    if( !data )
+        return true;
+
+    // delete single image
+    covr->DeleteChildAtom( data );
+    delete data;
+
+    // delete empty covr
+    if( covr->GetNumberOfChildAtoms() == 0 ) {
+        covr->GetParentAtom()->DeleteChildAtom( covr );
+        delete covr;
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+CoverArtBox::set( MP4FileHandle hFile, const Item& item, uint32_t index )
+{
+    MP4File& file = *((MP4File*)hFile);
+
+    MP4Atom* covr = file.FindAtom( "moov.udta.meta.ilst.covr" );
+    if( !covr )
+        return true;
+
+    if( !(index < covr->GetNumberOfChildAtoms()) )
+        return true;
+
+    MP4DataAtom* data = static_cast<MP4DataAtom*>( covr->GetChildAtom( index ));
+    if( !data )
+        return true;
+
+    MP4BytesProperty* metadata = NULL;
+    if ( !data->FindProperty( "data.metadata", (MP4Property**)&metadata ))
+        return true;
+
+    // autodetect type if BT_UNDEFINED
+    const BasicType final_type = (item.type == BT_UNDEFINED)
+        ? computeBasicType( item.buffer, item.size )
+        : item.type;
+
+    // set type; note: not really flags due to b0rked atom structure
+    data->typeCode.SetValue( final_type );
+    metadata->SetValue( item.buffer, item.size );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
diff --git a/src/itmf/CoverArtBox.h b/src/itmf/CoverArtBox.h
new file mode 100644
index 0000000..023e3c9
--- /dev/null
+++ b/src/itmf/CoverArtBox.h
@@ -0,0 +1,121 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ITMF_COVERARTBOX_H
+#define MP4V2_IMPL_ITMF_COVERARTBOX_H
+
+namespace mp4v2 { namespace impl { namespace itmf {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Functional class for covr-box (Cover-art Box) support.
+///
+class MP4V2_EXPORT CoverArtBox
+{
+public:
+    /// Data object for covr-box item.
+    /// This object correlates to one covr->data atom and offers automatic
+    /// memory freeing when <b>autofree</b> is true.
+    ///
+    class MP4V2_EXPORT Item
+    {
+    public:
+        Item();
+        Item( const Item& );
+        ~Item();
+
+        Item& operator=( const Item& );
+
+        /// Reset to state of newly constructed object.
+        /// If <b>buffer</b> is not NULL and <b>autofree</b> is true the
+        /// buffer will be free'd.
+        void reset();
+
+        BasicType type;     ///< covr-box type.
+        uint8_t*  buffer;   ///< buffer point to raw covr-box data.
+        uint32_t  size;     ///< size of covr-box buffer size in bytes.
+        bool      autofree; ///< when true invoke free(buffer) upon destruction.
+    };
+
+    /// Object representing a list of covr-box items.
+    typedef vector<Item> ItemList;
+
+    /// Fetch list of covr-box items from file.
+    ///
+    /// @param hFile on which to operate.
+    /// @param out vector of ArtItem objects.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool list( MP4FileHandle hFile, ItemList& out );
+
+    /// Add covr-box item to file.
+    /// Any necessary metadata atoms are first created.
+    /// Additionally, if an empty data-atom exists it will be used,
+    /// otherwise a new data-atom is added to <b>covr-atom</b>.
+    ///
+    /// @param hFile on which to operate.
+    /// @param item covr-box object to place in file.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool add( MP4FileHandle hFile, const Item& item );
+
+    /// Replace covr-box item in file.
+    ///
+    /// @param hFile on which to operate.
+    /// @param item covr-box object to place in file.
+    /// @param index 0-based index of image to replace.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool set( MP4FileHandle hFile, const Item& item, uint32_t index );
+
+    /// Fetch covr-box item from file.
+    ///
+    /// @param hFile on which to operate.
+    /// @param item covr-box object populated with data.
+    ///     The resulting object owns the malloc'd buffer and <b>item.autofree</b>
+    ///     is set to true for convenient memory management.
+    /// @param index 0-based index of image to fetch.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool get( MP4FileHandle hFile, Item& item, uint32_t index );
+
+    /// Remove covr-box item from file.
+    ///
+    /// @param hFile on which to operate.
+    /// @param index 0-based index of image to remove.
+    ///     Default value indicates wildcard behavior to remove all items.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool remove( MP4FileHandle hFile, uint32_t index = numeric_limits<uint32_t>::max() );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
+
+#endif // MP4V2_IMPL_ITMF_COVERARTBOX_H
diff --git a/src/itmf/Tags.cpp b/src/itmf/Tags.cpp
new file mode 100644
index 0000000..e8ca481
--- /dev/null
+++ b/src/itmf/Tags.cpp
@@ -0,0 +1,897 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2011.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      Rouven Wessling, mp4v2@rouvenwessling.de
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "impl.h"
+
+namespace mp4v2 { namespace impl { namespace itmf {
+
+///////////////////////////////////////////////////////////////////////////////
+
+Tags::Tags()
+    : hasMetadata(false)
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Tags::~Tags()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_addArtwork( MP4Tags*& tags, MP4TagArtwork& c_artwork )
+{
+    artwork.resize( artwork.size() + 1 );
+    c_setArtwork( tags, (uint32_t)artwork.size() - 1, c_artwork );
+    updateArtworkShadow( tags );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_alloc( MP4Tags*& tags )
+{
+    tags = new MP4Tags();
+    memset( tags, 0, sizeof(MP4Tags) ); // safe: pure C-struct
+    tags->__handle = this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_fetch( MP4Tags*& tags, MP4FileHandle hFile )
+{
+    MP4Tags& c = *tags;
+    MP4File& file = *static_cast<MP4File*>(hFile);
+
+    MP4ItmfItemList* itemList = genericGetItems( file ); // alloc
+
+    hasMetadata = (itemList->size > 0);
+
+    /* create code -> item map.
+     * map will only be used for items which do not repeat; we do not care if
+     * cover-art is inserted multiple times.
+     */
+    CodeItemMap cim;
+    for( uint32_t i = 0; i < itemList->size; i++ ) {
+        MP4ItmfItem& item = itemList->elements[i];
+        cim.insert( CodeItemMap::value_type( item.code, &item ));
+    }
+
+    fetchString(  cim, CODE_NAME,              name,              c.name );
+    fetchString(  cim, CODE_ARTIST,            artist,            c.artist );
+    fetchString(  cim, CODE_ALBUMARTIST,       albumArtist,       c.albumArtist );
+    fetchString(  cim, CODE_ALBUM,             album,             c.album );
+    fetchString(  cim, CODE_GROUPING,          grouping,          c.grouping );
+    fetchString(  cim, CODE_COMPOSER,          composer,          c.composer );
+    fetchString(  cim, CODE_COMMENTS,          comments,          c.comments );
+
+    fetchString(  cim, CODE_GENRE,             genre,             c.genre );
+    fetchGenre(   cim,                         genreType,         c.genreType );
+
+    fetchString(  cim, CODE_RELEASEDATE,       releaseDate,       c.releaseDate );
+    fetchTrack(   cim,                         track,             c.track );
+    fetchDisk(    cim,                         disk,              c.disk );
+    fetchInteger( cim, CODE_TEMPO,             tempo,             c.tempo );
+    fetchInteger( cim, CODE_COMPILATION,       compilation,       c.compilation );
+
+    fetchString(  cim, CODE_TVSHOW,            tvShow,            c.tvShow );
+    fetchString(  cim, CODE_TVNETWORK,         tvNetwork,         c.tvNetwork );
+    fetchString(  cim, CODE_TVEPISODEID,       tvEpisodeID,       c.tvEpisodeID );
+    fetchInteger( cim, CODE_TVSEASON,          tvSeason,          c.tvSeason );
+    fetchInteger( cim, CODE_TVEPISODE,         tvEpisode,         c.tvEpisode );
+
+    fetchString(  cim, CODE_SORTNAME,          sortName,          c.sortName );
+    fetchString(  cim, CODE_SORTARTIST,        sortArtist,        c.sortArtist );
+    fetchString(  cim, CODE_SORTALBUMARTIST,   sortAlbumArtist,   c.sortAlbumArtist );
+    fetchString(  cim, CODE_SORTALBUM,         sortAlbum,         c.sortAlbum );
+    fetchString(  cim, CODE_SORTCOMPOSER,      sortComposer,      c.sortComposer );
+    fetchString(  cim, CODE_SORTTVSHOW,        sortTVShow,        c.sortTVShow );
+
+    fetchString(  cim, CODE_DESCRIPTION,       description,       c.description );
+    fetchString(  cim, CODE_LONGDESCRIPTION,   longDescription,   c.longDescription );
+    fetchString(  cim, CODE_LYRICS,            lyrics,            c.lyrics );
+
+    fetchString(  cim, CODE_COPYRIGHT,         copyright,         c.copyright );
+    fetchString(  cim, CODE_ENCODINGTOOL,      encodingTool,      c.encodingTool ); 
+    fetchString(  cim, CODE_ENCODEDBY,         encodedBy,         c.encodedBy );
+    fetchString(  cim, CODE_PURCHASEDATE,      purchaseDate,      c.purchaseDate );
+
+    fetchInteger( cim, CODE_PODCAST,           podcast,           c.podcast );
+    fetchString(  cim, CODE_KEYWORDS,          keywords,          c.keywords );
+    fetchString(  cim, CODE_CATEGORY,          category,          c.category );
+
+    fetchInteger( cim, CODE_HDVIDEO,           hdVideo,           c.hdVideo );
+    fetchInteger( cim, CODE_MEDIATYPE,         mediaType,         c.mediaType );
+    fetchInteger( cim, CODE_CONTENTRATING,     contentRating,     c.contentRating );
+    fetchInteger( cim, CODE_GAPLESS,           gapless,           c.gapless );
+
+    fetchString(  cim, CODE_ITUNESACCOUNT,     iTunesAccount,     c.iTunesAccount );
+    fetchInteger( cim, CODE_ITUNESACCOUNTTYPE, iTunesAccountType, c.iTunesAccountType );
+    fetchInteger( cim, CODE_ITUNESCOUNTRY,     iTunesCountry,     c.iTunesCountry );
+
+    fetchInteger( cim, CODE_CONTENTID,         contentID,         c.contentID );
+    fetchInteger( cim, CODE_ARTISTID,          artistID,          c.artistID );
+    fetchInteger( cim, CODE_PLAYLISTID,        playlistID,        c.playlistID );
+    fetchInteger( cim, CODE_GENREID,           genreID,           c.genreID );
+    fetchInteger( cim, CODE_COMPOSERID,        composerID,        c.composerID );
+    fetchString(  cim, CODE_XID,               xid,               c.xid );
+
+    genericItemListFree( itemList ); // free
+
+    // fetch full list and overwrite our copy, otherwise clear
+    {
+        CoverArtBox::ItemList items;
+        if( CoverArtBox::list( hFile, items ))
+            artwork.clear();
+        else
+            artwork = items;
+
+        updateArtworkShadow( tags );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_free( MP4Tags*& tags )
+{
+    MP4Tags* c = const_cast<MP4Tags*>(tags);
+
+    delete[] c->artwork;
+    delete c;
+
+    tags = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_removeArtwork( MP4Tags*& tags, uint32_t index ) 
+{
+    if( !(index < artwork.size()) )
+        return;
+
+    artwork.erase( artwork.begin() + index );
+    updateArtworkShadow( tags );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setArtwork( MP4Tags*& tags, uint32_t index, MP4TagArtwork& c_artwork )
+{
+    if( !(index < artwork.size()) )
+        return;
+
+    CoverArtBox::Item& item = artwork[index];
+
+    switch( c_artwork.type ) {
+        case MP4_ART_BMP:
+            item.type = BT_BMP;
+            break;
+
+        case MP4_ART_GIF:
+            item.type = BT_GIF;
+            break;
+
+        case MP4_ART_JPEG:
+            item.type = BT_JPEG;
+            break;
+
+        case MP4_ART_PNG:
+            item.type = BT_PNG;
+            break;
+
+        case MP4_ART_UNDEFINED:
+        default:
+            item.type = computeBasicType( c_artwork.data, c_artwork.size );
+            break;
+    }
+
+    item.buffer   = (uint8_t*)malloc( c_artwork.size );
+    item.size     = c_artwork.size;
+    item.autofree = true;
+
+    memcpy( item.buffer, c_artwork.data, c_artwork.size );
+    updateArtworkShadow( tags );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setInteger( const uint8_t* value, uint8_t& cpp, const uint8_t*& c )
+{
+    if( !value ) {
+        cpp = 0;
+        c = NULL;
+    }
+    else {
+        cpp = *value;
+        c = &cpp;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setInteger( const uint16_t* value, uint16_t& cpp, const uint16_t*& c )
+{
+    if( !value ) {
+        cpp = 0;
+        c = NULL;
+    }
+    else {
+        cpp = *value;
+        c = &cpp;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setInteger( const uint32_t* value, uint32_t& cpp, const uint32_t*& c )
+{
+    if( !value ) {
+        cpp = 0;
+        c = NULL;
+    }
+    else {
+        cpp = *value;
+        c = &cpp;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setInteger( const uint64_t* value, uint64_t& cpp, const uint64_t*& c )
+{
+    if( !value ) {
+        cpp = 0;
+        c = NULL;
+    }
+    else {
+        cpp = *value;
+        c = &cpp;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setString( const char* value, string& cpp, const char*& c )
+{
+    if( !value ) {
+        cpp.clear();
+        c = NULL;
+    }
+    else {
+        cpp = value;
+        c = cpp.c_str();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setTrack( const MP4TagTrack* value, MP4TagTrack& cpp, const MP4TagTrack*& c )
+{
+    if( !value ) {
+        cpp.index = 0;
+        cpp.total = 0;
+        c = NULL;
+    }
+    else {
+        cpp.index = value->index;
+        cpp.total = value->total;
+        c = &cpp;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_setDisk( const MP4TagDisk* value, MP4TagDisk& cpp, const MP4TagDisk*& c )
+{
+    if( !value ) {
+        cpp.index = 0;
+        cpp.total = 0;
+        c = NULL;
+    }
+    else {
+        cpp.index = value->index;
+        cpp.total = value->total;
+        c = &cpp;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::c_store( MP4Tags*& tags, MP4FileHandle hFile )
+{
+    MP4Tags& c = *tags;
+    MP4File& file = *static_cast<MP4File*>(hFile);
+   
+    storeString(  file, CODE_NAME,              name,              c.name );
+    storeString(  file, CODE_ARTIST,            artist,            c.artist );
+    storeString(  file, CODE_ALBUMARTIST,       albumArtist,       c.albumArtist );
+    storeString(  file, CODE_ALBUM,             album,             c.album );
+    storeString(  file, CODE_GROUPING,          grouping,          c.grouping );
+    storeString(  file, CODE_COMPOSER,          composer,          c.composer );
+    storeString(  file, CODE_COMMENTS,          comments,          c.comments );
+
+    storeString(  file, CODE_GENRE,             genre,             c.genre );
+    storeGenre(   file,                         genreType,         c.genreType );
+
+    storeString(  file, CODE_RELEASEDATE,       releaseDate,       c.releaseDate );
+    storeTrack(   file,                         track,             c.track );
+    storeDisk(    file,                         disk,              c.disk );
+    storeInteger( file, CODE_TEMPO,             tempo,             c.tempo );
+    storeInteger( file, CODE_COMPILATION,       compilation,       c.compilation );
+    
+    storeString(  file, CODE_TVSHOW,            tvShow,            c.tvShow );
+    storeString(  file, CODE_TVNETWORK,         tvNetwork,         c.tvNetwork );
+    storeString(  file, CODE_TVEPISODEID,       tvEpisodeID,       c.tvEpisodeID );
+    storeInteger( file, CODE_TVSEASON,          tvSeason,          c.tvSeason );
+    storeInteger( file, CODE_TVEPISODE,         tvEpisode,         c.tvEpisode );
+    
+    storeString(  file, CODE_SORTNAME,          sortName,          c.sortName );
+    storeString(  file, CODE_SORTARTIST,        sortArtist,        c.sortArtist );
+    storeString(  file, CODE_SORTALBUMARTIST,   sortAlbumArtist,   c.sortAlbumArtist );
+    storeString(  file, CODE_SORTALBUM,         sortAlbum,         c.sortAlbum );
+    storeString(  file, CODE_SORTCOMPOSER,      sortComposer,      c.sortComposer );
+    storeString(  file, CODE_SORTTVSHOW,        sortTVShow,        c.sortTVShow );
+
+    storeString(  file, CODE_DESCRIPTION,       description,       c.description );
+    storeString(  file, CODE_LONGDESCRIPTION,   longDescription,   c.longDescription );
+    storeString(  file, CODE_LYRICS,            lyrics,            c.lyrics );
+
+    storeString(  file, CODE_COPYRIGHT,         copyright,         c.copyright );
+    storeString(  file, CODE_ENCODINGTOOL,      encodingTool,      c.encodingTool );
+    storeString(  file, CODE_ENCODEDBY,         encodedBy,         c.encodedBy );
+    storeString(  file, CODE_PURCHASEDATE,      purchaseDate,      c.purchaseDate );
+
+    storeInteger( file, CODE_PODCAST,           podcast,           c.podcast );
+    storeString(  file, CODE_KEYWORDS,          keywords,          c.keywords );
+    storeString(  file, CODE_CATEGORY,          category,          c.category );
+
+    storeInteger( file, CODE_HDVIDEO,           hdVideo,           c.hdVideo );
+    storeInteger( file, CODE_MEDIATYPE,         mediaType,         c.mediaType );
+    storeInteger( file, CODE_CONTENTRATING,     contentRating,     c.contentRating );
+    storeInteger( file, CODE_GAPLESS,           gapless,           c.gapless );
+
+    storeString(  file, CODE_ITUNESACCOUNT,     iTunesAccount,     c.iTunesAccount );
+    storeInteger( file, CODE_ITUNESACCOUNTTYPE, iTunesAccountType, c.iTunesAccountType );
+    storeInteger( file, CODE_ITUNESCOUNTRY,     iTunesCountry,     c.iTunesCountry );
+
+    storeInteger( file, CODE_CONTENTID,         contentID,         c.contentID );
+    storeInteger( file, CODE_ARTISTID,          artistID,          c.artistID );
+    storeInteger( file, CODE_PLAYLISTID,        playlistID,        c.playlistID );
+    storeInteger( file, CODE_GENREID,           genreID,           c.genreID );
+    storeInteger( file, CODE_COMPOSERID,        composerID,        c.composerID );
+    storeString(  file, CODE_XID,               xid,               c.xid );
+
+    // destroy all cover-art then add each
+    {
+        CoverArtBox::remove( hFile );
+        const CoverArtBox::ItemList::size_type max = artwork.size();
+        for( CoverArtBox::ItemList::size_type i = 0; i < max; i++ )
+            CoverArtBox::add( hFile, artwork[i] );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchGenre( const CodeItemMap& cim, uint16_t& cpp, const uint16_t*& c )
+{
+    cpp = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( CODE_GENRETYPE );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+    if( NULL == data.value )
+        return;
+
+    cpp = (uint16_t(data.value[0]) <<  8)
+        | (uint16_t(data.value[1])      );
+
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchDisk( const CodeItemMap& cim, MP4TagDisk& cpp, const MP4TagDisk*& c )
+{    
+    cpp.index = 0;
+    cpp.total = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( CODE_DISK );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+
+    if( NULL == data.value )
+        return;
+
+    cpp.index = (uint16_t(data.value[2]) <<  8)
+              | (uint16_t(data.value[3])      );
+
+    cpp.total = (uint16_t(data.value[4]) <<  8)
+              | (uint16_t(data.value[5])      );
+
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchTrack( const CodeItemMap& cim, MP4TagTrack& cpp, const MP4TagTrack*& c )
+{    
+    cpp.index = 0;
+    cpp.total = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( CODE_TRACK );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+
+    if( NULL == data.value )
+        return;
+
+    cpp.index = (uint16_t(data.value[2]) <<  8)
+              | (uint16_t(data.value[3])      );
+
+    cpp.total = (uint16_t(data.value[4]) <<  8)
+              | (uint16_t(data.value[5])      );
+
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint8_t& cpp, const uint8_t*& c )
+{
+    cpp = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( code );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+    if( NULL == data.value )
+        return;
+
+    cpp = data.value[0];
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint16_t& cpp, const uint16_t*& c )
+{
+    cpp = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( code );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+
+    if( NULL == data.value )
+        return;
+
+    cpp = (uint16_t(data.value[0]) <<  8)
+        | (uint16_t(data.value[1])      );
+
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint32_t& cpp, const uint32_t*& c )
+{
+    cpp = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( code );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+
+    if( NULL == data.value )
+        return;
+
+    cpp = (uint32_t(data.value[0]) << 24)
+        | (uint32_t(data.value[1]) << 16)
+        | (uint32_t(data.value[2]) <<  8)
+        | (uint32_t(data.value[3])      );
+
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchInteger( const CodeItemMap& cim, const string& code, uint64_t& cpp, const uint64_t*& c )
+{
+    cpp = 0;
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( code );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+
+    if( NULL == data.value )
+        return;
+
+    cpp = (uint64_t(data.value[0]) << 56)
+        | (uint64_t(data.value[1]) << 48)
+        | (uint64_t(data.value[2]) << 40)
+        | (uint64_t(data.value[3]) << 32)
+        | (uint64_t(data.value[4]) << 24)
+        | (uint64_t(data.value[5]) << 16)
+        | (uint64_t(data.value[6]) <<  8)
+        | (uint64_t(data.value[7])      );
+
+    c = &cpp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::fetchString( const CodeItemMap& cim, const string& code, string& cpp, const char*& c )
+{
+    cpp.clear();
+    c = NULL;
+
+    CodeItemMap::const_iterator f = cim.find( code );
+    if( f == cim.end() || 0 == f->second->dataList.size )
+        return;
+
+    MP4ItmfData& data = f->second->dataList.elements[0];
+
+    if( NULL == data.value )
+        return;
+
+    cpp.append( reinterpret_cast<char*>( data.value ), data.valueSize );
+    c = cpp.c_str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::remove( MP4File& file, const string& code )
+{
+    MP4ItmfItemList* itemList = genericGetItemsByCode( file, code ); // alloc
+
+    if( itemList->size )
+        genericRemoveItem( file, &itemList->elements[0] );
+
+    genericItemListFree( itemList ); // free
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::store( MP4File& file, const string& code, MP4ItmfBasicType basicType, const void* buffer, uint32_t size )
+{
+    // remove existing item
+    remove( file, code );
+
+    // add item
+    MP4ItmfItem& item = *genericItemAlloc( code, 1 ); // alloc
+    MP4ItmfData& data = item.dataList.elements[0];
+
+    data.typeCode = basicType;
+    data.valueSize = size;
+    data.value = (uint8_t*)malloc( data.valueSize );
+    memcpy( data.value, buffer, data.valueSize );
+
+    genericAddItem( file, &item );
+    genericItemFree( &item ); // free
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeGenre( MP4File& file, uint16_t cpp, const uint16_t* c )
+{
+    if( c ) {
+        uint8_t buf[2];
+
+        buf[0] = uint8_t((cpp & 0xff00) >> 8);
+        buf[1] = uint8_t((cpp & 0x00ff)     );
+
+        // it's not clear if you must use implicit in these situations and iirc iTunes and other software are not consistent in this regard.
+        // many other tags must be integer type yet no issues there. Silly that iTunes insists it must be implict, which is then hardcoded 
+        // to interpret as genres anyways.
+        store( file, CODE_GENRETYPE, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
+    }
+    else {
+        remove( file, CODE_GENRETYPE );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeDisk( MP4File& file, const MP4TagDisk& cpp, const MP4TagDisk* c )
+{
+    if( c ) {
+        uint8_t buf[6];
+        memset( buf, 0, sizeof(buf) );
+
+        buf[2] = uint8_t((cpp.index & 0xff00) >> 8);
+        buf[3] = uint8_t((cpp.index & 0x00ff)     );
+        buf[4] = uint8_t((cpp.total & 0xff00) >> 8);
+        buf[5] = uint8_t((cpp.total & 0x00ff)     );
+
+        store( file, CODE_DISK, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
+    }
+    else {
+        remove( file, CODE_DISK );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeTrack( MP4File& file, const MP4TagTrack& cpp, const MP4TagTrack* c )
+{
+    if( c ) {
+        uint8_t buf[8]; // iTMF spec says 7 but iTunes media is 8
+        memset( buf, 0, sizeof(buf) );
+
+        buf[2] = uint8_t((cpp.index & 0xff00) >> 8);
+        buf[3] = uint8_t((cpp.index & 0x00ff)     );
+        buf[4] = uint8_t((cpp.total & 0xff00) >> 8);
+        buf[5] = uint8_t((cpp.total & 0x00ff)     );
+
+        store( file, CODE_TRACK, MP4_ITMF_BT_IMPLICIT, buf, sizeof(buf) );
+    }
+    else {
+        remove( file, CODE_TRACK );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeInteger( MP4File& file, const string& code, uint8_t cpp, const uint8_t* c )
+{
+    if( c )
+        store( file, code, MP4_ITMF_BT_INTEGER, &cpp, sizeof(cpp) );
+    else
+        remove( file, code );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeInteger( MP4File& file, const string& code, uint16_t cpp, const uint16_t* c )
+{
+    if( c ) {
+        uint8_t buf[2];
+
+        buf[0] = uint8_t((cpp & 0xff00) >> 8);
+        buf[1] = uint8_t((cpp & 0x00ff)     );
+
+        store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
+    }
+    else {
+        remove( file, code );
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeInteger( MP4File& file, const string& code, uint32_t cpp, const uint32_t* c )
+{
+    if( c ) {
+        uint8_t buf[4];
+
+        buf[0] = uint8_t((cpp & 0xff000000) >> 24 );
+        buf[1] = uint8_t((cpp & 0x00ff0000) >> 16 );
+        buf[2] = uint8_t((cpp & 0x0000ff00) >>  8 );
+        buf[3] = uint8_t((cpp & 0x000000ff)       );
+
+        store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
+    }
+    else {
+        remove( file, code );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeInteger( MP4File& file, const string& code, uint64_t cpp, const uint64_t* c )
+{
+    if( c ) {
+        uint8_t buf[8];
+
+        buf[0] = uint8_t((cpp & 0xff00000000000000LL) >> 56 );
+        buf[1] = uint8_t((cpp & 0x00ff000000000000LL) >> 48 );
+        buf[2] = uint8_t((cpp & 0x0000ff0000000000LL) >> 40 );
+        buf[3] = uint8_t((cpp & 0x000000ff00000000LL) >> 32 );
+        buf[4] = uint8_t((cpp & 0x00000000ff000000LL) >> 24 );
+        buf[5] = uint8_t((cpp & 0x0000000000ff0000LL) >> 16 );
+        buf[6] = uint8_t((cpp & 0x000000000000ff00LL) >>  8 );
+        buf[7] = uint8_t((cpp & 0x00000000000000ffLL)       );
+
+        store( file, code, MP4_ITMF_BT_INTEGER, buf, sizeof(buf) );
+    }
+    else {
+        remove( file, code );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::storeString( MP4File& file, const string& code, const string& cpp, const char* c )
+{
+    if( c )
+        store( file, code, MP4_ITMF_BT_UTF8, cpp.c_str(), (uint32_t)cpp.size() );
+    else
+        remove( file, code );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Tags::updateArtworkShadow( MP4Tags*& tags )
+{
+    if( tags->artwork ) {
+        delete[] tags->artwork;
+        tags->artwork = NULL;
+        tags->artworkCount = 0;
+    }
+
+    if( artwork.empty() )
+        return;
+
+    MP4TagArtwork* const cartwork = new MP4TagArtwork[ artwork.size() ];
+    uint32_t max = (uint32_t)artwork.size();
+
+    for( uint32_t i = 0; i < max; i++ ) {
+        MP4TagArtwork& a = cartwork[i];
+        CoverArtBox::Item& item = artwork[i];
+
+        a.data = item.buffer;
+        a.size = item.size;
+
+        switch( item.type ) {
+            case BT_BMP:
+                a.type = MP4_ART_BMP;
+                break;
+
+            case BT_GIF:
+                a.type = MP4_ART_GIF;
+                break;
+
+            case BT_JPEG:
+                a.type = MP4_ART_JPEG;
+                break;
+
+            case BT_PNG:
+                a.type = MP4_ART_PNG;
+                break;
+
+            default:
+                a.type = MP4_ART_UNDEFINED;
+                break;
+        }
+    }
+
+    tags->artwork      = cartwork;
+    tags->artworkCount = max;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const string Tags::CODE_NAME              = "\xa9" "nam";
+const string Tags::CODE_ARTIST            = "\xa9" "ART";
+const string Tags::CODE_ALBUMARTIST       = "aART";
+const string Tags::CODE_ALBUM             = "\xa9" "alb";
+const string Tags::CODE_GROUPING          = "\xa9" "grp";
+const string Tags::CODE_COMPOSER          = "\xa9" "wrt";
+const string Tags::CODE_COMMENTS          = "\xa9" "cmt";
+const string Tags::CODE_GENRE             = "\xa9" "gen";
+const string Tags::CODE_GENRETYPE         = "gnre";
+const string Tags::CODE_RELEASEDATE       = "\xa9" "day";
+const string Tags::CODE_TRACK             = "trkn";
+const string Tags::CODE_DISK              = "disk";
+const string Tags::CODE_TEMPO             = "tmpo";
+const string Tags::CODE_COMPILATION       = "cpil";
+
+const string Tags::CODE_TVSHOW            = "tvsh";
+const string Tags::CODE_TVNETWORK         = "tvnn";
+const string Tags::CODE_TVEPISODEID       = "tven";
+const string Tags::CODE_TVSEASON          = "tvsn";
+const string Tags::CODE_TVEPISODE         = "tves";
+
+const string Tags::CODE_DESCRIPTION       = "desc";
+const string Tags::CODE_LONGDESCRIPTION   = "ldes";
+const string Tags::CODE_LYRICS            = "\xa9" "lyr";
+
+const string Tags::CODE_SORTNAME          = "sonm";
+const string Tags::CODE_SORTARTIST        = "soar";
+const string Tags::CODE_SORTALBUMARTIST   = "soaa";
+const string Tags::CODE_SORTALBUM         = "soal";
+const string Tags::CODE_SORTCOMPOSER      = "soco";
+const string Tags::CODE_SORTTVSHOW        = "sosn";
+
+const string Tags::CODE_COPYRIGHT         = "cprt";
+const string Tags::CODE_ENCODINGTOOL      = "\xa9" "too";
+const string Tags::CODE_ENCODEDBY         = "\xa9" "enc";
+const string Tags::CODE_PURCHASEDATE      = "purd";
+
+const string Tags::CODE_PODCAST           = "pcst";
+const string Tags::CODE_KEYWORDS          = "keyw";
+const string Tags::CODE_CATEGORY          = "catg";
+
+const string Tags::CODE_HDVIDEO           = "hdvd";
+const string Tags::CODE_MEDIATYPE         = "stik";
+const string Tags::CODE_CONTENTRATING     = "rtng";
+const string Tags::CODE_GAPLESS           = "pgap";
+
+const string Tags::CODE_ITUNESACCOUNT     = "apID";
+const string Tags::CODE_ITUNESACCOUNTTYPE = "akID";
+const string Tags::CODE_ITUNESCOUNTRY     = "sfID";
+const string Tags::CODE_CONTENTID         = "cnID";
+const string Tags::CODE_ARTISTID          = "atID";
+const string Tags::CODE_PLAYLISTID        = "plID";
+const string Tags::CODE_GENREID           = "geID";
+const string Tags::CODE_COMPOSERID        = "cmID";
+const string Tags::CODE_XID               = "xid ";
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
diff --git a/src/itmf/Tags.h b/src/itmf/Tags.h
new file mode 100644
index 0000000..57ef82c
--- /dev/null
+++ b/src/itmf/Tags.h
@@ -0,0 +1,210 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2011.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@gmail.com
+//      Rouven Wessling, mp4v2@rouvenwessling.de
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ITMF_TAGS_H
+#define MP4V2_IMPL_ITMF_TAGS_H
+
+namespace mp4v2 { namespace impl { namespace itmf {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class Tags
+{
+public:
+    static const string CODE_NAME;
+    static const string CODE_ARTIST;
+    static const string CODE_ALBUMARTIST;
+    static const string CODE_ALBUM;
+    static const string CODE_GROUPING;
+    static const string CODE_COMPOSER;
+    static const string CODE_COMMENTS;
+    static const string CODE_GENRE;
+    static const string CODE_GENRETYPE;
+    static const string CODE_RELEASEDATE;
+    static const string CODE_TRACK;
+    static const string CODE_DISK;
+    static const string CODE_TEMPO;
+    static const string CODE_COMPILATION;
+
+    static const string CODE_TVSHOW;
+    static const string CODE_TVNETWORK;
+    static const string CODE_TVEPISODEID;
+    static const string CODE_TVSEASON;
+    static const string CODE_TVEPISODE;
+
+    static const string CODE_DESCRIPTION;
+    static const string CODE_LONGDESCRIPTION;
+    static const string CODE_LYRICS;
+
+    static const string CODE_SORTNAME;
+    static const string CODE_SORTARTIST;
+    static const string CODE_SORTALBUMARTIST;
+    static const string CODE_SORTALBUM;
+    static const string CODE_SORTCOMPOSER;
+    static const string CODE_SORTTVSHOW;
+
+    static const string CODE_COPYRIGHT;
+    static const string CODE_ENCODINGTOOL;   
+    static const string CODE_ENCODEDBY;
+    static const string CODE_PURCHASEDATE;
+
+    static const string CODE_PODCAST;
+    static const string CODE_KEYWORDS;
+    static const string CODE_CATEGORY;
+
+    static const string CODE_HDVIDEO;
+    static const string CODE_MEDIATYPE;
+    static const string CODE_CONTENTRATING;
+    static const string CODE_GAPLESS;
+
+    static const string CODE_ITUNESACCOUNT;
+    static const string CODE_ITUNESACCOUNTTYPE;
+    static const string CODE_ITUNESCOUNTRY;
+    static const string CODE_CONTENTID;
+    static const string CODE_ARTISTID;
+    static const string CODE_PLAYLISTID;
+    static const string CODE_GENREID;
+    static const string CODE_COMPOSERID;
+    static const string CODE_XID;
+
+public:
+    string      name;
+    string      artist;
+    string      albumArtist;
+    string      album;
+    string      grouping;
+    string      composer;
+    string      comments;
+    string      genre;
+    uint16_t    genreType;
+    string      releaseDate;
+    MP4TagTrack track;
+    MP4TagDisk  disk;
+    uint16_t    tempo;
+    uint8_t     compilation;
+
+    string   tvShow;
+    string   tvEpisodeID;
+    uint32_t tvSeason;
+    uint32_t tvEpisode;
+    string   tvNetwork;
+
+    string description;
+    string longDescription;
+    string lyrics;
+
+    string sortName;
+    string sortArtist;
+    string sortAlbumArtist;
+    string sortAlbum;
+    string sortComposer;
+    string sortTVShow;
+
+    CoverArtBox::ItemList artwork;
+
+    string copyright;
+    string encodingTool;  
+    string encodedBy;
+    string purchaseDate;
+
+    uint8_t podcast;
+    string  keywords;
+    string  category;
+
+    uint8_t hdVideo;
+    uint8_t mediaType;
+    uint8_t contentRating;
+    uint8_t gapless;
+
+    string   iTunesAccount;
+    uint8_t  iTunesAccountType;
+    uint32_t iTunesCountry;
+    uint32_t contentID;
+    uint32_t artistID;
+    uint64_t playlistID;
+    uint32_t genreID;
+    uint32_t composerID;
+    string   xid;
+
+    bool     hasMetadata;
+
+public:
+    Tags();
+    ~Tags();
+
+    void c_alloc ( MP4Tags*& );
+    void c_fetch ( MP4Tags*&, MP4FileHandle );
+    void c_store ( MP4Tags*&, MP4FileHandle );
+    void c_free  ( MP4Tags*& );
+
+    void c_addArtwork    ( MP4Tags*&, MP4TagArtwork& );
+    void c_setArtwork    ( MP4Tags*&, uint32_t, MP4TagArtwork& );
+    void c_removeArtwork ( MP4Tags*&, uint32_t );
+
+    void c_setString  ( const char*, string&, const char*& );
+    void c_setInteger ( const uint8_t*,  uint8_t&,  const uint8_t*& );
+    void c_setInteger ( const uint16_t*, uint16_t&, const uint16_t*& );
+    void c_setInteger ( const uint32_t*, uint32_t&, const uint32_t*& );
+    void c_setInteger ( const uint64_t*, uint64_t&, const uint64_t*& );
+
+    void c_setTrack ( const MP4TagTrack*, MP4TagTrack&, const MP4TagTrack*& );
+    void c_setDisk  ( const MP4TagDisk*, MP4TagDisk&, const MP4TagDisk*& );
+
+private:
+    typedef map<string,MP4ItmfItem*> CodeItemMap;
+
+private:
+    void fetchString  ( const CodeItemMap&, const string&, string&, const char*& );
+    void fetchInteger ( const CodeItemMap&, const string&, uint8_t&,  const uint8_t*& );
+    void fetchInteger ( const CodeItemMap&, const string&, uint16_t&, const uint16_t*& );
+    void fetchInteger ( const CodeItemMap&, const string&, uint32_t&, const uint32_t*& );
+    void fetchInteger ( const CodeItemMap&, const string&, uint64_t&, const uint64_t*& );
+
+    void fetchGenre ( const CodeItemMap&, uint16_t&, const uint16_t*& );
+    void fetchTrack ( const CodeItemMap&, MP4TagTrack&, const MP4TagTrack*& );
+    void fetchDisk  ( const CodeItemMap&, MP4TagDisk&, const MP4TagDisk*& );
+
+    void storeString  ( MP4File&, const string&, const string&, const char* );
+    void storeInteger ( MP4File&, const string&, uint8_t,  const uint8_t* );
+    void storeInteger ( MP4File&, const string&, uint16_t, const uint16_t* );
+    void storeInteger ( MP4File&, const string&, uint32_t, const uint32_t* );
+    void storeInteger ( MP4File&, const string&, uint64_t, const uint64_t* );
+
+    void storeGenre ( MP4File&, uint16_t, const uint16_t* );
+    void storeTrack ( MP4File&, const MP4TagTrack&, const MP4TagTrack* );
+    void storeDisk  ( MP4File&, const MP4TagDisk&, const MP4TagDisk* );
+
+    void remove ( MP4File&, const string& );
+    void store  ( MP4File&, const string&, MP4ItmfBasicType, const void*, uint32_t );
+
+    void updateArtworkShadow( MP4Tags*& );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
+
+#endif // MP4V2_IMPL_ITMF_TAGS_H
diff --git a/src/itmf/generic.cpp b/src/itmf/generic.cpp
new file mode 100644
index 0000000..184cd74
--- /dev/null
+++ b/src/itmf/generic.cpp
@@ -0,0 +1,476 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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();
+    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 vector<uint32_t>::size_type max = indexList.size();
+    for( 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();
+    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 vector<uint32_t>::size_type max = indexList.size();
+    for( 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 = 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 == 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
diff --git a/src/itmf/generic.h b/src/itmf/generic.h
new file mode 100644
index 0000000..9edc7eb
--- /dev/null
+++ b/src/itmf/generic.h
@@ -0,0 +1,66 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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:
+//      KonaBlend, kona8lend@gmail.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ITMF_GENERIC_H
+#define MP4V2_IMPL_ITMF_GENERIC_H
+
+namespace mp4v2 { namespace impl { namespace itmf {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfItem*
+genericItemAlloc( const string& code, uint32_t numData );
+
+void
+genericItemFree( MP4ItmfItem* item );
+
+void
+genericItemListFree( MP4ItmfItemList* list );
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ItmfItemList*
+genericGetItems( MP4File& file );
+
+MP4ItmfItemList*
+genericGetItemsByCode( MP4File& file, const string& code );
+
+MP4ItmfItemList*
+genericGetItemsByMeaning( MP4File& file, const string& meaning, const string& name );
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+genericAddItem( MP4File& file, const MP4ItmfItem* item );
+
+bool
+genericSetItem( MP4File& file, const MP4ItmfItem* item );
+
+bool
+genericRemoveItem( MP4File& file, const MP4ItmfItem* item );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
+
+#endif // MP4V2_IMPL_ITMF_GENERIC_H
diff --git a/src/itmf/impl.h b/src/itmf/impl.h
new file mode 100644
index 0000000..9c7eb44
--- /dev/null
+++ b/src/itmf/impl.h
@@ -0,0 +1,34 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ITMF_IMPL_H
+#define MP4V2_IMPL_ITMF_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+#include "itmf.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_ITMF_IMPL_H
diff --git a/src/itmf/itmf.h b/src/itmf/itmf.h
new file mode 100644
index 0000000..cad38ed
--- /dev/null
+++ b/src/itmf/itmf.h
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ITMF_ITMF_H
+#define MP4V2_IMPL_ITMF_ITMF_H
+
+/// @namespace mp4v2::impl::itmf (private) iTunes Metadata Format.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+///
+/// This namespace implements some features that are specified by the
+/// iTunes Metadata Format Specification, revision 2008-04-16.
+///
+namespace mp4v2 { namespace impl { namespace itmf {
+    ;
+}}}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "CoverArtBox.h"
+#include "Tags.h"
+#include "generic.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_ITMF_ITMF_H
diff --git a/src/itmf/type.cpp b/src/itmf/type.cpp
new file mode 100644
index 0000000..b85e429
--- /dev/null
+++ b/src/itmf/type.cpp
@@ -0,0 +1,314 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//      Rouven Wessling, mp4v2@rouvenwessling.de
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const itmf::EnumBasicType::Entry itmf::EnumBasicType::data[] = {
+    { mp4v2::impl::itmf::BT_IMPLICIT,  "implicit",  "implicit" },
+    { mp4v2::impl::itmf::BT_UTF8,      "utf8",      "UTF-8" },
+    { mp4v2::impl::itmf::BT_UTF16,     "utf16",     "UTF-16" },
+    { mp4v2::impl::itmf::BT_SJIS,      "sjis",      "S/JIS" },
+    { mp4v2::impl::itmf::BT_HTML,      "html",      "HTML" },
+    { mp4v2::impl::itmf::BT_XML,       "xml",       "XML" },
+    { mp4v2::impl::itmf::BT_UUID,      "uuid",      "UUID" },
+    { mp4v2::impl::itmf::BT_ISRC,      "isrc",      "ISRC" },
+    { mp4v2::impl::itmf::BT_MI3P,      "mi3p",      "MI3P" },
+    { mp4v2::impl::itmf::BT_GIF,       "gif",       "GIF" },
+    { mp4v2::impl::itmf::BT_JPEG,      "jpeg",      "JPEG" },
+    { mp4v2::impl::itmf::BT_PNG,       "png",       "PNG" },
+    { mp4v2::impl::itmf::BT_URL,       "url",       "URL" },
+    { mp4v2::impl::itmf::BT_DURATION,  "duration",  "duration" },
+    { mp4v2::impl::itmf::BT_DATETIME,  "datetime",  "date/time" },
+    { mp4v2::impl::itmf::BT_GENRES,    "genres",    "genres" },
+    { mp4v2::impl::itmf::BT_INTEGER,   "integer",   "integer" },
+    { mp4v2::impl::itmf::BT_RIAA_PA,   "riaapa",    "RIAA-PA" },
+    { mp4v2::impl::itmf::BT_UPC,       "upc",       "UPC" },
+    { mp4v2::impl::itmf::BT_BMP,       "bmp",       "BMP" },
+
+    { mp4v2::impl::itmf::BT_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const itmf::EnumGenreType::Entry itmf::EnumGenreType::data[] = {
+    { mp4v2::impl::itmf::GENRE_BLUES,             "blues",             "Blues" },
+    { mp4v2::impl::itmf::GENRE_CLASSIC_ROCK,      "classicrock",       "Classic Rock" },
+    { mp4v2::impl::itmf::GENRE_COUNTRY,           "country",           "Country" },
+    { mp4v2::impl::itmf::GENRE_DANCE,             "dance",             "Dance" },
+    { mp4v2::impl::itmf::GENRE_DISCO,             "disco",             "Disco" },
+    { mp4v2::impl::itmf::GENRE_FUNK,              "funk",              "Funk" },
+    { mp4v2::impl::itmf::GENRE_GRUNGE,            "grunge",            "Grunge" },
+    { mp4v2::impl::itmf::GENRE_HIP_HOP,           "hiphop",            "Hop-Hop" },
+    { mp4v2::impl::itmf::GENRE_JAZZ,              "jazz",              "Jazz" },
+    { mp4v2::impl::itmf::GENRE_METAL,             "metal",             "Metal" },
+    { mp4v2::impl::itmf::GENRE_NEW_AGE,           "newage",            "New Age" },
+    { mp4v2::impl::itmf::GENRE_OLDIES,            "oldies",            "Oldies" },
+    { mp4v2::impl::itmf::GENRE_OTHER,             "other",             "Other" },
+    { mp4v2::impl::itmf::GENRE_POP,               "pop",               "Pop" },
+    { mp4v2::impl::itmf::GENRE_R_AND_B,           "rand_b",            "R&B" },
+    { mp4v2::impl::itmf::GENRE_RAP,               "rap",               "Rap" },
+    { mp4v2::impl::itmf::GENRE_REGGAE,            "reggae",            "Reggae" },
+    { mp4v2::impl::itmf::GENRE_ROCK,              "rock",              "Rock" },
+    { mp4v2::impl::itmf::GENRE_TECHNO,            "techno",            "Techno" },
+    { mp4v2::impl::itmf::GENRE_INDUSTRIAL,        "industrial",        "Industrial" },
+    { mp4v2::impl::itmf::GENRE_ALTERNATIVE,       "alternative",       "Alternative" },
+    { mp4v2::impl::itmf::GENRE_SKA,               "ska",               "Ska" },
+    { mp4v2::impl::itmf::GENRE_DEATH_METAL,       "deathmetal",        "Death Metal" },
+    { mp4v2::impl::itmf::GENRE_PRANKS,            "pranks",            "Pranks" },
+    { mp4v2::impl::itmf::GENRE_SOUNDTRACK,        "soundtrack",        "Soundtrack" },
+    { mp4v2::impl::itmf::GENRE_EURO_TECHNO,       "eurotechno",        "Euro-Techno" },
+    { mp4v2::impl::itmf::GENRE_AMBIENT,           "ambient",           "Ambient" },
+    { mp4v2::impl::itmf::GENRE_TRIP_HOP,          "triphop",           "Trip-Hop" },
+    { mp4v2::impl::itmf::GENRE_VOCAL,             "vocal",             "Vocal" },
+    { mp4v2::impl::itmf::GENRE_JAZZ_FUNK,         "jazzfunk",          "Jazz+Funk" },
+    { mp4v2::impl::itmf::GENRE_FUSION,            "fusion",            "Fusion" },
+    { mp4v2::impl::itmf::GENRE_TRANCE,            "trance",            "Trance" },
+    { mp4v2::impl::itmf::GENRE_CLASSICAL,         "classical",         "Classical" },
+    { mp4v2::impl::itmf::GENRE_INSTRUMENTAL,      "instrumental",      "Instrumental" },
+    { mp4v2::impl::itmf::GENRE_ACID,              "acid",              "Acid" },
+    { mp4v2::impl::itmf::GENRE_HOUSE,             "house",             "House" },
+    { mp4v2::impl::itmf::GENRE_GAME,              "game",              "Game" },
+    { mp4v2::impl::itmf::GENRE_SOUND_CLIP,        "soundclip",         "Sound Clip" },
+    { mp4v2::impl::itmf::GENRE_GOSPEL,            "gospel",            "Gospel" },
+    { mp4v2::impl::itmf::GENRE_NOISE,             "noise",             "Noise" },
+    { mp4v2::impl::itmf::GENRE_ALTERNROCK,        "alternrock",        "AlternRock" },
+    { mp4v2::impl::itmf::GENRE_BASS,              "bass",              "Bass" },
+    { mp4v2::impl::itmf::GENRE_SOUL,              "soul",              "Soul" },
+    { mp4v2::impl::itmf::GENRE_PUNK,              "punk",              "Punk" },
+    { mp4v2::impl::itmf::GENRE_SPACE,             "space",             "Space" },
+    { mp4v2::impl::itmf::GENRE_MEDITATIVE,        "meditative",        "Meditative" },
+    { mp4v2::impl::itmf::GENRE_INSTRUMENTAL_POP,  "instrumentalpop",   "Instrumental Pop" },
+    { mp4v2::impl::itmf::GENRE_INSTRUMENTAL_ROCK, "instrumentalrock",  "Instrumental Rock" },
+    { mp4v2::impl::itmf::GENRE_ETHNIC,            "ethnic",            "Ethnic" },
+    { mp4v2::impl::itmf::GENRE_GOTHIC,            "gothic",            "Gothic" },
+    { mp4v2::impl::itmf::GENRE_DARKWAVE,          "darkwave",          "Darkwave" },
+    { mp4v2::impl::itmf::GENRE_TECHNO_INDUSTRIAL, "technoindustrial",  "Techno-Industrial" },
+    { mp4v2::impl::itmf::GENRE_ELECTRONIC,        "electronic",        "Electronic" },
+    { mp4v2::impl::itmf::GENRE_POP_FOLK,          "popfolk",           "Pop-Folk" },
+    { mp4v2::impl::itmf::GENRE_EURODANCE,         "eurodance",         "Eurodance" },
+    { mp4v2::impl::itmf::GENRE_DREAM,             "dream",             "Dream" },
+    { mp4v2::impl::itmf::GENRE_SOUTHERN_ROCK,     "southernrock",      "Southern Rock" },
+    { mp4v2::impl::itmf::GENRE_COMEDY,            "comedy",            "Comedy" },
+    { mp4v2::impl::itmf::GENRE_CULT,              "cult",              "Cult" },
+    { mp4v2::impl::itmf::GENRE_GANGSTA,           "gangsta",           "Gangsta" },
+    { mp4v2::impl::itmf::GENRE_TOP_40,            "top40",             "Top 40" },
+    { mp4v2::impl::itmf::GENRE_CHRISTIAN_RAP,     "christianrap",      "Christian Rap" },
+    { mp4v2::impl::itmf::GENRE_POP_FUNK,          "popfunk",           "Pop/Funk" },
+    { mp4v2::impl::itmf::GENRE_JUNGLE,            "jungle",            "Jungle" },
+    { mp4v2::impl::itmf::GENRE_NATIVE_AMERICAN,   "nativeamerican",    "Native American" },
+    { mp4v2::impl::itmf::GENRE_CABARET,           "cabaret",           "Cabaret" },
+    { mp4v2::impl::itmf::GENRE_NEW_WAVE,          "newwave",           "New Wave" },
+    { mp4v2::impl::itmf::GENRE_PSYCHEDELIC,       "psychedelic",       "Psychedelic" },
+    { mp4v2::impl::itmf::GENRE_RAVE,              "rave",              "Rave" },
+    { mp4v2::impl::itmf::GENRE_SHOWTUNES,         "showtunes",         "Showtunes" },
+    { mp4v2::impl::itmf::GENRE_TRAILER,           "trailer",           "Trailer" },
+    { mp4v2::impl::itmf::GENRE_LO_FI,             "lofi",              "Lo-Fi" },
+    { mp4v2::impl::itmf::GENRE_TRIBAL,            "tribal",            "Tribal" },
+    { mp4v2::impl::itmf::GENRE_ACID_PUNK,         "acidpunk",          "Acid Punk" },
+    { mp4v2::impl::itmf::GENRE_ACID_JAZZ,         "acidjazz",          "Acid Jazz" },
+    { mp4v2::impl::itmf::GENRE_POLKA,             "polka",             "Polka" },
+    { mp4v2::impl::itmf::GENRE_RETRO,             "retro",             "Retro" },
+    { mp4v2::impl::itmf::GENRE_MUSICAL,           "musical",           "Musical" },
+    { mp4v2::impl::itmf::GENRE_ROCK_AND_ROLL,     "rockand_roll",      "Rock & Roll" },
+
+    { mp4v2::impl::itmf::GENRE_HARD_ROCK,         "hardrock",          "Hard Rock" },
+    { mp4v2::impl::itmf::GENRE_FOLK,              "folk",              "Folk" },
+    { mp4v2::impl::itmf::GENRE_FOLK_ROCK,         "folkrock",          "Folk-Rock" },
+    { mp4v2::impl::itmf::GENRE_NATIONAL_FOLK,     "nationalfolk",      "National Folk" },
+    { mp4v2::impl::itmf::GENRE_SWING,             "swing",             "Swing" },
+    { mp4v2::impl::itmf::GENRE_FAST_FUSION,       "fastfusion",        "Fast Fusion" },
+    { mp4v2::impl::itmf::GENRE_BEBOB,             "bebob",             "Bebob" },
+    { mp4v2::impl::itmf::GENRE_LATIN,             "latin",             "Latin" },
+    { mp4v2::impl::itmf::GENRE_REVIVAL,           "revival",           "Revival" },
+    { mp4v2::impl::itmf::GENRE_CELTIC,            "celtic",            "Celtic" },
+    { mp4v2::impl::itmf::GENRE_BLUEGRASS,         "bluegrass",         "Bluegrass" },
+    { mp4v2::impl::itmf::GENRE_AVANTGARDE,        "avantgarde",        "Avantgarde" },
+    { mp4v2::impl::itmf::GENRE_GOTHIC_ROCK,       "gothicrock",        "Gothic Rock" },
+    { mp4v2::impl::itmf::GENRE_PROGRESSIVE_ROCK,  "progressiverock",   "Progresive Rock" },
+    { mp4v2::impl::itmf::GENRE_PSYCHEDELIC_ROCK,  "psychedelicrock",   "Psychedelic Rock" },
+    { mp4v2::impl::itmf::GENRE_SYMPHONIC_ROCK,    "symphonicrock",     "SYMPHONIC_ROCK" },
+    { mp4v2::impl::itmf::GENRE_SLOW_ROCK,         "slowrock",          "Slow Rock" },
+    { mp4v2::impl::itmf::GENRE_BIG_BAND,          "bigband",           "Big Band" },
+    { mp4v2::impl::itmf::GENRE_CHORUS,            "chorus",            "Chorus" },
+    { mp4v2::impl::itmf::GENRE_EASY_LISTENING,    "easylistening",     "Easy Listening" },
+    { mp4v2::impl::itmf::GENRE_ACOUSTIC,          "acoustic",          "Acoustic" },
+    { mp4v2::impl::itmf::GENRE_HUMOUR,            "humour",            "Humor" },
+    { mp4v2::impl::itmf::GENRE_SPEECH,            "speech",            "Speech" },
+    { mp4v2::impl::itmf::GENRE_CHANSON,           "chanson",           "Chason" },
+    { mp4v2::impl::itmf::GENRE_OPERA,             "opera",             "Opera" },
+    { mp4v2::impl::itmf::GENRE_CHAMBER_MUSIC,     "chambermusic",      "Chamber Music" },
+    { mp4v2::impl::itmf::GENRE_SONATA,            "sonata",            "Sonata" },
+    { mp4v2::impl::itmf::GENRE_SYMPHONY,          "symphony",          "Symphony" },
+    { mp4v2::impl::itmf::GENRE_BOOTY_BASS,        "bootybass",         "Booty Bass" },
+    { mp4v2::impl::itmf::GENRE_PRIMUS,            "primus",            "Primus" },
+    { mp4v2::impl::itmf::GENRE_PORN_GROOVE,       "porngroove",        "Porn Groove" },
+    { mp4v2::impl::itmf::GENRE_SATIRE,            "satire",            "Satire" },
+    { mp4v2::impl::itmf::GENRE_SLOW_JAM,          "slowjam",           "Slow Jam" },
+    { mp4v2::impl::itmf::GENRE_CLUB,              "club",              "Club" },
+    { mp4v2::impl::itmf::GENRE_TANGO,             "tango",             "Tango" },
+    { mp4v2::impl::itmf::GENRE_SAMBA,             "samba",             "Samba" },
+    { mp4v2::impl::itmf::GENRE_FOLKLORE,          "folklore",          "Folklore" },
+    { mp4v2::impl::itmf::GENRE_BALLAD,            "ballad",            "Ballad" },
+    { mp4v2::impl::itmf::GENRE_POWER_BALLAD,      "powerballad",       "Power Ballad" },
+    { mp4v2::impl::itmf::GENRE_RHYTHMIC_SOUL,     "rhythmicsoul",      "Rhythmic Soul" },
+    { mp4v2::impl::itmf::GENRE_FREESTYLE,         "freestyle",         "Freestyle" },
+    { mp4v2::impl::itmf::GENRE_DUET,              "duet",              "Duet" },
+    { mp4v2::impl::itmf::GENRE_PUNK_ROCK,         "punkrock",          "Punk Rock" },
+    { mp4v2::impl::itmf::GENRE_DRUM_SOLO,         "drumsolo",          "Drum Solo" },
+    { mp4v2::impl::itmf::GENRE_A_CAPELLA,         "acapella",          "A capella" },
+    { mp4v2::impl::itmf::GENRE_EURO_HOUSE,        "eurohouse",         "Euro-House" },
+    { mp4v2::impl::itmf::GENRE_DANCE_HALL,        "dancehall",         "Dance Hall" },
+    { mp4v2::impl::itmf::GENRE_NONE,              "none",              "none" },
+
+    { mp4v2::impl::itmf::GENRE_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const itmf::EnumStikType::Entry itmf::EnumStikType::data[] = {
+    { mp4v2::impl::itmf::STIK_OLD_MOVIE,    "oldmovie",    "Movie" },
+    { mp4v2::impl::itmf::STIK_NORMAL,       "normal",      "Normal" },
+    { mp4v2::impl::itmf::STIK_AUDIOBOOK,    "audiobook",   "Audio Book" },
+    { mp4v2::impl::itmf::STIK_MUSIC_VIDEO,  "musicvideo",  "Music Video" },
+    { mp4v2::impl::itmf::STIK_MOVIE,        "movie",       "Movie" },
+    { mp4v2::impl::itmf::STIK_TV_SHOW,      "tvshow",      "TV Show" },
+    { mp4v2::impl::itmf::STIK_BOOKLET,      "booklet",     "Booklet" },
+    { mp4v2::impl::itmf::STIK_RINGTONE,     "ringtone",    "Ringtone" },
+
+    { mp4v2::impl::itmf::STIK_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const itmf::EnumAccountType::Entry itmf::EnumAccountType::data[] = {
+    { mp4v2::impl::itmf::AT_ITUNES,  "itunes",   "iTunes" },
+    { mp4v2::impl::itmf::AT_AOL,     "aol",      "AOL" },
+
+    { mp4v2::impl::itmf::AT_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const itmf::EnumCountryCode::Entry itmf::EnumCountryCode::data[] = {
+    { mp4v2::impl::itmf::CC_USA,  "usa",   "United States" },
+    { mp4v2::impl::itmf::CC_USA,  "fra",   "France" },
+    { mp4v2::impl::itmf::CC_DEU,  "ger",   "Germany" },
+    { mp4v2::impl::itmf::CC_GBR,  "gbr",   "United Kingdom" },
+    { mp4v2::impl::itmf::CC_AUT,  "aut",   "Austria" },
+    { mp4v2::impl::itmf::CC_BEL,  "bel",   "Belgium" },
+    { mp4v2::impl::itmf::CC_FIN,  "fin",   "Finland" },
+    { mp4v2::impl::itmf::CC_GRC,  "grc",   "Greece" },
+    { mp4v2::impl::itmf::CC_IRL,  "irl",   "Ireland" },
+    { mp4v2::impl::itmf::CC_ITA,  "ita",   "Italy" },
+    { mp4v2::impl::itmf::CC_LUX,  "lux",   "Luxembourg" },
+    { mp4v2::impl::itmf::CC_NLD,  "nld",   "Netherlands" },
+    { mp4v2::impl::itmf::CC_PRT,  "prt",   "Portugal" },
+    { mp4v2::impl::itmf::CC_ESP,  "esp",   "Spain" },
+    { mp4v2::impl::itmf::CC_CAN,  "can",   "Canada" },
+    { mp4v2::impl::itmf::CC_SWE,  "swe",   "Sweden" },
+    { mp4v2::impl::itmf::CC_NOR,  "nor",   "Norway" },
+    { mp4v2::impl::itmf::CC_DNK,  "dnk",   "Denmark" },
+    { mp4v2::impl::itmf::CC_CHE,  "che",   "Switzerland" },
+    { mp4v2::impl::itmf::CC_AUS,  "aus",   "Australia" },
+    { mp4v2::impl::itmf::CC_NZL,  "nzl",   "New Zealand" },
+    { mp4v2::impl::itmf::CC_JPN,  "jpn",   "Japan" },
+
+    { mp4v2::impl::itmf::CC_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <>
+const itmf::EnumContentRating::Entry itmf::EnumContentRating::data[] = {
+    { mp4v2::impl::itmf::CR_NONE,      "none",       "None" },
+    { mp4v2::impl::itmf::CR_CLEAN,     "clean",      "Clean" },
+    { mp4v2::impl::itmf::CR_EXPLICIT,  "explicit",   "Explicit" },
+
+    { mp4v2::impl::itmf::CR_UNDEFINED } // must be last
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace itmf {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// must come after static data init
+const EnumBasicType enumBasicType;
+const EnumGenreType enumGenreType;
+const EnumStikType enumStikType;
+const EnumAccountType enumAccountType;
+const EnumCountryCode enumCountryCode;
+const EnumContentRating enumContentRating;
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+    struct ImageHeader {
+        BasicType type;
+        string    data;
+    };
+
+    // POD static init does not need singletons
+    static ImageHeader IMAGE_HEADERS[] = {
+        { BT_BMP,  "\x42\x4d" },
+        { BT_GIF,  "GIF87a" },
+        { BT_GIF,  "GIF89a" },
+        { BT_JPEG, "\xff\xd8\xff" },
+        { BT_PNG,  "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a" },
+        { BT_UNDEFINED } // must be last
+    };
+}
+
+BasicType
+computeBasicType( const void* buffer, uint32_t size )
+{
+    ImageHeader* found = NULL;
+    for( ImageHeader* p = IMAGE_HEADERS; p->type != BT_UNDEFINED; p++ ) {
+        ImageHeader& h = *p;
+
+        if( size < h.data.size() )
+            continue;
+
+        if( memcmp(h.data.data(), buffer, h.data.size()) == 0 ) {
+            found = &h;
+            break;
+        }
+    }
+
+    return found ? found->type : BT_IMPLICIT;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
diff --git a/src/itmf/type.h b/src/itmf/type.h
new file mode 100644
index 0000000..484c241
--- /dev/null
+++ b/src/itmf/type.h
@@ -0,0 +1,296 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//      Rouven Wessling, mp4v2@rouvenwessling.de
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_ITMF_TYPE_H
+#define MP4V2_IMPL_ITMF_TYPE_H
+
+namespace mp4v2 { namespace impl { namespace itmf {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Basic set of types as detailed in spec.
+enum BasicType {
+    BT_IMPLICIT  = 0,   ///< for use with tags for which no type needs to be indicated
+    BT_UTF8      = 1,   ///< without any count or null terminator
+    BT_UTF16     = 2,   ///< also known as UTF-16BE
+    BT_SJIS      = 3,   ///< deprecated unless it is needed for special Japanese characters
+    BT_HTML      = 6,   ///< the HTML file header specifies which HTML version
+    BT_XML       = 7,   ///< the XML header must identify the DTD or schemas
+    BT_UUID      = 8,   ///< also known as GUID; stored as 16 bytes in binary (valid as an ID)
+    BT_ISRC      = 9,   ///< stored as UTF-8 text (valid as an ID)
+    BT_MI3P      = 10,  ///< stored as UTF-8 text (valid as an ID)
+    BT_GIF       = 12,  ///< (deprecated) a GIF image
+    BT_JPEG      = 13,  ///< a JPEG image
+    BT_PNG       = 14,  ///< a PNG image
+    BT_URL       = 15,  ///< absolute, in UTF-8 characters
+    BT_DURATION  = 16,  ///< in milliseconds, 32-bit integer
+    BT_DATETIME  = 17,  ///< in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits
+    BT_GENRES    = 18,  ///< a list of enumerated values, see #Genre
+    BT_INTEGER   = 21,  ///< a signed big-endian integer with length one of { 1,2,3,4,8 } bytes
+    BT_RIAA_PA   = 24,  ///< RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger
+    BT_UPC       = 25,  ///< Universal Product Code, in text UTF-8 format (valid as an ID)
+    BT_BMP       = 27,  ///< Windows bitmap image
+
+    BT_UNDEFINED = 255
+};
+
+typedef Enum<BasicType,BT_UNDEFINED> EnumBasicType;
+MP4V2_EXPORT extern const EnumBasicType enumBasicType;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// enumerated genre as defined in ID3v1 specification but +1 as per iTMF spec.
+/// Note values 80 and higher are Winamp extensions.
+enum GenreType {
+    GENRE_UNDEFINED          = 0,
+
+    /* ID3v1 standard */
+    GENRE_BLUES              = 1,
+    GENRE_CLASSIC_ROCK       = 2,
+    GENRE_COUNTRY            = 3,
+    GENRE_DANCE              = 4,
+    GENRE_DISCO              = 5,
+    GENRE_FUNK               = 6,
+    GENRE_GRUNGE             = 7,
+    GENRE_HIP_HOP            = 8,
+    GENRE_JAZZ               = 9,
+    GENRE_METAL              = 10,
+    GENRE_NEW_AGE            = 11,
+    GENRE_OLDIES             = 12,
+    GENRE_OTHER              = 13,
+    GENRE_POP                = 14,
+    GENRE_R_AND_B            = 15,
+    GENRE_RAP                = 16,
+    GENRE_REGGAE             = 17,
+    GENRE_ROCK               = 18,
+    GENRE_TECHNO             = 19,
+    GENRE_INDUSTRIAL         = 20,
+    GENRE_ALTERNATIVE        = 21,
+    GENRE_SKA                = 22,
+    GENRE_DEATH_METAL        = 23,
+    GENRE_PRANKS             = 24,
+    GENRE_SOUNDTRACK         = 25,
+    GENRE_EURO_TECHNO        = 26,
+    GENRE_AMBIENT            = 27,
+    GENRE_TRIP_HOP           = 28,
+    GENRE_VOCAL              = 29,
+    GENRE_JAZZ_FUNK          = 30,
+    GENRE_FUSION             = 31,
+    GENRE_TRANCE             = 32,
+    GENRE_CLASSICAL          = 33,
+    GENRE_INSTRUMENTAL       = 34,
+    GENRE_ACID               = 35,
+    GENRE_HOUSE              = 36,
+    GENRE_GAME               = 37,
+    GENRE_SOUND_CLIP         = 38,
+    GENRE_GOSPEL             = 39,
+    GENRE_NOISE              = 40,
+    GENRE_ALTERNROCK         = 41,
+    GENRE_BASS               = 42,
+    GENRE_SOUL               = 43,
+    GENRE_PUNK               = 44,
+    GENRE_SPACE              = 45,
+    GENRE_MEDITATIVE         = 46,
+    GENRE_INSTRUMENTAL_POP   = 47,
+    GENRE_INSTRUMENTAL_ROCK  = 48,
+    GENRE_ETHNIC             = 49,
+    GENRE_GOTHIC             = 50,
+    GENRE_DARKWAVE           = 51,
+    GENRE_TECHNO_INDUSTRIAL  = 52,
+    GENRE_ELECTRONIC         = 53,
+    GENRE_POP_FOLK           = 54,
+    GENRE_EURODANCE          = 55,
+    GENRE_DREAM              = 56,
+    GENRE_SOUTHERN_ROCK      = 57,
+    GENRE_COMEDY             = 58,
+    GENRE_CULT               = 59,
+    GENRE_GANGSTA            = 60,
+    GENRE_TOP_40             = 61,
+    GENRE_CHRISTIAN_RAP      = 62,
+    GENRE_POP_FUNK           = 63,
+    GENRE_JUNGLE             = 64,
+    GENRE_NATIVE_AMERICAN    = 65,
+    GENRE_CABARET            = 66,
+    GENRE_NEW_WAVE           = 67,
+    GENRE_PSYCHEDELIC        = 68,
+    GENRE_RAVE               = 69,
+    GENRE_SHOWTUNES          = 70,
+    GENRE_TRAILER            = 71,
+    GENRE_LO_FI              = 72,
+    GENRE_TRIBAL             = 73,
+    GENRE_ACID_PUNK          = 74,
+    GENRE_ACID_JAZZ          = 75,
+    GENRE_POLKA              = 76,
+    GENRE_RETRO              = 77,
+    GENRE_MUSICAL            = 78,
+    GENRE_ROCK_AND_ROLL      = 79,
+
+    /* Winamp extension */
+    GENRE_HARD_ROCK          = 80,
+    GENRE_FOLK               = 81,
+    GENRE_FOLK_ROCK          = 82,
+    GENRE_NATIONAL_FOLK      = 83,
+    GENRE_SWING              = 84,
+    GENRE_FAST_FUSION        = 85,
+    GENRE_BEBOB              = 86,
+    GENRE_LATIN              = 87,
+    GENRE_REVIVAL            = 88,
+    GENRE_CELTIC             = 89,
+    GENRE_BLUEGRASS          = 90,
+    GENRE_AVANTGARDE         = 91,
+    GENRE_GOTHIC_ROCK        = 92,
+    GENRE_PROGRESSIVE_ROCK   = 93,
+    GENRE_PSYCHEDELIC_ROCK   = 94,
+    GENRE_SYMPHONIC_ROCK     = 95,
+    GENRE_SLOW_ROCK          = 96,
+    GENRE_BIG_BAND           = 97,
+    GENRE_CHORUS             = 98,
+    GENRE_EASY_LISTENING     = 99,
+    GENRE_ACOUSTIC           = 100,
+    GENRE_HUMOUR             = 101,
+    GENRE_SPEECH             = 102,
+    GENRE_CHANSON            = 103,
+    GENRE_OPERA              = 104,
+    GENRE_CHAMBER_MUSIC      = 105,
+    GENRE_SONATA             = 106,
+    GENRE_SYMPHONY           = 107,
+    GENRE_BOOTY_BASS         = 108,
+    GENRE_PRIMUS             = 109,
+    GENRE_PORN_GROOVE        = 110,
+    GENRE_SATIRE             = 111,
+    GENRE_SLOW_JAM           = 112,
+    GENRE_CLUB               = 113,
+    GENRE_TANGO              = 114,
+    GENRE_SAMBA              = 115,
+    GENRE_FOLKLORE           = 116,
+    GENRE_BALLAD             = 117,
+    GENRE_POWER_BALLAD       = 118,
+    GENRE_RHYTHMIC_SOUL      = 119,
+    GENRE_FREESTYLE          = 120,
+    GENRE_DUET               = 121,
+    GENRE_PUNK_ROCK          = 122,
+    GENRE_DRUM_SOLO          = 123,
+    GENRE_A_CAPELLA          = 124,
+    GENRE_EURO_HOUSE         = 125,
+    GENRE_DANCE_HALL         = 126,
+
+    GENRE_NONE = 255
+};
+
+typedef Enum<GenreType,GENRE_UNDEFINED> EnumGenreType;
+MP4V2_EXPORT extern const EnumGenreType enumGenreType;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// enumerated 8-bit Video Type used by iTunes.
+/// Note values are not formally defined in any specification.
+enum StikType {
+    STIK_OLD_MOVIE   = 0,
+    STIK_NORMAL      = 1,
+    STIK_AUDIOBOOK   = 2,
+    STIK_MUSIC_VIDEO = 6,
+    STIK_MOVIE       = 9,
+    STIK_TV_SHOW     = 10,
+    STIK_BOOKLET     = 11,
+    STIK_RINGTONE    = 14,
+
+    STIK_UNDEFINED   = 255
+};
+
+typedef Enum<StikType,STIK_UNDEFINED> EnumStikType;
+MP4V2_EXPORT extern const EnumStikType enumStikType;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// enumerated 8-bit Account Type used by the iTunes Store.
+/// Note values are not formally defined in any specification.
+enum AccountType {
+    AT_ITUNES    = 0,
+    AT_AOL       = 1,
+
+    AT_UNDEFINED = 255
+};
+
+typedef Enum<AccountType,AT_UNDEFINED> EnumAccountType;
+MP4V2_EXPORT extern const EnumAccountType enumAccountType;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// enumerated 32-bit Country Code used by the iTunes Store.
+/// Note values are not formally defined in any specification.
+enum CountryCode {   
+    CC_USA   = 143441,
+    CC_FRA   = 143442,
+    CC_DEU   = 143443,
+    CC_GBR   = 143444,
+    CC_AUT   = 143445,
+    CC_BEL   = 143446,
+    CC_FIN   = 143447,
+    CC_GRC   = 143448,
+    CC_IRL   = 143449,
+    CC_ITA   = 143450,
+    CC_LUX   = 143451,
+    CC_NLD   = 143452,
+    CC_PRT   = 143453,
+    CC_ESP   = 143454,
+    CC_CAN   = 143455,
+    CC_SWE   = 143456,
+    CC_NOR   = 143457,
+    CC_DNK   = 143458,
+    CC_CHE   = 143459,
+    CC_AUS   = 143460,
+    CC_NZL   = 143461,
+    CC_JPN   = 143462,
+
+    CC_UNDEFINED = 0
+};
+
+typedef Enum<CountryCode,CC_UNDEFINED> EnumCountryCode;
+MP4V2_EXPORT extern const EnumCountryCode enumCountryCode;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// enumerated 8-bit Content Rating used by iTunes.
+/// Note values are not formally defined in any specification.
+enum ContentRating {   
+    CR_NONE      = 0,
+    CR_CLEAN     = 2,
+    CR_EXPLICIT  = 4,
+
+    CR_UNDEFINED = 255
+};
+
+typedef Enum<ContentRating,CR_UNDEFINED> EnumContentRating;
+MP4V2_EXPORT extern const EnumContentRating enumContentRating;
+
+///////////////////////////////////////////////////////////////////////////////
+/// compute BasicType by examining raw bytes header.
+MP4V2_EXPORT BasicType
+computeBasicType( const void* buffer, uint32_t size );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::itmf
+
+#endif // MP4V2_IMPL_ITMF_TYPE_H
diff --git a/src/log.cpp b/src/log.cpp
new file mode 100644
index 0000000..26eea6b
--- /dev/null
+++ b/src/log.cpp
@@ -0,0 +1,534 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 David Byron.
+//  Portions created by David Byron are Copyright (C) 2009, 2010, 2011.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include <iomanip>
+#include <iostream>
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+MP4LogCallback Log::_cb_func = NULL;
+
+// There's no mechanism to set the log level at runtime at
+// the moment so construct this so it only logs important
+// stuff.
+Log log(MP4_LOG_WARNING);
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log class constructor
+ */
+Log::Log( MP4LogLevel verbosity_ /* = MP4_LOG_NONE */ )
+    : _verbosity ( verbosity_ )
+    , verbosity  ( _verbosity )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log class destructor
+ */
+Log::~Log()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Mutator for the callback function
+ *
+ * @param value the function to call
+ */
+void
+Log::setLogCallback( MP4LogCallback value )
+{
+    Log::_cb_func = value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Mutator for the verbosity
+ *
+ * @param value the verbosity to use
+ */
+void
+Log::setVerbosity( MP4LogLevel value )
+{
+    _verbosity = value;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log an error message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::errorf( const char* format,
+             ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_ERROR,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a warning message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::warningf( const char* format,
+               ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_WARNING,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log an info message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::infof( const char* format,
+            ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_INFO,format,ap);
+    va_end(ap);
+}
+
+/**
+ * Log a verbose1 message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::verbose1f( const char* format,
+                ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_VERBOSE1,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a verbose2 message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::verbose2f( const char* format,
+                ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_VERBOSE2,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a verbose3 message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::verbose3f( const char* format,
+                ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_VERBOSE3,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a verbose4 message
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::verbose4f( const char* format,
+                ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(MP4_LOG_VERBOSE4,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Dump info to the console or a callback
+ *
+ * @param indent the number of spaces to indent the info
+ *
+ * @param verbosity the level of detail the message contains
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::dump ( uint8_t       indent,
+            MP4LogLevel   verbosity_,
+            const char*   format, ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vdump(indent,verbosity,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Dump info if it has appropriate verbosity, either to
+ * standard out (with a newline appended to @p format) or to
+ * the callback function (with no newline appended).
+ *
+ * @param indent the number of spaces to indent the info
+ *
+ * @param verbosity the level of detail the message contains
+ *
+ * @param format the format string to use to process @p ap.
+ * @p format should not contain a newline.
+ *
+ * @param ap varargs to build the message
+ */
+void
+Log::vdump( uint8_t     indent,
+            MP4LogLevel verbosity_,
+            const char* format,
+            va_list     ap )
+{
+    // Make sure nothing gets logged with MP4_LOG_NONE.
+    // That way people who ask for nothing to get logged
+    // won't get anything logged.
+    ASSERT(verbosity_ != MP4_LOG_NONE);
+    ASSERT(format);
+    ASSERT(format[0] != '\0');
+
+    if (verbosity_ > this->_verbosity)
+    {
+        // We're not set verbose enough to log this
+        return;
+    }
+
+    if (Log::_cb_func)
+    {
+        ostringstream   new_format;
+
+        if (indent > 0)
+        {
+            string      indent_str(indent,' ');
+            // new_format << setw(indent) << setfill(' ') << "" << setw(0);
+            // new_format << format;
+            new_format << indent_str << format;
+            Log::_cb_func(verbosity_,new_format.str().c_str(),ap);
+            return;
+        }
+
+        Log::_cb_func(verbosity_,format,ap);
+        return;
+    }
+
+    // No callback set so log to standard out.  
+    if (indent > 0)
+    {
+        ::fprintf(stdout,"%*c",indent,' ');
+    }
+    ::vfprintf(stdout,format,ap);
+    ::fprintf(stdout,"\n");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a message
+ *
+ * @param verbosity the level of detail the message contains
+ *
+ * @param format the format string to use to process the
+ * remaining arguments.  @p format should not contain a
+ * newline.
+ */
+void
+Log::printf( MP4LogLevel        verbosity,
+             const char*        format,
+             ... )
+{
+    va_list     ap;
+
+    va_start(ap,format);
+    this->vprintf(verbosity,format,ap);
+    va_end(ap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a message if it has appropriate verbosity, either to
+ * standard out (with a newline appended to @p format) or to
+ * the callback function (with no newline appended).
+ *
+ * @param verbosity the level of detail the message contains
+ *
+ * @param format the format string to use to process @p ap.
+ * @p format should not contain a newline.
+ *
+ * @param ap varargs to build the message
+ */
+void
+Log::vprintf( MP4LogLevel       verbosity_,
+              const char*       format,
+              va_list           ap )
+{
+    // Make sure nothing gets logged with MP4_LOG_NONE.
+    // That way people who ask for nothing to get logged
+    // won't get anything logged.
+    ASSERT(verbosity_ != MP4_LOG_NONE);
+    ASSERT(format);
+
+    if (verbosity_ > this->_verbosity)
+    {
+        // We're not set verbose enough to log this
+        return;
+    }
+
+    if (Log::_cb_func)
+    {
+        Log::_cb_func(verbosity_,format,ap);
+        return;
+    }
+
+    // No callback set so log to standard out.  
+    ::vfprintf(stdout,format,ap);
+    ::fprintf(stdout,"\n");
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log a buffer as ascii-hex
+ *
+ * @param indent the number of spaces to indent the buffer
+ *
+ * @param verbosity the level of detail the message contains
+ *
+ * @param pBytes the buffer to log
+ *
+ * @param numBytes the number of bytes to log
+ *
+ * @param format the format string to use to process the
+ * remaining arguments, where the format + remaining args
+ * describe @p pBytes.  The resulting string should not
+ * contain a newline.  Only the first 255 characters of the
+ * resulting string (not including the NUL terminator) make
+ * it to the log callback or stdout.
+ */
+void
+Log::hexDump( uint8_t           indent,
+              MP4LogLevel       verbosity_,
+              const uint8_t*    pBytes,
+              uint32_t          numBytes,
+              const char*       format,
+              ... )
+{
+    va_list     ap;
+
+    ASSERT(pBytes || (numBytes == 0));
+    ASSERT(format);
+
+    if (verbosity_ > this->_verbosity)
+    {
+        // We're not set verbose enough to log this
+        return;
+    }
+
+    // Build the description by processing format and the
+    // remaining args.  Since we don't have asprintf, pick
+    // an arbitrary length for the string and use snprintf.
+    // To save a memory allocation, only do this if there's
+    // a non-empty format string or non-zero indent
+    char *desc = NULL;
+    if (format[0] || indent)
+    {
+        desc = (char *)MP4Calloc(256 + indent);
+        sprintf(desc,"%*c",indent,' ');
+        va_start(ap,format);
+        vsnprintf(desc + indent,255,format,ap);
+        va_end(ap);
+    }
+
+    // From here we can use the C++ standard lib classes and
+    // build a string for each line
+    for (uint32_t i = 0;(i < numBytes);i += 16)
+    {
+        // ios_base::ate means at end.  With out this desc
+        // gets overwritten with each << operation
+        ostringstream oneLine(desc ? desc : "",ios_base::ate);
+
+        // Append the byte offset this line starts with as
+        // an 8 character, leading 0, hex number.  Leave the
+        // fill character set to 0 for the remaining
+        // operations
+        oneLine << ':' << hex << setw(8) << setfill('0') <<
+            std::right << i << setw(0) << setfill(' ') << ": ";
+
+        uint32_t curlen = min((uint32_t)16,numBytes - i);
+        const uint8_t *b = pBytes + i;
+        uint32_t j;
+
+        for (j = 0;(j < curlen);j++)
+        {
+            oneLine << hex << setw(2) << setfill('0') << right << static_cast<uint32_t>(b[j]);
+            oneLine << setw(0) << setfill(' ') << ' ';
+        }
+
+        for (; j < 16; j++)
+        {
+            oneLine << "   ";
+        }
+
+        b = pBytes + i;
+        for (j = 0;(j < curlen);j++)
+        {
+            if (isprint(static_cast<int>(b[j])))
+            {
+                oneLine << static_cast<char>(b[j]);
+            }
+            else
+            {
+                oneLine << '.';
+            }
+        }
+
+        // We can either call the callback directly or use
+        // the Log::printf function.  To call the callback
+        // directly, we need a va_list.  (I think) we need
+        // and extra function call to build that, so we may
+        // as well call Log::printf.  It's going to
+        // double-check the verbosity and the callback
+        // function pointer, but that seems OK (13-feb-09,
+        // dbyron)
+        this->printf(verbosity_,"%s",oneLine.str().c_str());
+    }
+
+    if (desc)
+    {
+        MP4Free(desc);
+        desc = NULL;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Log an Exception as an error
+ *
+ * @param x the exception to log
+ */
+void
+Log::errorf ( const Exception&      x )
+{
+    this->printf(MP4_LOG_ERROR,"%s",x.msg().c_str());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+using namespace mp4v2::impl;
+
+extern "C"
+void MP4SetLogCallback( MP4LogCallback cb_func )
+{
+    Log::setLogCallback(cb_func);
+}
+
+extern "C"
+MP4LogLevel MP4LogGetLevel(void)
+{
+    return mp4v2::impl::log.verbosity;
+}
+
+extern "C"
+void MP4LogSetLevel( MP4LogLevel verbosity )
+{
+    try
+    {
+        mp4v2::impl::log.setVerbosity(verbosity);
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+    }
+}
+
diff --git a/src/log.h b/src/log.h
new file mode 100644
index 0000000..e7a6e00
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,93 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 David Byron.
+//  Portions created by David Byron are Copyright (C) 2009, 2010, 2011.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_LOG_H
+#define MP4V2_IMPL_LOG_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Handle logging either to standard out or to a callback
+ * function
+ */
+class MP4V2_EXPORT Log {
+private:
+    MP4LogLevel                 _verbosity;
+    static MP4LogCallback       _cb_func;
+
+public:
+    const MP4LogLevel&          verbosity;
+
+public:
+    Log( MP4LogLevel = MP4_LOG_NONE );
+    virtual ~Log();
+
+    static void setLogCallback ( MP4LogCallback );
+
+    void setVerbosity   ( MP4LogLevel );
+
+    void errorf ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void warningf ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void infof ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void verbose1f ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void verbose2f ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void verbose3f ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+    void verbose4f ( const char* format, ... ) MP4V2_WFORMAT_PRINTF(2,3);
+
+    void dump ( uint8_t       indent,
+                MP4LogLevel   verbosity_,
+                const char*   format, ... ) MP4V2_WFORMAT_PRINTF(4,5);
+    void vdump ( uint8_t       indent,
+                 MP4LogLevel   verbosity_,
+                 const char*   format, va_list ap );
+    void printf ( MP4LogLevel   verbosity_,
+                  const char*   format, ... ) MP4V2_WFORMAT_PRINTF(3,4);
+    void vprintf ( MP4LogLevel  verbosity_,
+                   const char*  format, va_list ap );
+
+    void hexDump ( uint8_t              indent,
+                   MP4LogLevel          verbosity_,
+                   const uint8_t*       pBytes,
+                   uint32_t             numBytes,
+                   const char*          format, ... ) MP4V2_WFORMAT_PRINTF(6,7);
+
+    void errorf ( const Exception&      x );
+
+private:
+    Log ( const Log &src );
+    Log &operator= ( const Log &src );
+};
+
+/**
+ * A global (at least to mp4v2) log object for code that
+ * needs to log something but doesn't otherwise have access
+ * to one
+ */
+extern Log log;
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_LOG_H
diff --git a/src/mp4.cpp b/src/mp4.cpp
new file mode 100644
index 0000000..1016f79
--- /dev/null
+++ b/src/mp4.cpp
@@ -0,0 +1,4612 @@
+/*
+ * 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 - 2005.  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.
+ *
+ * Contributor(s):
+ *      Dave Mackie         dmackie@cisco.com
+ *      Alix Marchandise-Franquet   alix@cisco.com
+ *              Ximpo Group Ltd.                mp4v2@ximpo.com
+ *              Bill May                        wmay@cisco.com
+ */
+
+/*
+ * MP4 library API functions
+ *
+ * These are wrapper functions that provide C linkage conventions
+ * to the library, and catch any internal errors, ensuring that
+ * a proper return value is given.
+ */
+
+#include "src/impl.h"
+
+using namespace mp4v2::impl;
+
+static MP4File  *ConstructMP4File ( void )
+{
+    MP4File* pFile = NULL;
+    try {
+        pFile = new MP4File();
+    }
+    catch( std::bad_alloc ) {
+        mp4v2::impl::log.errorf("%s: unable to allocate MP4File", __FUNCTION__);
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: unknown exception constructing MP4File", __FUNCTION__ );
+    }
+
+    return pFile;
+}
+
+extern "C" {
+
+const char* MP4GetFilename( MP4FileHandle hFile )
+{
+    if (!MP4_IS_VALID_FILE_HANDLE(hFile))
+        return NULL;
+    try
+    {
+        ASSERT(hFile);
+        MP4File& file = *static_cast<MP4File*>(hFile);
+        ASSERT(file.GetFilename().c_str());
+        return file.GetFilename().c_str();
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: unknown exception accessing MP4File "
+                                "filename", __FUNCTION__ );
+    }
+
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4FileHandle MP4Read( const char* fileName )
+{
+    if (!fileName)
+        return MP4_INVALID_FILE_HANDLE;
+
+    MP4File *pFile = ConstructMP4File();
+    if (!pFile)
+        return MP4_INVALID_FILE_HANDLE;
+
+    try
+    {
+        ASSERT(pFile);
+        pFile->Read( fileName, NULL );
+        return (MP4FileHandle)pFile;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: \"%s\": failed", __FUNCTION__,
+                                fileName );
+    }
+
+    if (pFile)
+        delete pFile;
+    return MP4_INVALID_FILE_HANDLE;
+}
+
+MP4FileHandle MP4ReadProvider( const char* fileName, const MP4FileProvider* fileProvider )
+{
+    if (!fileName)
+        return MP4_INVALID_FILE_HANDLE;
+
+    MP4File *pFile = ConstructMP4File();
+    if (!pFile)
+        return MP4_INVALID_FILE_HANDLE;
+
+    try {
+        pFile->Read( fileName, fileProvider );
+        return (MP4FileHandle)pFile;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: \"%s\": failed", __FUNCTION__,
+                                fileName );
+    }
+
+    if (pFile)
+        delete pFile;
+    return MP4_INVALID_FILE_HANDLE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+    MP4FileHandle MP4Create (const char* fileName,
+                             uint32_t flags)
+    {
+        return MP4CreateProviderEx(fileName, flags);
+    }
+
+    MP4FileHandle MP4CreateEx (const char* fileName,
+                               uint32_t  flags,
+                               int add_ftyp,
+                               int add_iods,
+                               char* majorBrand,
+                               uint32_t minorVersion,
+                               char** supportedBrands,
+                               uint32_t supportedBrandsCount)
+    {
+        return MP4CreateProviderEx(fileName, flags, NULL,
+                                   add_ftyp, add_iods,
+                                   majorBrand, minorVersion,
+                                   supportedBrands, supportedBrandsCount);
+    }
+
+    MP4FileHandle MP4CreateProvider (const char* fileName,
+                                     uint32_t flags,
+                                     const MP4FileProvider* fileProvider)
+    {
+        return MP4CreateProviderEx(fileName, flags, fileProvider);
+    }
+
+    MP4FileHandle MP4CreateProviderEx (const char* fileName,
+                                       uint32_t flags,
+                                       const MP4FileProvider* fileProvider,
+                                       int add_ftyp,
+                                       int add_iods,
+                                       char* majorBrand,
+                                       uint32_t minorVersion,
+                                       char** supportedBrands,
+                                       uint32_t supportedBrandsCount)
+    {
+        if (!fileName)
+            return MP4_INVALID_FILE_HANDLE;
+
+        MP4File* pFile = ConstructMP4File();
+        if (!pFile)
+            return MP4_INVALID_FILE_HANDLE;
+
+        try {
+            ASSERT(pFile);
+            // LATER useExtensibleFormat, moov first, then mvex's
+            pFile->Create(fileName, flags, fileProvider,
+                          add_ftyp, add_iods,
+                          majorBrand, minorVersion,
+                          supportedBrands, supportedBrandsCount);
+            return (MP4FileHandle)pFile;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: \"%s\": failed", __FUNCTION__,
+                                    fileName );
+        }
+
+        if (pFile)
+            delete pFile;
+        return MP4_INVALID_FILE_HANDLE;
+    }
+
+///////////////////////////////////////////////////////////////////////////////
+
+    MP4FileHandle MP4Modify(const char* fileName,
+                            uint32_t flags)
+    {
+        if (!fileName)
+            return MP4_INVALID_FILE_HANDLE;
+
+        MP4File* pFile = ConstructMP4File();
+        if (!pFile)
+            return MP4_INVALID_FILE_HANDLE;
+
+        try {
+            ASSERT(pFile);
+            // LATER useExtensibleFormat, moov first, then mvex's
+            if (pFile->Modify(fileName))
+                return (MP4FileHandle)pFile;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: \"%s\": failed", __FUNCTION__,
+                                    fileName );
+        }
+
+        if (pFile)
+            delete pFile;
+        return MP4_INVALID_FILE_HANDLE;
+    }
+
+    bool MP4Optimize(const char* fileName,
+                     const char* newFileName)
+    {
+        // Must at least have fileName for in-place optimize; newFileName
+        // can be null, however.
+        if (fileName == NULL)
+            return false;
+
+        MP4File* pFile = ConstructMP4File();
+        if (!pFile)
+            return MP4_INVALID_FILE_HANDLE;
+
+        try {
+            ASSERT(pFile);
+            pFile->Optimize(fileName, newFileName);
+            delete pFile;
+            return true;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s(%s,%s) failed", __FUNCTION__,
+                                    fileName, newFileName );
+        }
+
+        if (pFile)
+            delete pFile;
+        return false;
+    }
+
+    void MP4Close(MP4FileHandle hFile, uint32_t  flags)
+    {
+        if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+            return;
+
+        MP4File& f = *(MP4File*)hFile;
+        try {
+            f.Close(flags);
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+        }
+
+        delete &f;
+    }
+
+    bool MP4Dump(
+        MP4FileHandle hFile,
+        bool dumpImplicits)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->Dump(dumpImplicits);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    MP4Duration MP4GetDuration(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetDuration();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    uint32_t MP4GetTimeScale(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTimeScale();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4SetTimeScale(MP4FileHandle hFile, uint32_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTimeScale(value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint8_t MP4GetODProfileLevel(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetODProfileLevel();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4SetODProfileLevel(MP4FileHandle hFile, uint8_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetODProfileLevel(value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint8_t MP4GetSceneProfileLevel(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSceneProfileLevel();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4SetSceneProfileLevel(MP4FileHandle hFile, uint8_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetSceneProfileLevel(value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint8_t MP4GetVideoProfileLevel(MP4FileHandle hFile,
+                                    MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetVideoProfileLevel();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+            if (MP4_IS_VALID_TRACK_ID(trackId)) {
+                uint8_t *foo;
+                uint32_t bufsize;
+                uint8_t type;
+                // for mpeg4 video tracks, try to look for the VOSH header,
+                // which has this info.
+                type = MP4GetTrackEsdsObjectTypeId(hFile, trackId);
+                if (type == MP4_MPEG4_VIDEO_TYPE) {
+                    if (MP4GetTrackESConfiguration(hFile,
+                                                   trackId,
+                                                   &foo,
+                                                   &bufsize)) {
+                        uint8_t *ptr = foo;
+                        while (bufsize > 0) {
+                            if (MP4V2_HTONL(*(uint32_t *)ptr) == 0x1b0) {
+                                uint8_t ret = ptr[4];
+                                free(foo);
+                                return ret;
+                            }
+                            ptr++;
+                            bufsize--;
+                        }
+                        free(foo);
+                    }
+                }
+            }
+
+        }
+        return 0;
+    }
+
+    void MP4SetVideoProfileLevel(MP4FileHandle hFile, uint8_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetVideoProfileLevel(value);
+                return ;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return ;
+    }
+
+    uint8_t MP4GetAudioProfileLevel(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetAudioProfileLevel();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    void MP4SetAudioProfileLevel(MP4FileHandle hFile, uint8_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetAudioProfileLevel(value);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    uint8_t MP4GetGraphicsProfileLevel(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetGraphicsProfileLevel();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4SetGraphicsProfileLevel(MP4FileHandle hFile, uint8_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetGraphicsProfileLevel(value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    /* generic file properties */
+
+    bool MP4HaveAtom (MP4FileHandle hFile, const char *atomName)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File *)hFile)->FindAtom(atomName) != NULL;
+            } catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetIntegerProperty(
+        MP4FileHandle hFile, const char* propName, uint64_t *retvalue)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *retvalue = ((MP4File*)hFile)->GetIntegerProperty(propName);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetFloatProperty(
+        MP4FileHandle hFile, const char* propName, float *retvalue)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *retvalue = ((MP4File*)hFile)->GetFloatProperty(propName);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetStringProperty(
+        MP4FileHandle hFile, const char* propName,
+        const char **retvalue)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *retvalue =  ((MP4File*)hFile)->GetStringProperty(propName);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetBytesProperty(
+        MP4FileHandle hFile, const char* propName,
+        uint8_t** ppValue, uint32_t* pValueSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->GetBytesProperty(propName, ppValue, pValueSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        *ppValue = NULL;
+        *pValueSize = 0;
+        return false;
+    }
+
+    bool MP4SetIntegerProperty(
+        MP4FileHandle hFile, const char* propName, int64_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetIntegerProperty(propName, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetFloatProperty(
+        MP4FileHandle hFile, const char* propName, float value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetFloatProperty(propName, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetStringProperty(
+        MP4FileHandle hFile, const char* propName, const char* value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetStringProperty(propName, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetBytesProperty(
+        MP4FileHandle hFile, const char* propName,
+        const uint8_t* pValue, uint32_t valueSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetBytesProperty(propName, pValue, valueSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    /* track operations */
+
+    MP4TrackId MP4AddTrack(
+        MP4FileHandle hFile, const char* type,uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddSystemsTrack(type, timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddSystemsTrack(
+        MP4FileHandle hFile, const char* type)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddSystemsTrack(type);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddODTrack(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddODTrack();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddSceneTrack(MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddSceneTrack();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddULawAudioTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       AddULawAudioTrack(timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddALawAudioTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       AddALawAudioTrack(timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddAudioTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint8_t audioType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       AddAudioTrack(timeScale, sampleDuration, audioType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+//
+// API to initialize ismacryp properties to sensible defaults.
+// if the input pointer is null then an ismacryp params is malloc'd.
+// caller must see to it that it is properly disposed of.
+//
+    mp4v2_ismacrypParams *MP4DefaultISMACrypParams(mp4v2_ismacrypParams *ptr)
+    {
+        try
+        {
+            if (ptr == NULL) {
+                ptr = (mp4v2_ismacrypParams *)MP4Malloc(sizeof(mp4v2_ismacrypParams));
+            }
+            memset(ptr, 0, sizeof(*ptr));
+            return ptr;
+        }
+
+        catch (...) {
+            return MP4_INVALID_TRACK_ID;
+        }
+    }
+
+
+    MP4TrackId MP4AddAC3AudioTrack(
+        MP4FileHandle hFile,
+        uint32_t samplingRate,
+        uint8_t fscod,
+        uint8_t bsid,
+        uint8_t bsmod,
+        uint8_t acmod,
+        uint8_t lfeon,
+        uint8_t bit_rate_code)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                    AddAC3AudioTrack(samplingRate, fscod, bsid, bsmod, acmod, lfeon, bit_rate_code);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddEncAudioTrack(MP4FileHandle hFile,
+                                   uint32_t timeScale,
+                                   MP4Duration sampleDuration,
+                                   mp4v2_ismacrypParams *icPp,
+                                   uint8_t audioType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                if (icPp == NULL) {
+                    return ((MP4File*)hFile)->
+                           AddEncAudioTrack(timeScale, sampleDuration, audioType,
+                                            0, 0,
+                                            0, 0,
+                                            false, NULL, false);
+                } else {
+                    return ((MP4File*)hFile)->
+                           AddEncAudioTrack(timeScale, sampleDuration, audioType,
+                                            icPp->scheme_type, icPp->scheme_version,
+                                            icPp->key_ind_len, icPp->iv_len,
+                                            icPp->selective_enc, icPp->kms_uri, true);
+                }
+            } catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+    MP4TrackId MP4AddAmrAudioTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale,
+        uint16_t modeSet,
+        uint8_t modeChangePeriod,
+        uint8_t framesPerSample,
+        bool isAmrWB)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       AddAmrAudioTrack(timeScale, modeSet, modeChangePeriod, framesPerSample, isAmrWB);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    void MP4SetAmrVendor(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint32_t vendor)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->
+                SetAmrVendor(trackId, vendor);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    void MP4SetAmrDecoderVersion(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint8_t decoderVersion)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->
+                SetAmrDecoderVersion(trackId, decoderVersion);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    void MP4SetAmrModeSet(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint16_t modeSet)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->
+                SetAmrModeSet(trackId, modeSet);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    uint16_t MP4GetAmrModeSet(
+        MP4FileHandle hFile,
+        MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       GetAmrModeSet(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    MP4TrackId MP4AddHrefTrack (MP4FileHandle hFile,
+                                uint32_t timeScale,
+                                MP4Duration sampleDuration,
+                                const char *base_url)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4File *pFile = (MP4File *)hFile;
+
+                return pFile->AddHrefTrack(timeScale,
+                                           sampleDuration,
+                                           base_url);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    const char *MP4GetHrefTrackBaseUrl (MP4FileHandle hFile,
+                                        MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackStringProperty(trackId,
+                        "mdia.minf.stbl.stsd.href.burl.base_url");
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+
+    MP4TrackId MP4AddVideoTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        uint8_t videoType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4File *pFile = (MP4File *)hFile;
+
+                return pFile->AddMP4VideoTrack(timeScale,
+                                               sampleDuration,
+                                               width,
+                                               height,
+                                               videoType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddEncVideoTrack(MP4FileHandle hFile,
+                                   uint32_t timeScale,
+                                   MP4Duration sampleDuration,
+                                   uint16_t width,
+                                   uint16_t height,
+                                   mp4v2_ismacrypParams *icPp,
+                                   uint8_t videoType,
+                                   const char *oFormat)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+
+                // test for valid ismacrypt session descriptor
+                if (icPp == NULL) {
+                    return MP4_INVALID_TRACK_ID;
+                }
+                MP4File *pFile = (MP4File *)hFile;
+
+                return pFile->AddEncVideoTrack(timeScale,
+                                               sampleDuration,
+                                               width,
+                                               height,
+                                               videoType,
+                                               icPp,
+                                               oFormat);
+
+            } catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddColr(
+        MP4FileHandle hFile, MP4TrackId refTrackId, uint16_t pri, uint16_t tran, uint16_t mat)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddColr(refTrackId, pri, tran, mat);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+
+    MP4TrackId MP4AddH264VideoTrack(MP4FileHandle hFile,
+                                    uint32_t timeScale,
+                                    MP4Duration sampleDuration,
+                                    uint16_t width,
+                                    uint16_t height,
+                                    uint8_t AVCProfileIndication,
+                                    uint8_t profile_compat,
+                                    uint8_t AVCLevelIndication,
+                                    uint8_t sampleLenFieldSizeMinusOne)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4File *pFile = (MP4File *)hFile;
+
+                return pFile->AddH264VideoTrack(timeScale,
+                                                sampleDuration,
+                                                width,
+                                                height,
+                                                AVCProfileIndication,
+                                                profile_compat,
+                                                AVCLevelIndication,
+                                                sampleLenFieldSizeMinusOne);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddEncH264VideoTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        MP4FileHandle srcFile,
+        MP4TrackId srcTrackId,
+        mp4v2_ismacrypParams *icPp
+    )
+
+    {
+        MP4Atom *srcAtom;
+        MP4File *pFile;
+
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+
+                pFile = (MP4File *)srcFile;
+                srcAtom = pFile->FindTrackAtom(srcTrackId, "mdia.minf.stbl.stsd.avc1.avcC");
+                if (srcAtom == NULL)
+                    return MP4_INVALID_TRACK_ID;
+
+                pFile = (MP4File *)hFile;
+
+                return pFile->AddEncH264VideoTrack(timeScale,
+                                                   sampleDuration,
+                                                   width,
+                                                   height,
+                                                   srcAtom,
+                                                   icPp);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    void MP4AddH264SequenceParameterSet (MP4FileHandle hFile,
+                                         MP4TrackId trackId,
+                                         const uint8_t *pSequence,
+                                         uint16_t sequenceLen)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4File *pFile = (MP4File *)hFile;
+
+                pFile->AddH264SequenceParameterSet(trackId,
+                                                   pSequence,
+                                                   sequenceLen);
+                return;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return;
+    }
+    void MP4AddH264PictureParameterSet (MP4FileHandle hFile,
+                                        MP4TrackId trackId,
+                                        const uint8_t *pPict,
+                                        uint16_t pictLen)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4File *pFile = (MP4File *)hFile;
+
+                pFile->AddH264PictureParameterSet(trackId,
+                                                  pPict,
+                                                  pictLen);
+                return;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return;
+    }
+
+    MP4TrackId MP4AddH263VideoTrack(
+        MP4FileHandle hFile,
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        uint8_t h263Level,
+        uint8_t h263Profile,
+        uint32_t avgBitrate,
+        uint32_t maxBitrate)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       AddH263VideoTrack(timeScale, sampleDuration, width, height, h263Level, h263Profile, avgBitrate, maxBitrate);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    void MP4SetH263Vendor(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint32_t vendor)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->
+                SetH263Vendor(trackId, vendor);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    void MP4SetH263DecoderVersion(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint8_t decoderVersion)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+
+            try {
+                ((MP4File*)hFile)->
+                SetH263DecoderVersion(trackId, decoderVersion);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    void MP4SetH263Bitrates(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint32_t avgBitrate,
+        uint32_t maxBitrate)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+
+            try {
+                ((MP4File*)hFile)->
+                SetH263Bitrates(trackId, avgBitrate, maxBitrate);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    MP4TrackId MP4AddHintTrack(
+        MP4FileHandle hFile, MP4TrackId refTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddHintTrack(refTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddTextTrack(
+        MP4FileHandle hFile, MP4TrackId refTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddTextTrack(refTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddSubtitleTrack(MP4FileHandle hFile,
+                                   uint32_t timescale,
+                                   uint16_t width,
+                                   uint16_t height)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddSubtitleTrack(timescale, width, height);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddSubpicTrack(MP4FileHandle hFile,
+                                   uint32_t timescale,
+                                   uint16_t width,
+                                   uint16_t height)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddSubpicTrack(timescale, width, height);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddChapterTextTrack(
+        MP4FileHandle hFile, MP4TrackId refTrackId, uint32_t timescale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddChapterTextTrack(refTrackId, timescale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId MP4AddPixelAspectRatio(
+        MP4FileHandle hFile, MP4TrackId refTrackId, uint32_t hSpacing, uint32_t vSpacing)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->AddPixelAspectRatio(refTrackId, hSpacing, vSpacing);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    void MP4AddChapter(
+        MP4FileHandle hFile, MP4TrackId chapterTrackId, MP4Duration chapterDuration, const char *chapterTitle)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddChapter(chapterTrackId, chapterDuration, chapterTitle);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    void MP4AddNeroChapter(
+        MP4FileHandle hFile, MP4Timestamp chapterStart, const char *chapterTitle)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddNeroChapter(chapterStart, chapterTitle);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    MP4ChapterType MP4ConvertChapters(
+        MP4FileHandle hFile, MP4ChapterType toChapterType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile))
+        {
+            try {
+                return ((MP4File*)hFile)->ConvertChapters(toChapterType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4ChapterTypeNone;
+    }
+
+    MP4ChapterType MP4DeleteChapters(
+        MP4FileHandle hFile, MP4ChapterType fromChapterType, MP4TrackId chapterTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->DeleteChapters(fromChapterType, chapterTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4ChapterTypeNone;
+    }
+
+    MP4ChapterType MP4GetChapters(
+        MP4FileHandle hFile, MP4Chapter_t ** chapterList, uint32_t * chapterCount, MP4ChapterType fromChapterType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetChapters(chapterList, chapterCount, fromChapterType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4ChapterTypeNone;
+    }
+
+    MP4ChapterType MP4SetChapters(
+        MP4FileHandle hFile, MP4Chapter_t * chapterList, uint32_t chapterCount, MP4ChapterType toChapterType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->SetChapters(chapterList, chapterCount, toChapterType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4ChapterTypeNone;
+    }
+
+    void MP4ChangeMovieTimeScale(
+        MP4FileHandle hFile, uint32_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->ChangeMovieTimeScale(value);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+    }
+
+    MP4TrackId MP4CloneTrack (MP4FileHandle srcFile,
+                              MP4TrackId srcTrackId,
+                              MP4FileHandle dstFile,
+                              MP4TrackId dstHintTrackReferenceTrack)
+    {
+        MP4TrackId dstTrackId = MP4_INVALID_TRACK_ID;
+
+        if (dstFile == NULL) {
+            dstFile = srcFile;
+        }
+
+        const char* trackType =
+            MP4GetTrackType(srcFile, srcTrackId);
+
+        if (!trackType) {
+            return dstTrackId;
+        }
+
+        const char *media_data_name =
+            MP4GetTrackMediaDataName(srcFile, srcTrackId);
+        if (media_data_name == NULL) return dstTrackId;
+
+        if (MP4_IS_VIDEO_TRACK_TYPE(trackType)) {
+            if (ATOMID(media_data_name) == ATOMID("mp4v")) {
+                MP4SetVideoProfileLevel(dstFile,
+                                        MP4GetVideoProfileLevel(srcFile));
+                dstTrackId = MP4AddVideoTrack(
+                                 dstFile,
+                                 MP4GetTrackTimeScale(srcFile,
+                                                      srcTrackId),
+                                 MP4GetTrackFixedSampleDuration(srcFile,
+                                                                srcTrackId),
+                                 MP4GetTrackVideoWidth(srcFile,
+                                                       srcTrackId),
+                                 MP4GetTrackVideoHeight(srcFile,
+                                                        srcTrackId),
+                                 MP4GetTrackEsdsObjectTypeId(srcFile,
+                                                             srcTrackId));
+            } else if (ATOMID(media_data_name) == ATOMID("avc1")) {
+                uint8_t AVCProfileIndication;
+                uint8_t profile_compat;
+                uint8_t AVCLevelIndication;
+                uint32_t sampleLenFieldSizeMinusOne;
+                uint64_t temp;
+
+                if (MP4GetTrackH264ProfileLevel(srcFile, srcTrackId,
+                                                &AVCProfileIndication,
+                                                &AVCLevelIndication) == false) {
+                    return dstTrackId;
+                }
+                if (MP4GetTrackH264LengthSize(srcFile, srcTrackId,
+                                              &sampleLenFieldSizeMinusOne) == false) {
+                    return dstTrackId;
+                }
+                sampleLenFieldSizeMinusOne--;
+                if (MP4GetTrackIntegerProperty(srcFile, srcTrackId,
+                                               "mdia.minf.stbl.stsd.*[0].avcC.profile_compatibility",
+                                               &temp) == false) return dstTrackId;
+                profile_compat = temp & 0xff;
+
+                dstTrackId = MP4AddH264VideoTrack(dstFile,
+                                                  MP4GetTrackTimeScale(srcFile,
+                                                                       srcTrackId),
+                                                  MP4GetTrackFixedSampleDuration(srcFile,
+                                                                                 srcTrackId),
+                                                  MP4GetTrackVideoWidth(srcFile,
+                                                                        srcTrackId),
+                                                  MP4GetTrackVideoHeight(srcFile,
+                                                                         srcTrackId),
+                                                  AVCProfileIndication,
+                                                  profile_compat,
+                                                  AVCLevelIndication,
+                                                  sampleLenFieldSizeMinusOne);
+                uint8_t **seqheader, **pictheader;
+                uint32_t *pictheadersize, *seqheadersize;
+                uint32_t ix;
+                MP4GetTrackH264SeqPictHeaders(srcFile, srcTrackId,
+                                              &seqheader, &seqheadersize,
+                                              &pictheader, &pictheadersize);
+                for (ix = 0; seqheadersize[ix] != 0; ix++) {
+                    MP4AddH264SequenceParameterSet(dstFile, dstTrackId,
+                                                   seqheader[ix], seqheadersize[ix]);
+                    free(seqheader[ix]);
+                }
+                free(seqheader);
+                free(seqheadersize);
+                for (ix = 0; pictheadersize[ix] != 0; ix++) {
+                    MP4AddH264PictureParameterSet(dstFile, dstTrackId,
+                                                  pictheader[ix], pictheadersize[ix]);
+                    free(pictheader[ix]);
+                }
+                free(pictheader);
+                free(pictheadersize);
+            } else
+                return dstTrackId;
+        } else if (MP4_IS_AUDIO_TRACK_TYPE(trackType)) {
+            if (ATOMID(media_data_name) != ATOMID("mp4a")) return dstTrackId;
+            MP4SetAudioProfileLevel(dstFile,
+                                    MP4GetAudioProfileLevel(srcFile));
+            dstTrackId = MP4AddAudioTrack(
+                             dstFile,
+                             MP4GetTrackTimeScale(srcFile, srcTrackId),
+                             MP4GetTrackFixedSampleDuration(srcFile, srcTrackId),
+                             MP4GetTrackEsdsObjectTypeId(srcFile, srcTrackId));
+
+        } else if (MP4_IS_OD_TRACK_TYPE(trackType)) {
+            dstTrackId = MP4AddODTrack(dstFile);
+
+        } else if (MP4_IS_SCENE_TRACK_TYPE(trackType)) {
+            dstTrackId = MP4AddSceneTrack(dstFile);
+
+        } else if (MP4_IS_HINT_TRACK_TYPE(trackType)) {
+            if (dstHintTrackReferenceTrack == MP4_INVALID_TRACK_ID) {
+                dstTrackId = MP4_INVALID_TRACK_ID;
+            } else {
+                dstTrackId = MP4AddHintTrack(
+                                 dstFile,
+                                 dstHintTrackReferenceTrack);
+            }
+
+        } else if (MP4_IS_SYSTEMS_TRACK_TYPE(trackType)) {
+            dstTrackId = MP4AddSystemsTrack(dstFile, trackType);
+
+        } else {
+            dstTrackId = MP4AddTrack(dstFile, trackType);
+        }
+
+        if (dstTrackId == MP4_INVALID_TRACK_ID) {
+            return dstTrackId;
+        }
+
+        MP4SetTrackTimeScale(
+            dstFile,
+            dstTrackId,
+            MP4GetTrackTimeScale(srcFile, srcTrackId));
+
+        if (MP4_IS_AUDIO_TRACK_TYPE(trackType)
+                || MP4_IS_VIDEO_TRACK_TYPE(trackType)) {
+            // copy track ES configuration
+            uint8_t* pConfig = NULL;
+            uint32_t configSize = 0;
+            MP4LogLevel verb = mp4v2::impl::log.verbosity;
+            mp4v2::impl::log.setVerbosity(MP4_LOG_NONE);
+            bool haveEs = MP4GetTrackESConfiguration(srcFile,
+                          srcTrackId,
+                          &pConfig,
+                          &configSize);
+            mp4v2::impl::log.setVerbosity(verb);
+            if (haveEs &&
+                    pConfig != NULL && configSize != 0) {
+                if (!MP4SetTrackESConfiguration(
+                            dstFile,
+                            dstTrackId,
+                            pConfig,
+                            configSize)) {
+                    free(pConfig);
+                    MP4DeleteTrack(dstFile, dstTrackId);
+                    return MP4_INVALID_TRACK_ID;
+                }
+
+                free(pConfig);
+            }
+        }
+
+        if (MP4_IS_HINT_TRACK_TYPE(trackType)) {
+            // probably not exactly what is wanted
+            // but caller can adjust later to fit their desires
+
+            char* payloadName = NULL;
+            char *encodingParms = NULL;
+            uint8_t payloadNumber;
+            uint16_t maxPayloadSize;
+
+            if (MP4GetHintTrackRtpPayload(
+                        srcFile,
+                        srcTrackId,
+                        &payloadName,
+                        &payloadNumber,
+                        &maxPayloadSize,
+                        &encodingParms)) {
+
+                if (MP4SetHintTrackRtpPayload(
+                            dstFile,
+                            dstTrackId,
+                            payloadName,
+                            &payloadNumber,
+                            maxPayloadSize,
+                            encodingParms) == false) {
+                    MP4DeleteTrack(dstFile, dstTrackId);
+                    return MP4_INVALID_TRACK_ID;
+                }
+            }
+#if 0
+            MP4SetHintTrackSdp(
+                dstFile,
+                dstTrackId,
+                MP4GetHintTrackSdp(srcFile, srcTrackId));
+#endif
+        }
+
+        return dstTrackId;
+    }
+
+// Given a track, make an encrypted clone of it in the dest. file
+    MP4TrackId MP4EncAndCloneTrack(MP4FileHandle srcFile,
+                                   MP4TrackId srcTrackId,
+                                   mp4v2_ismacrypParams *icPp,
+                                   MP4FileHandle dstFile,
+                                   MP4TrackId dstHintTrackReferenceTrack
+                                  )
+    {
+        const char *oFormat;
+
+        MP4TrackId dstTrackId = MP4_INVALID_TRACK_ID;
+
+        if (dstFile == NULL) {
+            dstFile = srcFile;
+        }
+
+        const char* trackType = MP4GetTrackType(srcFile, srcTrackId);
+
+        if (!trackType) {
+            return dstTrackId;
+        }
+
+        if (MP4_IS_VIDEO_TRACK_TYPE(trackType)) {
+
+            // test source file format for avc1
+            oFormat = MP4GetTrackMediaDataName(srcFile, srcTrackId);
+            if (!strcasecmp(oFormat, "avc1"))
+            {
+                dstTrackId = MP4AddEncH264VideoTrack(dstFile,
+                                                     MP4GetTrackTimeScale(srcFile, srcTrackId),
+                                                     MP4GetTrackFixedSampleDuration(srcFile, srcTrackId),
+                                                     MP4GetTrackVideoWidth(srcFile, srcTrackId),
+                                                     MP4GetTrackVideoHeight(srcFile, srcTrackId),
+                                                     srcFile,
+                                                     srcTrackId,
+                                                     icPp
+                                                    );
+            }
+            else
+            {
+                MP4SetVideoProfileLevel(dstFile, MP4GetVideoProfileLevel(srcFile));
+                dstTrackId = MP4AddEncVideoTrack(dstFile,
+                                                 MP4GetTrackTimeScale(srcFile, srcTrackId),
+                                                 MP4GetTrackFixedSampleDuration(srcFile, srcTrackId),
+                                                 MP4GetTrackVideoWidth(srcFile, srcTrackId),
+                                                 MP4GetTrackVideoHeight(srcFile, srcTrackId),
+                                                 icPp,
+                                                 MP4GetTrackEsdsObjectTypeId(srcFile, srcTrackId),
+                                                 oFormat
+                                                );
+            }
+
+        } else if (MP4_IS_AUDIO_TRACK_TYPE(trackType)) {
+            MP4SetAudioProfileLevel(dstFile, MP4GetAudioProfileLevel(srcFile));
+            dstTrackId = MP4AddEncAudioTrack(dstFile,
+                                             MP4GetTrackTimeScale(srcFile, srcTrackId),
+                                             MP4GetTrackFixedSampleDuration(srcFile,
+                                                                            srcTrackId),
+                                             icPp,
+                                             MP4GetTrackEsdsObjectTypeId(srcFile,
+                                                                         srcTrackId)
+                                            );
+
+        } else if (MP4_IS_OD_TRACK_TYPE(trackType)) {
+            dstTrackId = MP4AddODTrack(dstFile);
+
+        } else if (MP4_IS_SCENE_TRACK_TYPE(trackType)) {
+            dstTrackId = MP4AddSceneTrack(dstFile);
+
+        } else if (MP4_IS_HINT_TRACK_TYPE(trackType)) {
+            if (dstHintTrackReferenceTrack == MP4_INVALID_TRACK_ID) {
+                dstTrackId = MP4_INVALID_TRACK_ID;
+            } else {
+                dstTrackId = MP4AddHintTrack(dstFile,
+                                             MP4GetHintTrackReferenceTrackId(srcFile,
+                                                                             srcTrackId));
+            }
+        } else if (MP4_IS_SYSTEMS_TRACK_TYPE(trackType)) {
+            dstTrackId = MP4AddSystemsTrack(dstFile, trackType);
+
+        } else {
+            dstTrackId = MP4AddTrack(dstFile, trackType);
+        }
+
+        if (dstTrackId == MP4_INVALID_TRACK_ID) {
+            return dstTrackId;
+        }
+
+        MP4SetTrackTimeScale(dstFile,
+                             dstTrackId,
+                             MP4GetTrackTimeScale(srcFile, srcTrackId));
+
+        if (MP4_IS_AUDIO_TRACK_TYPE(trackType)
+                || MP4_IS_VIDEO_TRACK_TYPE(trackType)) {
+            // copy track ES configuration
+            uint8_t* pConfig = NULL;
+            uint32_t configSize = 0;
+            if (MP4GetTrackESConfiguration(srcFile, srcTrackId,
+                                           &pConfig, &configSize)) {
+
+                if (pConfig != NULL) {
+                    MP4SetTrackESConfiguration(dstFile, dstTrackId,
+                                               pConfig, configSize);
+                }
+            }
+            if (pConfig != NULL)
+                free(pConfig);
+        }
+
+        // Bill's change to MP4CloneTrack
+        if (MP4_IS_HINT_TRACK_TYPE(trackType)) {
+            // probably not exactly what is wanted
+            // but caller can adjust later to fit their desires
+
+            char* payloadName = NULL;
+            char *encodingParms = NULL;
+            uint8_t payloadNumber;
+            uint16_t maxPayloadSize;
+
+            if (MP4GetHintTrackRtpPayload(
+                        srcFile,
+                        srcTrackId,
+                        &payloadName,
+                        &payloadNumber,
+                        &maxPayloadSize,
+                        &encodingParms)) {
+
+                (void)MP4SetHintTrackRtpPayload(
+                    dstFile,
+                    dstTrackId,
+                    payloadName,
+                    &payloadNumber,
+                    maxPayloadSize,
+                    encodingParms);
+            }
+#if 0
+            MP4SetHintTrackSdp(
+                dstFile,
+                dstTrackId,
+                MP4GetHintTrackSdp(srcFile, srcTrackId));
+#endif
+        }
+
+        return dstTrackId;
+    }
+
+    MP4TrackId MP4CopyTrack(MP4FileHandle srcFile,
+                            MP4TrackId srcTrackId,
+                            MP4FileHandle dstFile,
+                            bool applyEdits,
+                            MP4TrackId dstHintTrackReferenceTrack)
+    {
+        bool copySamples = true;  // LATER allow false => reference samples
+
+        MP4TrackId dstTrackId =
+            MP4CloneTrack(srcFile, srcTrackId, dstFile, dstHintTrackReferenceTrack);
+
+        if (dstTrackId == MP4_INVALID_TRACK_ID) {
+            return dstTrackId;
+        }
+
+        bool viaEdits =
+            applyEdits && MP4GetTrackNumberOfEdits(srcFile, srcTrackId);
+
+        MP4SampleId sampleId = 0;
+        MP4SampleId numSamples =
+            MP4GetTrackNumberOfSamples(srcFile, srcTrackId);
+
+        MP4Timestamp when = 0;
+        MP4Duration editsDuration =
+            MP4GetTrackEditTotalDuration(srcFile, srcTrackId);
+
+        while (true) {
+            MP4Duration sampleDuration = MP4_INVALID_DURATION;
+
+            if (viaEdits) {
+                sampleId = MP4GetSampleIdFromEditTime(
+                               srcFile,
+                               srcTrackId,
+                               when,
+                               NULL,
+                               &sampleDuration);
+
+                // in theory, this shouldn't happen
+                if (sampleId == MP4_INVALID_SAMPLE_ID) {
+                    MP4DeleteTrack(dstFile, dstTrackId);
+                    return MP4_INVALID_TRACK_ID;
+                }
+
+                when += sampleDuration;
+
+                if (when >= editsDuration) {
+                    break;
+                }
+            } else {
+                sampleId++;
+                if (sampleId > numSamples) {
+                    break;
+                }
+            }
+
+            bool rc = false;
+
+            if (copySamples) {
+                rc = MP4CopySample(
+                         srcFile,
+                         srcTrackId,
+                         sampleId,
+                         dstFile,
+                         dstTrackId,
+                         sampleDuration);
+
+            } else {
+                rc = MP4ReferenceSample(
+                         srcFile,
+                         srcTrackId,
+                         sampleId,
+                         dstFile,
+                         dstTrackId,
+                         sampleDuration);
+            }
+
+            if (!rc) {
+                MP4DeleteTrack(dstFile, dstTrackId);
+                return MP4_INVALID_TRACK_ID;
+            }
+        }
+
+        return dstTrackId;
+    }
+
+// Given a source track in a source file, make an encrypted copy of
+// the track in the destination file, including sample encryption
+    MP4TrackId MP4EncAndCopyTrack(MP4FileHandle srcFile,
+                                  MP4TrackId srcTrackId,
+                                  mp4v2_ismacrypParams *icPp,
+                                  encryptFunc_t encfcnp,
+                                  uint32_t encfcnparam1,
+                                  MP4FileHandle dstFile,
+                                  bool applyEdits,
+                                  MP4TrackId dstHintTrackReferenceTrack
+                                 )
+    {
+        bool copySamples = true;  // LATER allow false => reference samples
+
+        MP4TrackId dstTrackId =
+            MP4EncAndCloneTrack(srcFile, srcTrackId,
+                                icPp,
+                                dstFile, dstHintTrackReferenceTrack);
+
+        if (dstTrackId == MP4_INVALID_TRACK_ID) {
+            return dstTrackId;
+        }
+
+        bool viaEdits =
+            applyEdits && MP4GetTrackNumberOfEdits(srcFile, srcTrackId);
+
+        MP4SampleId sampleId = 0;
+        MP4SampleId numSamples =
+            MP4GetTrackNumberOfSamples(srcFile, srcTrackId);
+
+        MP4Timestamp when = 0;
+        MP4Duration editsDuration =
+            MP4GetTrackEditTotalDuration(srcFile, srcTrackId);
+
+        while (true) {
+            MP4Duration sampleDuration = MP4_INVALID_DURATION;
+
+            if (viaEdits) {
+                sampleId = MP4GetSampleIdFromEditTime(srcFile,
+                                                      srcTrackId,
+                                                      when,
+                                                      NULL,
+                                                      &sampleDuration);
+
+                // in theory, this shouldn't happen
+                if (sampleId == MP4_INVALID_SAMPLE_ID) {
+                    MP4DeleteTrack(dstFile, dstTrackId);
+                    return MP4_INVALID_TRACK_ID;
+                }
+
+                when += sampleDuration;
+
+                if (when >= editsDuration) {
+                    break;
+                }
+            } else {
+                sampleId++;
+                if (sampleId > numSamples) {
+                    break;
+                }
+            }
+
+            bool rc = false;
+
+            if (copySamples) {
+                // encrypt and copy
+                rc = MP4EncAndCopySample(srcFile,
+                                         srcTrackId,
+                                         sampleId,
+                                         encfcnp,
+                                         encfcnparam1,
+                                         dstFile,
+                                         dstTrackId,
+                                         sampleDuration);
+
+            } else {
+                // not sure what these are - encrypt?
+                rc = MP4ReferenceSample(srcFile,
+                                        srcTrackId,
+                                        sampleId,
+                                        dstFile,
+                                        dstTrackId,
+                                        sampleDuration);
+            }
+
+            if (!rc) {
+                MP4DeleteTrack(dstFile, dstTrackId);
+                return MP4_INVALID_TRACK_ID;
+            }
+        }
+
+        return dstTrackId;
+    }
+
+    bool MP4DeleteTrack(
+        MP4FileHandle hFile,
+        MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->DeleteTrack(trackId);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint32_t MP4GetNumberOfTracks(
+        MP4FileHandle hFile,
+        const char* type,
+        uint8_t subType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetNumberOfTracks(type, subType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    MP4TrackId MP4FindTrackId(
+        MP4FileHandle hFile,
+        uint16_t index,
+        const char* type,
+        uint8_t subType)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->FindTrackId(index, type, subType);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    uint16_t MP4FindTrackIndex(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->FindTrackIndex(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return (uint16_t)-1;
+    }
+
+    /* specific track properties */
+
+    const char* MP4GetTrackType(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackType(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+    const char* MP4GetTrackMediaDataName(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackMediaDataName(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+
+    bool MP4GetTrackMediaDataOriginalFormat(
+        MP4FileHandle hFile, MP4TrackId trackId, char *originalFormat,
+        uint32_t buflen)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+
+                return ((MP4File*)hFile)->GetTrackMediaDataOriginalFormat(trackId,
+                        originalFormat, buflen);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    MP4Duration MP4GetTrackDuration(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackDuration(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    uint32_t MP4GetTrackTimeScale(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackTimeScale(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4SetTrackTimeScale(
+        MP4FileHandle hFile, MP4TrackId trackId, uint32_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackTimeScale(trackId, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint8_t MP4GetTrackAudioMpeg4Type(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackAudioMpeg4Type(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_MPEG4_INVALID_AUDIO_TYPE;
+    }
+
+
+
+// Replacement to MP4GetTrackVideoType and MP4GetTrackAudioType
+// Basically does the same thing but with a more self-explanatory name
+    uint8_t MP4GetTrackEsdsObjectTypeId(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+
+                return ((MP4File*)hFile)->GetTrackEsdsObjectTypeId(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_AUDIO_TYPE;
+    }
+
+    MP4Duration MP4GetTrackFixedSampleDuration(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackFixedSampleDuration(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    uint32_t MP4GetTrackBitRate(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            MP4File *pFile = (MP4File *)hFile;
+            try {
+                uint32_t bitrate =  pFile->GetTrackIntegerProperty(trackId,
+                                                      "mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate");
+                if( bitrate != 0 ) {
+                    return bitrate;
+                }
+            }
+            catch( Exception* x ) {
+                //mp4v2::impl::log.errorf(*x);  we don't really need to print this.
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+            // if we're here, we can't get the bitrate from above -
+            // lets calculate it
+            try {
+                MP4Duration trackDur;
+                trackDur = MP4GetTrackDuration(hFile, trackId);
+                uint64_t msDuration =
+                    pFile->ConvertFromTrackDuration(trackId, trackDur,
+                                                    MP4_MSECS_TIME_SCALE);
+                if (msDuration == 0) return 0;
+
+                MP4Track *pTrack = pFile->GetTrack(trackId);
+                uint64_t bytes = pTrack->GetTotalOfSampleSizes();
+                bytes *= UINT64_C(8000);	// 8 * 1000
+                bytes /= msDuration;
+                return (uint32_t)bytes;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4GetTrackESConfiguration(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        uint8_t** ppConfig, uint32_t* pConfigSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->GetTrackESConfiguration(
+                    trackId, ppConfig, pConfigSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        *ppConfig = NULL;
+        *pConfigSize = 0;
+        return false;
+    }
+    bool MP4GetTrackVideoMetadata(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        uint8_t** ppConfig, uint32_t* pConfigSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->GetTrackVideoMetadata(
+                    trackId, ppConfig, pConfigSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        *ppConfig = NULL;
+        *pConfigSize = 0;
+        return false;
+    }
+
+    bool MP4SetTrackESConfiguration(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const uint8_t* pConfig, uint32_t configSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackESConfiguration(
+                    trackId, pConfig, configSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetTrackH264ProfileLevel (MP4FileHandle hFile,
+                                      MP4TrackId trackId,
+                                      uint8_t *pProfile,
+                                      uint8_t *pLevel)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *pProfile =
+                    ((MP4File *)hFile)->GetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*[0].avcC.AVCProfileIndication");
+                *pLevel =
+                    ((MP4File *)hFile)->GetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*[0].avcC.AVCLevelIndication");
+
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    void MP4FreeH264SeqPictHeaders(uint8_t** pSeqHeaders,
+                                   uint32_t* pSeqHeaderSize,
+                                   uint8_t** pPictHeader,
+                                   uint32_t* pPictHeaderSize )
+    {
+        uint32_t ix;
+
+        for (ix = 0; pSeqHeaderSize[ix] != 0; ++ix) {
+            free(pSeqHeaders[ix]);
+        }
+        free(pSeqHeaders);
+        free(pSeqHeaderSize);
+
+        for (ix = 0; pPictHeaderSize[ix] != 0; ++ix) {
+            free(pPictHeader[ix]);
+        }
+        free(pPictHeader);
+        free(pPictHeaderSize);
+    }
+
+    bool MP4GetTrackH264SeqPictHeaders (MP4FileHandle hFile,
+                                        MP4TrackId trackId,
+                                        uint8_t ***pSeqHeader,
+                                        uint32_t **pSeqHeaderSize,
+                                        uint8_t ***pPictHeader,
+                                        uint32_t **pPictHeaderSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->GetTrackH264SeqPictHeaders(trackId,
+                        pSeqHeader,
+                        pSeqHeaderSize,
+                        pPictHeader,
+                        pPictHeaderSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+    bool MP4GetTrackH264LengthSize (MP4FileHandle hFile,
+                                    MP4TrackId trackId,
+                                    uint32_t *pLength)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *pLength = 1 +
+                           ((MP4File*) hFile)->GetTrackIntegerProperty(trackId,
+                                   "mdia.minf.stbl.stsd.*[0].avcC.lengthSizeMinusOne");
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    MP4SampleId MP4GetTrackNumberOfSamples(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackNumberOfSamples(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    uint16_t MP4GetTrackVideoWidth(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackIntegerProperty(trackId,
+                        "mdia.minf.stbl.stsd.*.width");
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    uint16_t MP4GetTrackVideoHeight(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackIntegerProperty(trackId,
+                        "mdia.minf.stbl.stsd.*.height");
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    double MP4GetTrackVideoFrameRate(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackVideoFrameRate(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0.0;
+    }
+
+    int MP4GetTrackAudioChannels (MP4FileHandle hFile,
+                                  MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackAudioChannels(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return -1;
+    }
+
+// returns true if the track is a media track encrypted according to ismacryp
+    bool MP4IsIsmaCrypMediaTrack(
+        MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        bool retval = false;
+        MP4LogLevel verb = mp4v2::impl::log.verbosity;
+        mp4v2::impl::log.setVerbosity(MP4_LOG_NONE);
+
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                retval = ((MP4File*)hFile)->IsIsmaCrypMediaTrack(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        mp4v2::impl::log.setVerbosity(verb);
+        return retval;
+    }
+
+
+    /* generic track properties */
+
+    bool MP4HaveTrackAtom (MP4FileHandle hFile,
+                           MP4TrackId trackId,
+                           const char *atomName)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->FindTrackAtom(trackId, atomName) != NULL;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetTrackIntegerProperty (
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName,
+        uint64_t *retvalue)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *retvalue = ((MP4File*)hFile)->GetTrackIntegerProperty(trackId,
+                            propName);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetTrackFloatProperty(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName,
+        float *retvalue)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *retvalue = ((MP4File*)hFile)->GetTrackFloatProperty(trackId, propName);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetTrackStringProperty(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName,
+        const char **retvalue)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                *retvalue = ((MP4File*)hFile)->GetTrackStringProperty(trackId, propName);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4GetTrackBytesProperty(
+        MP4FileHandle hFile, MP4TrackId trackId, const char* propName,
+        uint8_t** ppValue, uint32_t* pValueSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->GetTrackBytesProperty(
+                    trackId, propName, ppValue, pValueSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        *ppValue = NULL;
+        *pValueSize = 0;
+        return false;
+    }
+
+    bool MP4SetTrackIntegerProperty(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName, int64_t value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackIntegerProperty(trackId,
+                        propName, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetTrackFloatProperty(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName, float value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackFloatProperty(trackId, propName, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetTrackStringProperty(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName, const char* value)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackStringProperty(trackId, propName, value);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetTrackBytesProperty(
+        MP4FileHandle hFile, MP4TrackId trackId,
+        const char* propName, const uint8_t* pValue, uint32_t valueSize)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackBytesProperty(
+                    trackId, propName, pValue, valueSize);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    /* sample operations */
+
+    bool MP4ReadSample(
+        /* input parameters */
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId,
+        /* output parameters */
+        uint8_t** ppBytes,
+        uint32_t* pNumBytes,
+        MP4Timestamp* pStartTime,
+        MP4Duration* pDuration,
+        MP4Duration* pRenderingOffset,
+        bool* pIsSyncSample)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->ReadSample(
+                    trackId,
+                    sampleId,
+                    ppBytes,
+                    pNumBytes,
+                    pStartTime,
+                    pDuration,
+                    pRenderingOffset,
+                    pIsSyncSample);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        *pNumBytes = 0;
+        return false;
+    }
+
+    bool MP4ReadSampleFromTime(
+        /* input parameters */
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4Timestamp when,
+        /* output parameters */
+        uint8_t** ppBytes,
+        uint32_t* pNumBytes,
+        MP4Timestamp* pStartTime,
+        MP4Duration* pDuration,
+        MP4Duration* pRenderingOffset,
+        bool* pIsSyncSample)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4SampleId sampleId =
+                    ((MP4File*)hFile)->GetSampleIdFromTime(
+                        trackId, when, false);
+
+                ((MP4File*)hFile)->ReadSample(
+                    trackId,
+                    sampleId,
+                    ppBytes,
+                    pNumBytes,
+                    pStartTime,
+                    pDuration,
+                    pRenderingOffset,
+                    pIsSyncSample);
+
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        *pNumBytes = 0;
+        return false;
+    }
+
+    bool MP4WriteSample(
+        MP4FileHandle  hFile,
+        MP4TrackId     trackId,
+        const uint8_t* pBytes,
+        uint32_t       numBytes,
+        MP4Duration    duration,
+        MP4Duration    renderingOffset,
+        bool           isSyncSample )
+    {
+        if( MP4_IS_VALID_FILE_HANDLE( hFile )) {
+            try {
+                ((MP4File*)hFile)->WriteSample(
+                    trackId,
+                    pBytes,
+                    numBytes,
+                    duration,
+                    renderingOffset,
+                    isSyncSample );
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4WriteSampleDependency(
+        MP4FileHandle  hFile,
+        MP4TrackId     trackId,
+        const uint8_t* pBytes,
+        uint32_t       numBytes,
+        MP4Duration    duration,
+        MP4Duration    renderingOffset,
+        bool           isSyncSample,
+        uint32_t       dependencyFlags )
+    {
+        if( MP4_IS_VALID_FILE_HANDLE( hFile )) {
+            try {
+                ((MP4File*)hFile)->WriteSampleDependency(
+                    trackId,
+                    pBytes,
+                    numBytes,
+                    duration,
+                    renderingOffset,
+                    isSyncSample,
+                    dependencyFlags );
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4CopySample(
+        MP4FileHandle srcFile,
+        MP4TrackId    srcTrackId,
+        MP4SampleId   srcSampleId,
+        MP4FileHandle dstFile,
+        MP4TrackId    dstTrackId,
+        MP4Duration   dstSampleDuration )
+    {
+        if( !MP4_IS_VALID_FILE_HANDLE( srcFile ))
+            return false;
+
+        try {
+            MP4File::CopySample(
+                (MP4File*)srcFile,
+                srcTrackId,
+                srcSampleId,
+                (MP4File*)dstFile,
+                dstTrackId,
+                dstSampleDuration );
+            return true;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+        }
+
+        return false;
+    }
+
+    bool MP4EncAndCopySample(
+        MP4FileHandle srcFile,
+        MP4TrackId    srcTrackId,
+        MP4SampleId   srcSampleId,
+        encryptFunc_t encfcnp,
+        uint32_t      encfcnparam1,
+        MP4FileHandle dstFile,
+        MP4TrackId    dstTrackId,
+        MP4Duration   dstSampleDuration)
+    {
+        if( !MP4_IS_VALID_FILE_HANDLE( srcFile ))
+            return false;
+
+        try {
+            MP4File::EncAndCopySample(
+                (MP4File*)srcFile,
+                srcTrackId,
+                srcSampleId,
+                encfcnp,
+                encfcnparam1,
+                (MP4File*)dstFile,
+                dstTrackId,
+                dstSampleDuration );
+            return true;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+        }
+
+        return false;
+    }
+
+    bool MP4ReferenceSample(
+        MP4FileHandle srcFile,
+        MP4TrackId srcTrackId,
+        MP4SampleId srcSampleId,
+        MP4FileHandle dstFile,
+        MP4TrackId dstTrackId,
+        MP4Duration dstSampleDuration)
+    {
+        // LATER Not yet implemented
+        return false;
+    }
+
+    uint32_t MP4GetSampleSize(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleSize(
+                           trackId, sampleId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    uint32_t MP4GetTrackMaxSampleSize(
+        MP4FileHandle hFile,
+        MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackMaxSampleSize(trackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    MP4SampleId MP4GetSampleIdFromTime(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4Timestamp when,
+        bool wantSyncSample)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleIdFromTime(
+                           trackId, when, wantSyncSample);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_SAMPLE_ID;
+    }
+
+    MP4Timestamp MP4GetSampleTime(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleTime(
+                           trackId, sampleId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TIMESTAMP;
+    }
+
+    MP4Duration MP4GetSampleDuration(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleDuration(
+                           trackId, sampleId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    MP4Duration MP4GetSampleRenderingOffset(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleRenderingOffset(
+                           trackId, sampleId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    bool MP4SetSampleRenderingOffset(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId,
+        MP4Duration renderingOffset)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetSampleRenderingOffset(
+                    trackId, sampleId, renderingOffset);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    int8_t MP4GetSampleSync(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4SampleId sampleId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleSync(
+                           trackId, sampleId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return -1;
+    }
+
+
+    uint64_t MP4ConvertFromMovieDuration(
+        MP4FileHandle hFile,
+        MP4Duration duration,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->ConvertFromMovieDuration(
+                           duration, timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return (uint64_t)MP4_INVALID_DURATION;
+    }
+
+    uint64_t MP4ConvertFromTrackTimestamp(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4Timestamp timeStamp,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->ConvertFromTrackTimestamp(
+                           trackId, timeStamp, timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return (uint64_t)MP4_INVALID_TIMESTAMP;
+    }
+
+    MP4Timestamp MP4ConvertToTrackTimestamp(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint64_t timeStamp,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->ConvertToTrackTimestamp(
+                           trackId, timeStamp, timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TIMESTAMP;
+    }
+
+    uint64_t MP4ConvertFromTrackDuration(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4Duration duration,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->ConvertFromTrackDuration(
+                           trackId, duration, timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return (uint64_t)MP4_INVALID_DURATION;
+    }
+
+    MP4Duration MP4ConvertToTrackDuration(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        uint64_t duration,
+        uint32_t timeScale)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->ConvertToTrackDuration(
+                           trackId, duration, timeScale);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    bool MP4GetHintTrackRtpPayload(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        char** ppPayloadName,
+        uint8_t* pPayloadNumber,
+        uint16_t* pMaxPayloadSize,
+        char **ppEncodingParams)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->GetHintTrackRtpPayload(
+                    hintTrackId, ppPayloadName, pPayloadNumber, pMaxPayloadSize,
+                    ppEncodingParams);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4SetHintTrackRtpPayload(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        const char* pPayloadName,
+        uint8_t* pPayloadNumber,
+        uint16_t maxPayloadSize,
+        const char *encode_params,
+        bool include_rtp_map,
+        bool include_mpeg4_esid)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetHintTrackRtpPayload(
+                    hintTrackId, pPayloadName, pPayloadNumber, maxPayloadSize, encode_params,
+                    include_rtp_map, include_mpeg4_esid);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    const char* MP4GetSessionSdp(
+        MP4FileHandle hFile)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSessionSdp();
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+
+    bool MP4SetSessionSdp(
+        MP4FileHandle hFile,
+        const char* sdpString)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetSessionSdp(sdpString);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AppendSessionSdp(
+        MP4FileHandle hFile,
+        const char* sdpString)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AppendSessionSdp(sdpString);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    const char* MP4GetHintTrackSdp(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetHintTrackSdp(hintTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+
+    bool MP4SetHintTrackSdp(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        const char* sdpString)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetHintTrackSdp(hintTrackId, sdpString);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AppendHintTrackSdp(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        const char* sdpString)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AppendHintTrackSdp(hintTrackId, sdpString);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    MP4TrackId MP4GetHintTrackReferenceTrackId(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       GetHintTrackReferenceTrackId(hintTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    bool MP4ReadRtpHint(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        MP4SampleId hintSampleId,
+        uint16_t* pNumPackets)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->ReadRtpHint(
+                    hintTrackId, hintSampleId, pNumPackets);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint16_t MP4GetRtpHintNumberOfPackets(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetRtpHintNumberOfPackets(hintTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    int8_t MP4GetRtpPacketBFrame(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        uint16_t packetIndex)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       GetRtpPacketBFrame(hintTrackId, packetIndex);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return -1;
+    }
+
+    int32_t MP4GetRtpPacketTransmitOffset(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        uint16_t packetIndex)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->
+                       GetRtpPacketTransmitOffset(hintTrackId, packetIndex);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    bool MP4ReadRtpPacket(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        uint16_t packetIndex,
+        uint8_t** ppBytes,
+        uint32_t* pNumBytes,
+        uint32_t ssrc,
+        bool includeHeader,
+        bool includePayload)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->ReadRtpPacket(
+                    hintTrackId, packetIndex,
+                    ppBytes, pNumBytes,
+                    ssrc, includeHeader, includePayload);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    MP4Timestamp MP4GetRtpTimestampStart(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetRtpTimestampStart(hintTrackId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TIMESTAMP;
+    }
+
+    bool MP4SetRtpTimestampStart(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        MP4Timestamp rtpStart)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetRtpTimestampStart(
+                    hintTrackId, rtpStart);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AddRtpHint(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId)
+    {
+        return MP4AddRtpVideoHint(hFile, hintTrackId, false, 0);
+    }
+
+    bool MP4AddRtpVideoHint(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        bool isBframe,
+        uint32_t timestampOffset)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddRtpHint(hintTrackId,
+                                              isBframe, timestampOffset);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AddRtpPacket(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        bool setMbit,
+        int32_t transmitOffset)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddRtpPacket(
+                    hintTrackId, setMbit, transmitOffset);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AddRtpImmediateData(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        const uint8_t* pBytes,
+        uint32_t numBytes)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddRtpImmediateData(hintTrackId,
+                                                       pBytes, numBytes);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AddRtpSampleData(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        MP4SampleId sampleId,
+        uint32_t dataOffset,
+        uint32_t dataLength)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddRtpSampleData(
+                    hintTrackId, sampleId, dataOffset, dataLength);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4AddRtpESConfigurationPacket(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->AddRtpESConfigurationPacket(hintTrackId);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4WriteRtpHint(
+        MP4FileHandle hFile,
+        MP4TrackId hintTrackId,
+        MP4Duration duration,
+        bool isSyncSample)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->WriteRtpHint(
+                    hintTrackId, duration, isSyncSample);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf( "%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+    /* 3GPP specific operations */
+
+    bool MP4Make3GPCompliant(
+        const char* fileName,
+        char* majorBrand,
+        uint32_t minorVersion,
+        char** supportedBrands,
+        uint32_t supportedBrandsCount,
+        bool deleteIodsAtom)
+    {
+        if (!fileName)
+            return false;
+
+        MP4File* pFile = ConstructMP4File();
+        if (!pFile)
+            return MP4_INVALID_FILE_HANDLE;
+
+        try {
+            ASSERT(pFile);
+            pFile->Modify(fileName);
+            pFile->Make3GPCompliant(fileName, majorBrand, minorVersion, supportedBrands, supportedBrandsCount, deleteIodsAtom);
+            pFile->Close();
+            delete pFile;
+            return true;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+        }
+
+        if (pFile)
+            delete pFile;
+        return false;
+    }
+
+    /* ISMA specific operations */
+
+    bool MP4MakeIsmaCompliant(
+        const char* fileName,
+        bool addIsmaComplianceSdp)
+    {
+        if (!fileName)
+            return false;
+
+        MP4File* pFile = ConstructMP4File();
+        if (!pFile)
+            return MP4_INVALID_FILE_HANDLE;
+
+        try {
+            ASSERT(pFile);
+            pFile->Modify(fileName);
+            pFile->MakeIsmaCompliant(addIsmaComplianceSdp);
+            pFile->Close();
+            delete pFile;
+            return true;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: \"%s\": failed", __FUNCTION__,
+                                    fileName );
+        }
+
+        if (pFile)
+            delete pFile;
+        return false;
+    }
+
+    char* MP4MakeIsmaSdpIod(
+        uint8_t videoProfile,
+        uint32_t videoBitrate,
+        uint8_t* videoConfig,
+        uint32_t videoConfigLength,
+        uint8_t audioProfile,
+        uint32_t audioBitrate,
+        uint8_t* audioConfig,
+        uint32_t audioConfigLength)
+
+    {
+        MP4File* pFile = ConstructMP4File();
+        if (!pFile)
+            return NULL;
+
+        try {
+            uint8_t* pBytes = NULL;
+            uint64_t numBytes = 0;
+
+            ASSERT(pFile);
+            pFile->CreateIsmaIodFromParams(
+                videoProfile,
+                videoBitrate,
+                videoConfig,
+                videoConfigLength,
+                audioProfile,
+                audioBitrate,
+                audioConfig,
+                audioConfigLength,
+                &pBytes,
+                &numBytes);
+
+            char* iodBase64 =
+                MP4ToBase64(pBytes, numBytes);
+            MP4Free(pBytes);
+
+            char* sdpIod =
+                (char*)MP4Malloc(strlen(iodBase64) + 64);
+            snprintf(sdpIod, strlen(iodBase64) + 64,
+                     "a=mpeg4-iod: \042data:application/mpeg4-iod;base64,%s\042",
+                     iodBase64);
+            MP4Free(iodBase64);
+
+            delete pFile;
+
+            return sdpIod;
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+        }
+
+        if (pFile)
+            delete pFile;
+        return NULL;
+    }
+
+    /* Edit list */
+
+    MP4EditId MP4AddTrackEdit(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId,
+        MP4Timestamp startTime,
+        MP4Duration duration,
+        bool dwell)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                MP4EditId newEditId =
+                    ((MP4File*)hFile)->AddTrackEdit(trackId, editId);
+
+                if (newEditId != MP4_INVALID_EDIT_ID) {
+                    ((MP4File*)hFile)->SetTrackEditMediaStart(
+                        trackId, newEditId, startTime);
+                    ((MP4File*)hFile)->SetTrackEditDuration(
+                        trackId, newEditId, duration);
+                    ((MP4File*)hFile)->SetTrackEditDwell(
+                        trackId, newEditId, dwell);
+                }
+
+                return newEditId;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_EDIT_ID;
+    }
+
+    bool MP4DeleteTrackEdit(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->DeleteTrackEdit(trackId, editId);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    uint32_t MP4GetTrackNumberOfEdits(
+        MP4FileHandle hFile,
+        MP4TrackId trackId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackNumberOfEdits(trackId);
+            }
+            catch( Exception* x ) {
+                //mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return 0;
+    }
+
+    MP4Timestamp MP4GetTrackEditMediaStart(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackEditMediaStart(
+                           trackId, editId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_TIMESTAMP;
+    }
+
+    MP4Duration MP4GetTrackEditTotalDuration(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackEditTotalDuration(
+                           trackId, editId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    bool MP4SetTrackEditMediaStart(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId,
+        MP4Timestamp startTime)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackEditMediaStart(
+                    trackId, editId, startTime);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    MP4Duration MP4GetTrackEditDuration(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackEditDuration(trackId, editId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_DURATION;
+    }
+
+    bool MP4SetTrackEditDuration(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId,
+        MP4Duration duration)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackEditDuration(trackId, editId, duration);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    int8_t MP4GetTrackEditDwell(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetTrackEditDwell(trackId, editId);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return -1;
+    }
+
+    bool MP4SetTrackEditDwell(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4EditId editId,
+        bool dwell)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                ((MP4File*)hFile)->SetTrackEditDwell(trackId, editId, dwell);
+                return true;
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return false;
+    }
+
+    bool MP4ReadSampleFromEditTime(
+        /* input parameters */
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4Timestamp when,
+        /* output parameters */
+        uint8_t** ppBytes,
+        uint32_t* pNumBytes,
+        MP4Timestamp* pStartTime,
+        MP4Duration* pDuration,
+        MP4Duration* pRenderingOffset,
+        bool* pIsSyncSample)
+    {
+        MP4SampleId sampleId =
+            MP4GetSampleIdFromEditTime(
+                hFile,
+                trackId,
+                when,
+                pStartTime,
+                pDuration);
+
+        return MP4ReadSample(
+                   hFile,
+                   trackId,
+                   sampleId,
+                   ppBytes,
+                   pNumBytes,
+                   NULL,
+                   NULL,
+                   pRenderingOffset,
+                   pIsSyncSample);
+    }
+
+    MP4SampleId MP4GetSampleIdFromEditTime(
+        MP4FileHandle hFile,
+        MP4TrackId trackId,
+        MP4Timestamp when,
+        MP4Timestamp* pStartTime,
+        MP4Duration* pDuration)
+    {
+        if (MP4_IS_VALID_FILE_HANDLE(hFile)) {
+            try {
+                return ((MP4File*)hFile)->GetSampleIdFromEditTime(
+                           trackId, when, pStartTime, pDuration);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return MP4_INVALID_SAMPLE_ID;
+    }
+
+    /* Utlities */
+
+    char* MP4BinaryToBase16(
+        const uint8_t* pData,
+        uint32_t dataSize)
+    {
+        if (pData || dataSize == 0) {
+            try {
+                return MP4ToBase16(pData, dataSize);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+
+    char* MP4BinaryToBase64(
+        const uint8_t* pData,
+        uint32_t dataSize)
+    {
+        if (pData || dataSize == 0) {
+            try {
+                return MP4ToBase64(pData, dataSize);
+            }
+            catch( Exception* x ) {
+                mp4v2::impl::log.errorf(*x);
+                delete x;
+            }
+            catch( ... ) {
+                mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            }
+        }
+        return NULL;
+    }
+
+    void MP4Free (void *p)
+    {
+        if (p != NULL)
+            free(p);
+    }
+
+    bool MP4AddIPodUUID (MP4FileHandle hFile, MP4TrackId trackId)
+    {
+        if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+            return false;
+
+        MP4Track* track = NULL;
+        MP4Atom* avc1 = NULL;
+
+        try
+        {
+            track = ((MP4File*)hFile)->GetTrack(trackId);
+            ASSERT(track);
+            avc1 = track->GetTrakAtom().FindChildAtom("mdia.minf.stbl.stsd.avc1");
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+            return false;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+            return false;
+        }
+
+        IPodUUIDAtom    *ipod_uuid = NULL;
+        try
+        {
+            ipod_uuid = new IPodUUIDAtom(*(MP4File*)hFile);
+        }
+        catch( std::bad_alloc ) {
+            mp4v2::impl::log.errorf("%s: unable to allocate IPodUUIDAtom", __FUNCTION__);
+        }
+        catch( Exception* x ) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+            return false;
+        }
+        catch( ... ) {
+            mp4v2::impl::log.errorf("%s: unknown exception constructing IPodUUIDAtom", __FUNCTION__ );
+            return false;
+        }
+
+        try
+        {
+            ASSERT(avc1);
+            ASSERT(ipod_uuid);
+            avc1->AddChildAtom(ipod_uuid);
+            return true;
+        }
+        catch( Exception* x ) {
+            delete ipod_uuid;
+            ipod_uuid = NULL;
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+            return false;
+        }
+        catch( ... ) {
+            delete ipod_uuid;
+            ipod_uuid = NULL;
+            mp4v2::impl::log.errorf("%s: unknown exception adding IPodUUIDAtom", __FUNCTION__ );
+            return false;
+        }
+
+        return false;
+    }
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4GetTrackLanguage(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    char*         code )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return ((MP4File*)hFile)->GetTrackLanguage( trackId, code );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4SetTrackLanguage(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   code )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return ((MP4File*)hFile)->SetTrackLanguage( trackId, code );
+    }   
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4GetTrackName(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    char**        name )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return ((MP4File*)hFile)->GetTrackName( trackId, name );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4SetTrackName(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    const char*   code )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        return ((MP4File*)hFile)->SetTrackName( trackId, code );
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4GetTrackDurationPerChunk(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId, 
+    MP4Duration*  duration )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    if (!duration)
+        return false;
+
+    try {
+        *duration = ((MP4File*)hFile)->GetTrackDurationPerChunk( trackId );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4SetTrackDurationPerChunk(
+    MP4FileHandle hFile,
+    MP4TrackId    trackId,
+    MP4Duration   duration )
+{
+    if( !MP4_IS_VALID_FILE_HANDLE( hFile ))
+        return false;
+
+    try {
+        ((MP4File*)hFile)->SetTrackDurationPerChunk( trackId, duration );
+        return true;
+    }
+    catch( Exception* x ) {
+        mp4v2::impl::log.errorf(*x);
+        delete x;
+    }
+    catch( ... ) {
+        mp4v2::impl::log.errorf("%s: failed", __FUNCTION__ );
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // extern "C"
diff --git a/src/mp4array.h b/src/mp4array.h
new file mode 100644
index 0000000..c49d59b
--- /dev/null
+++ b/src/mp4array.h
@@ -0,0 +1,145 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_MP4ARRAY_H
+#define MP4V2_IMPL_MP4ARRAY_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint32_t MP4ArrayIndex;
+
+class MP4Array {
+public:
+    MP4Array() {
+        m_numElements = 0;
+        m_maxNumElements = 0;
+    }
+
+    inline bool ValidIndex(MP4ArrayIndex index) {
+        return (index < m_numElements);
+    }
+
+    inline MP4ArrayIndex Size(void) {
+        return m_numElements;
+    }
+
+    inline MP4ArrayIndex MaxSize(void) {
+        return m_maxNumElements;
+    }
+
+protected:
+    MP4ArrayIndex   m_numElements;
+    MP4ArrayIndex   m_maxNumElements;
+};
+
+// macro to generate subclasses
+// we use this as an alternative to templates
+// due to the excessive compile time price of extensive template usage
+
+#define MP4ARRAY_DECL(name, type) \
+    class name##Array : public MP4Array { \
+    public: \
+        name##Array() { \
+            m_elements = NULL; \
+        } \
+        \
+        ~name##Array() { \
+            MP4Free(m_elements); \
+        } \
+        \
+        inline void Add(type newElement) { \
+            Insert(newElement, m_numElements); \
+        } \
+        \
+        void Insert(type newElement, MP4ArrayIndex newIndex) { \
+            if (newIndex > m_numElements) { \
+                  throw new PlatformException("illegal array index", ERANGE, __FILE__, __LINE__, __FUNCTION__); \
+            } \
+            if (m_numElements == m_maxNumElements) { \
+                m_maxNumElements = max(m_maxNumElements, (MP4ArrayIndex)1) * 2; \
+                m_elements = (type*)MP4Realloc(m_elements, \
+                    m_maxNumElements * sizeof(type)); \
+            } \
+            memmove(&m_elements[newIndex + 1], &m_elements[newIndex], \
+                (m_numElements - newIndex) * sizeof(type)); \
+            m_elements[newIndex] = newElement; \
+            m_numElements++; \
+        } \
+        \
+        void Delete(MP4ArrayIndex index) { \
+            if (!ValidIndex(index)) { \
+                ostringstream msg; \
+                msg << "illegal array index: " << index << " of " << m_numElements; \
+                throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__); \
+            } \
+            m_numElements--; \
+            if (index < m_numElements) { \
+              memmove(&m_elements[index], &m_elements[index + 1], \
+                  (m_numElements - index) * sizeof(type)); \
+            } \
+        } \
+        void Resize(MP4ArrayIndex newSize) { \
+            m_numElements = newSize; \
+            m_maxNumElements = newSize; \
+            m_elements = (type*)MP4Realloc(m_elements, \
+                m_maxNumElements * sizeof(type)); \
+        } \
+        \
+        type& operator[](MP4ArrayIndex index) { \
+            if (ValidIndex(index)) { \
+                return m_elements[index]; \
+            } \
+            else { \
+                ostringstream msg; \
+                msg << "illegal array index: " << index << " of " << m_numElements; \
+                throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__ ); \
+            } \
+        } \
+        \
+    protected: \
+        type*   m_elements; \
+    };
+
+MP4ARRAY_DECL(MP4Integer8, uint8_t)
+
+MP4ARRAY_DECL(MP4Integer16, uint16_t)
+
+MP4ARRAY_DECL(MP4Integer32, uint32_t)
+
+MP4ARRAY_DECL(MP4Integer64, uint64_t)
+
+MP4ARRAY_DECL(MP4Float32, float)
+
+MP4ARRAY_DECL(MP4Float64, double)
+
+MP4ARRAY_DECL(MP4String, char*)
+
+MP4ARRAY_DECL(MP4Bytes, uint8_t*)
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4ARRAY_H
diff --git a/src/mp4atom.cpp b/src/mp4atom.cpp
new file mode 100644
index 0000000..520cbc8
--- /dev/null
+++ b/src/mp4atom.cpp
@@ -0,0 +1,1017 @@
+/*
+ * 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<string> 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<string>::iterator ie = tlist.end();
+        for( list<string>::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\": <table entries suppressed>",
+                     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
diff --git a/src/mp4atom.h b/src/mp4atom.h
new file mode 100644
index 0000000..b504c75
--- /dev/null
+++ b/src/mp4atom.h
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ *              Ximpo Group Ltd.        mp4v2@ximpo.com
+ */
+
+#ifndef MP4V2_IMPL_MP4ATOM_H
+#define MP4V2_IMPL_MP4ATOM_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4Atom;
+MP4ARRAY_DECL(MP4Atom, MP4Atom*);
+
+#define Required    true
+#define Optional    false
+#define OnlyOne     true
+#define Many        false
+#define Counted     true
+
+/* helper class */
+class MP4AtomInfo {
+public:
+    MP4AtomInfo() {
+        m_name = NULL;
+    }
+    MP4AtomInfo(const char* name, bool mandatory, bool onlyOne);
+
+    const char* m_name;
+    bool m_mandatory;
+    bool m_onlyOne;
+    uint32_t m_count;
+};
+
+MP4ARRAY_DECL(MP4AtomInfo, MP4AtomInfo*);
+
+class MP4Atom
+{
+public:
+    static MP4Atom* ReadAtom( MP4File& file, MP4Atom* pParentAtom );
+    static MP4Atom* CreateAtom( MP4File& file, MP4Atom* parent, const char* type );
+    static bool IsReasonableType( const char* type );
+
+private:
+    static MP4Atom* factory( MP4File &file, MP4Atom* parent, const char* type );
+    static bool descendsFrom( MP4Atom* parent, const char* type );
+
+public:
+    MP4Atom(MP4File& file, const char* type = NULL);
+    virtual ~MP4Atom();
+
+    MP4File& GetFile() {
+        return m_File;
+    };
+
+    uint64_t GetStart() {
+        return m_start;
+    };
+    void SetStart(uint64_t pos) {
+        m_start = pos;
+    };
+
+    uint64_t GetEnd() {
+        return m_end;
+    };
+    void SetEnd(uint64_t pos) {
+        m_end = pos;
+    };
+
+    uint64_t GetSize() {
+        return m_size;
+    }
+    void SetSize(uint64_t size) {
+        m_size = size;
+    }
+
+    const char* GetType() {
+        return m_type;
+    };
+    void SetType(const char* type) {
+        if (type && *type != '\0') {
+            // not needed ASSERT(strlen(type) == 4);
+            memcpy(m_type, type, 4);
+            m_type[4] = '\0';
+        } else {
+            memset(m_type, 0, 5);
+        }
+    }
+
+    void GetExtendedType(uint8_t* pExtendedType) {
+        memcpy(pExtendedType, m_extendedType, sizeof(m_extendedType));
+    };
+    void SetExtendedType(uint8_t* pExtendedType) {
+        memcpy(m_extendedType, pExtendedType, sizeof(m_extendedType));
+    };
+
+    bool IsUnknownType() {
+        return m_unknownType;
+    }
+    void SetUnknownType(bool unknownType = true) {
+        m_unknownType = unknownType;
+    }
+
+    bool IsRootAtom() {
+        return m_type[0] == '\0';
+    }
+
+    MP4Atom* GetParentAtom() {
+        return m_pParentAtom;
+    }
+    void SetParentAtom(MP4Atom* pParentAtom) {
+        m_pParentAtom = pParentAtom;
+    }
+
+    void AddChildAtom(MP4Atom* pChildAtom) {
+        pChildAtom->SetParentAtom(this);
+        m_pChildAtoms.Add(pChildAtom);
+    }
+
+    void InsertChildAtom(MP4Atom* pChildAtom, uint32_t index) {
+        pChildAtom->SetParentAtom(this);
+        m_pChildAtoms.Insert(pChildAtom, index);
+    }
+
+    void DeleteChildAtom(MP4Atom* pChildAtom) {
+        for (MP4ArrayIndex i = 0; i < m_pChildAtoms.Size(); i++) {
+            if (m_pChildAtoms[i] == pChildAtom) {
+                m_pChildAtoms.Delete(i);
+                return;
+            }
+        }
+    }
+
+    uint32_t GetNumberOfChildAtoms() {
+        return m_pChildAtoms.Size();
+    }
+
+    MP4Atom* GetChildAtom(uint32_t index) {
+        return m_pChildAtoms[index];
+    }
+
+    MP4Property* GetProperty(uint32_t index) {
+        return m_pProperties[index];
+    }
+
+    uint32_t GetCount() {
+        return m_pProperties.Size();
+    }
+
+    MP4Atom* FindAtom(const char* name);
+
+    MP4Atom* FindChildAtom(const char* name);
+
+    bool FindProperty(const char* name,
+                      MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    uint32_t GetFlags();
+    void SetFlags(uint32_t flags);
+
+    uint8_t GetDepth();
+
+    void Skip();
+
+    virtual void Generate();
+    virtual void Read();
+    virtual void BeginWrite(bool use64 = false);
+    virtual void Write();
+    virtual void Rewrite();
+    virtual void FinishWrite(bool use64 = false);
+    virtual void Dump(uint8_t indent, bool dumpImplicits);
+
+    bool GetLargesizeMode();
+
+protected:
+    void AddProperty(MP4Property* pProperty);
+
+    void AddVersionAndFlags();
+
+    void AddReserved(MP4Atom& parentAtom, const char* name, uint32_t size);
+
+    void ExpectChildAtom(const char* name,
+                         bool mandatory, bool onlyOne = true);
+
+    MP4AtomInfo* FindAtomInfo(const char* name);
+
+    bool IsMe(const char* name);
+
+    bool FindContainedProperty(const char* name,
+                               MP4Property** ppProperty, uint32_t* pIndex);
+
+    void ReadProperties(
+        uint32_t startIndex = 0, uint32_t count = 0xFFFFFFFF);
+    void ReadChildAtoms();
+
+    void WriteProperties(
+        uint32_t startIndex = 0, uint32_t count = 0xFFFFFFFF);
+    void WriteChildAtoms();
+
+    uint8_t GetVersion();
+    void SetVersion(uint8_t version);
+
+    void SetLargesizeMode( bool );
+
+protected:
+    MP4File&    m_File;
+    uint64_t    m_start;
+    uint64_t    m_end;
+    bool        m_largesizeMode; // true if largesize mode
+    uint64_t    m_size;
+    char        m_type[5];
+    bool        m_unknownType;
+    uint8_t m_extendedType[16];
+
+    MP4Atom*    m_pParentAtom;
+    uint8_t m_depth;
+
+    MP4PropertyArray    m_pProperties;
+    MP4AtomInfoArray    m_pChildAtomInfos;
+    MP4AtomArray        m_pChildAtoms;
+private:
+    MP4Atom();
+    MP4Atom( const MP4Atom &src );
+    MP4Atom &operator= ( const MP4Atom &src );
+};
+
+inline uint32_t ATOMID(const char* type) {
+    return STRTOINT32(type);
+}
+
+// inverse ATOMID - 32 bit id to string
+inline void IDATOM(uint32_t type, char *s) {
+    INT32TOSTR(type, s);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4ATOM_H
diff --git a/src/mp4container.cpp b/src/mp4container.cpp
new file mode 100644
index 0000000..acfbd29
--- /dev/null
+++ b/src/mp4container.cpp
@@ -0,0 +1,228 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Container::~MP4Container()
+{
+    for (uint32_t i = 0; i < m_pProperties.Size(); i++) {
+        delete m_pProperties[i];
+    }
+}
+
+void MP4Container::AddProperty(MP4Property* pProperty)
+{
+    ASSERT(pProperty);
+    m_pProperties.Add(pProperty);
+}
+
+bool MP4Container::FindProperty(const char *name,
+                                MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (pIndex) {
+        *pIndex = 0;    // set the default answer for index
+    }
+
+    uint32_t numProperties = m_pProperties.Size();
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void MP4Container::FindIntegerProperty(const char* name,
+                                       MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        throw new Exception("no such property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    switch ((*ppProperty)->GetType()) {
+    case Integer8Property:
+    case Integer16Property:
+    case Integer24Property:
+    case Integer32Property:
+    case Integer64Property:
+        break;
+    default:
+        throw new Exception("type mismatch", __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+uint64_t MP4Container::GetIntegerProperty(const char* name)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindIntegerProperty(name, &pProperty, &index);
+
+    return ((MP4IntegerProperty*)pProperty)->GetValue(index);
+}
+
+void MP4Container::SetIntegerProperty(const char* name, uint64_t value)
+{
+    MP4Property* pProperty = NULL;
+    uint32_t index = 0;
+
+    FindIntegerProperty(name, &pProperty, &index);
+
+    ((MP4IntegerProperty*)pProperty)->SetValue(value, index);
+}
+
+void MP4Container::FindFloatProperty(const char* name,
+                                     MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        throw new Exception("no such property", __FILE__, __LINE__, __FUNCTION__);
+    }
+    if ((*ppProperty)->GetType() != Float32Property) {
+        throw new Exception("type mismatch", __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+float MP4Container::GetFloatProperty(const char* name)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindFloatProperty(name, &pProperty, &index);
+
+    return ((MP4Float32Property*)pProperty)->GetValue(index);
+}
+
+void MP4Container::SetFloatProperty(const char* name, float value)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindFloatProperty(name, &pProperty, &index);
+
+    ((MP4Float32Property*)pProperty)->SetValue(value, index);
+}
+
+void MP4Container::FindStringProperty(const char* name,
+                                      MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        throw new Exception("no such property", __FILE__, __LINE__, __FUNCTION__);
+    }
+    if ((*ppProperty)->GetType() != StringProperty) {
+        throw new Exception("type mismatch", __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+const char* MP4Container::GetStringProperty(const char* name)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindStringProperty(name, &pProperty, &index);
+
+    return ((MP4StringProperty*)pProperty)->GetValue(index);
+}
+
+void MP4Container::SetStringProperty(const char* name, const char* value)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindStringProperty(name, &pProperty, &index);
+
+    ((MP4StringProperty*)pProperty)->SetValue(value, index);
+}
+
+void MP4Container::FindBytesProperty(const char* name,
+                                     MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        throw new Exception("no such property", __FILE__, __LINE__, __FUNCTION__);
+    }
+    if ((*ppProperty)->GetType() != BytesProperty) {
+        throw new Exception("type mismatch", __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+void MP4Container::GetBytesProperty(const char* name,
+                                    uint8_t** ppValue, uint32_t* pValueSize)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindBytesProperty(name, &pProperty, &index);
+
+    ((MP4BytesProperty*)pProperty)->GetValue(ppValue, pValueSize, index);
+}
+
+void MP4Container::SetBytesProperty(const char* name,
+                                    const uint8_t* pValue, uint32_t valueSize)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindBytesProperty(name, &pProperty, &index);
+
+    ((MP4BytesProperty*)pProperty)->SetValue(pValue, valueSize, index);
+}
+
+void MP4Container::Read(MP4File& file)
+{
+    uint32_t numProperties = m_pProperties.Size();
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        m_pProperties[i]->Read(file);
+    }
+}
+
+void MP4Container::Write(MP4File& file)
+{
+    uint32_t numProperties = m_pProperties.Size();
+
+    if (numProperties == 0) {
+        WARNING(numProperties == 0);
+        return;
+    }
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        m_pProperties[i]->Write(file);
+    }
+}
+
+void MP4Container::Dump(uint8_t indent, bool dumpImplicits)
+{
+    uint32_t numProperties = m_pProperties.Size();
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        m_pProperties[i]->Dump(indent, dumpImplicits);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/mp4container.h b/src/mp4container.h
new file mode 100644
index 0000000..100d94f
--- /dev/null
+++ b/src/mp4container.h
@@ -0,0 +1,94 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_MP4CONTAINER_H
+#define MP4V2_IMPL_MP4CONTAINER_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// base class - container of mp4 properties
+class MP4Container {
+public:
+    MP4Container() { }
+
+    virtual ~MP4Container();
+
+    void AddProperty(MP4Property* pProperty);
+
+    virtual void Read(MP4File& file);
+
+    virtual void Write(MP4File& file);
+
+    virtual void Dump(uint8_t indent, bool dumpImplicits);
+
+    MP4Property* GetProperty(uint32_t index) {
+        return m_pProperties[index];
+    }
+
+    // LATER MP4Property* GetProperty(const char* name); throw on error
+    // LATER MP4Property* FindProperty(const char* name, uint32_t* pIndex = NULL); returns NULL on error
+
+    bool FindProperty(const char* name,
+                      MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    void FindIntegerProperty(const char* name,
+                             MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    uint64_t GetIntegerProperty(const char* name);
+
+    void SetIntegerProperty(const char* name, uint64_t value);
+
+    void FindFloatProperty(const char* name,
+                           MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    float GetFloatProperty(const char* name);
+
+    void SetFloatProperty(const char* name, float value);
+
+    void FindStringProperty(const char* name,
+                            MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    const char* GetStringProperty(const char* name);
+
+    void SetStringProperty(const char* name, const char* value);
+
+    void FindBytesProperty(const char* name,
+                           MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    void GetBytesProperty(const char* name,
+                          uint8_t** ppValue, uint32_t* pValueSize);
+
+    void SetBytesProperty(const char* name,
+                          const uint8_t* pValue, uint32_t valueSize);
+
+protected:
+    MP4PropertyArray    m_pProperties;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4CONTAINER_H
diff --git a/src/mp4descriptor.cpp b/src/mp4descriptor.cpp
new file mode 100644
index 0000000..202deb1
--- /dev/null
+++ b/src/mp4descriptor.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Descriptor::MP4Descriptor(MP4Atom& parentAtom, uint8_t tag)
+    : m_parentAtom(parentAtom)
+{
+    m_tag = tag;
+    m_start = 0;
+    m_size = 0;
+    m_readMutatePoint = 0;
+}
+
+MP4Descriptor::~MP4Descriptor()
+{
+    for (uint32_t i = 0; i < m_pProperties.Size(); i++) {
+        delete m_pProperties[i];
+    }
+}
+
+void MP4Descriptor::AddProperty(MP4Property* pProperty)
+{
+    ASSERT(pProperty);
+    m_pProperties.Add(pProperty);
+}
+
+bool MP4Descriptor::FindContainedProperty(const char *name,
+        MP4Property** ppProperty, uint32_t* pIndex)
+{
+    uint32_t numProperties = m_pProperties.Size();
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void MP4Descriptor::Generate()
+{
+    // generate properties
+    for (uint32_t i = 0; i < m_pProperties.Size(); i++) {
+        m_pProperties[i]->Generate();
+    }
+}
+
+void MP4Descriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    ReadProperties(file, 0, m_readMutatePoint);
+
+    Mutate();
+
+    ReadProperties(file, m_readMutatePoint);
+
+    // flush any leftover read bits
+    file.FlushReadBits();
+}
+
+void MP4Descriptor::ReadHeader(MP4File& file)
+{
+    log.verbose1f("\"%s\": ReadDescriptor: pos = 0x%" PRIx64, file.GetFilename().c_str(),
+                  file.GetPosition());
+
+    // read tag and length
+    uint8_t tag = file.ReadUInt8();
+    if (m_tag) {
+        ASSERT(tag == m_tag);
+    } else {
+        m_tag = tag;
+    }
+    m_size = file.ReadMpegLength();
+    m_start = file.GetPosition();
+
+    log.verbose1f("\"%s\": ReadDescriptor: tag 0x%02x data size %u (0x%x)",
+                  file.GetFilename().c_str(), m_tag, m_size, m_size);
+}
+
+void MP4Descriptor::ReadProperties(MP4File& file,
+                                   uint32_t propStartIndex, uint32_t propCount)
+{
+    uint32_t numProperties = min(propCount,
+                                 m_pProperties.Size() - propStartIndex);
+
+    for (uint32_t i = propStartIndex;
+            i < propStartIndex + numProperties; i++) {
+
+        MP4Property* pProperty = m_pProperties[i];
+
+        int32_t remaining = m_size - (file.GetPosition() - m_start);
+
+        if (pProperty->GetType() == DescriptorProperty) {
+            if (remaining > 0) {
+                // place a limit on how far this sub-descriptor looks
+                ((MP4DescriptorProperty*)pProperty)->SetSizeLimit(remaining);
+                pProperty->Read(file);
+            } // else do nothing, empty descriptor
+        } else {
+            // non-descriptor property
+            if (remaining >= 0) {
+                pProperty->Read(file);
+
+                MP4LogLevel thisVerbosity =
+                    (pProperty->GetType() == TableProperty) ?
+                    MP4_LOG_VERBOSE2 : MP4_LOG_VERBOSE1;
+
+                if (log.verbosity >= thisVerbosity) {
+                    // log.printf(thisVerbosity,"Read: ");
+                    pProperty->Dump(0, true);
+                }
+            } else {
+                log.errorf("%s: \"%s\": Overran descriptor, tag %u data size %u property %u",
+                           __FUNCTION__, file.GetFilename().c_str(), m_tag, m_size, i);
+                throw new Exception("overran descriptor",__FILE__, __LINE__, __FUNCTION__);
+            }
+        }
+    }
+}
+
+void MP4Descriptor::Write(MP4File& file)
+{
+    // call virtual function to adapt properties before writing
+    Mutate();
+
+    uint32_t numProperties = m_pProperties.Size();
+
+    if (numProperties == 0) {
+        WARNING(numProperties == 0);
+        return;
+    }
+
+    // write tag and length placeholder
+    file.WriteUInt8(m_tag);
+    uint64_t lengthPos = file.GetPosition();
+    file.WriteMpegLength(0);
+    uint64_t startPos = file.GetPosition();
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        m_pProperties[i]->Write(file);
+    }
+
+    // align with byte boundary (rarely necessary)
+    file.PadWriteBits();
+
+    // go back and write correct length
+    uint64_t endPos = file.GetPosition();
+    file.SetPosition(lengthPos);
+    file.WriteMpegLength(endPos - startPos);
+    file.SetPosition(endPos);
+}
+
+void MP4Descriptor::WriteToMemory(MP4File& file,
+                                  uint8_t** ppBytes, uint64_t* pNumBytes)
+{
+    // use memory buffer to save descriptor in memory
+    // instead of going directly to disk
+
+    file.EnableMemoryBuffer();
+
+    Write(file);
+
+    file.DisableMemoryBuffer(ppBytes, pNumBytes);
+}
+
+void MP4Descriptor::Dump(uint8_t indent, bool dumpImplicits)
+{
+    // call virtual function to adapt properties before dumping
+    Mutate();
+
+    uint32_t numProperties = m_pProperties.Size();
+
+    if (numProperties == 0) {
+        WARNING(numProperties == 0);
+        return;
+    }
+    for (uint32_t i = 0; i < numProperties; i++) {
+        m_pProperties[i]->Dump(indent, dumpImplicits);
+    }
+}
+
+uint8_t MP4Descriptor::GetDepth()
+{
+    return m_parentAtom.GetDepth();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/mp4descriptor.h b/src/mp4descriptor.h
new file mode 100644
index 0000000..160c5cf
--- /dev/null
+++ b/src/mp4descriptor.h
@@ -0,0 +1,104 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_MP4DESCRIPTOR_H
+#define MP4V2_IMPL_MP4DESCRIPTOR_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4Descriptor {
+public:
+    MP4Descriptor(MP4Atom& parentAtom, uint8_t tag = 0);
+
+    virtual ~MP4Descriptor();
+
+    uint8_t GetTag() {
+        return m_tag;
+    }
+    void SetTag(uint8_t tag) {
+        m_tag = tag;
+    }
+
+    void AddProperty(MP4Property* pProperty);
+
+    virtual void Generate();
+    virtual void Read(MP4File& file);
+    virtual void Write(MP4File& file);
+    virtual void Dump(uint8_t indent, bool dumpImplicits);
+
+    MP4Property* GetProperty(uint32_t index) {
+        return m_pProperties[index];
+    }
+
+    // use with extreme caution
+    void SetProperty(uint32_t index, MP4Property* pProperty) {
+        m_pProperties[index] = pProperty;
+    }
+
+    bool FindProperty( const char* name, MP4Property** ppProperty,
+                       uint32_t* pIndex = NULL)
+    {
+        return FindContainedProperty(name, ppProperty, pIndex);
+    }
+
+    void WriteToMemory(MP4File& file,
+                       uint8_t** ppBytes, uint64_t* pNumBytes);
+
+protected:
+    void SetReadMutate(uint32_t propIndex) {
+        m_readMutatePoint = propIndex;
+    }
+
+    void ReadHeader(MP4File& file);
+    void ReadProperties(MP4File& file,
+                        uint32_t startIndex = 0, uint32_t count = 0xFFFFFFFF);
+
+    virtual void Mutate() {
+        // default is a no-op
+    };
+
+    bool FindContainedProperty(const char* name,
+                               MP4Property** ppProperty, uint32_t* pIndex);
+
+    uint8_t GetDepth();
+
+protected:
+    MP4Atom&            m_parentAtom;
+    uint8_t             m_tag;
+    uint64_t            m_start;
+    uint32_t            m_size;
+    MP4PropertyArray    m_pProperties;
+    uint32_t            m_readMutatePoint;
+private:
+    MP4Descriptor();
+    MP4Descriptor ( const MP4Descriptor &src );
+    MP4Descriptor &operator= ( const MP4Descriptor &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4DESCRIPTOR_H
diff --git a/src/mp4file.cpp b/src/mp4file.cpp
new file mode 100644
index 0000000..86f386b
--- /dev/null
+++ b/src/mp4file.cpp
@@ -0,0 +1,4365 @@
+/*
+ * 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 - 2005.  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.
+ *
+ * Contributor(s):
+ *      Dave Mackie       dmackie@cisco.com
+ *              Alix Marchandise-Franquet alix@cisco.com
+ *              Ximpo Group Ltd.          mp4v2@ximpo.com
+ *              Bill May                  wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4File::MP4File( ) :
+    m_file             ( NULL )
+    , m_fileOriginalSize ( 0 )
+    , m_createFlags      ( 0 )
+{
+    this->Init();
+}
+
+/**
+ * Initialize member variables (shared among constructors)
+ */
+void MP4File::Init()
+{
+    m_pRootAtom = NULL;
+    m_odTrackId = MP4_INVALID_TRACK_ID;
+
+    m_useIsma = false;
+
+    m_pModificationProperty = NULL;
+    m_pTimeScaleProperty = NULL;
+    m_pDurationProperty = NULL;
+
+    m_memoryBuffer = NULL;
+    m_memoryBufferSize = 0;
+    m_memoryBufferPosition = 0;
+
+    m_numReadBits = 0;
+    m_bufReadBits = 0;
+    m_numWriteBits = 0;
+    m_bufWriteBits = 0;
+    m_editName = NULL;
+    m_trakName[0] = '\0';
+}
+
+MP4File::~MP4File()
+{
+    delete m_pRootAtom;
+    for( uint32_t i = 0; i < m_pTracks.Size(); i++ )
+        delete m_pTracks[i];
+    MP4Free( m_memoryBuffer ); // just in case
+    CHECK_AND_FREE( m_editName );
+    delete m_file;
+}
+
+const std::string &
+MP4File::GetFilename() const
+{
+    // No one should call this unless Read, etc. has
+    // succeeded and m_file exists since this method really
+    // only exists for the public API.  This helps us
+    // guarantee that MP4GetFilename always returns a valid
+    // string given a valid MP4FileHandle
+    ASSERT(m_file);
+    return m_file->name;
+}
+
+void MP4File::Read( const char* name, const MP4FileProvider* provider )
+{
+    Open( name, File::MODE_READ, provider );
+    ReadFromFile();
+    CacheProperties();
+}
+
+void MP4File::Create( const char*            fileName,
+                      uint32_t               flags,
+                      const MP4FileProvider* provider,
+                      int                    add_ftyp,
+                      int                    add_iods,
+                      char*                  majorBrand,
+                      uint32_t               minorVersion,
+                      char**                 supportedBrands,
+                      uint32_t               supportedBrandsCount )
+{
+    m_createFlags = flags;
+    Open( fileName, File::MODE_CREATE, provider );
+
+    // generate a skeletal atom tree
+    m_pRootAtom = MP4Atom::CreateAtom(*this, NULL, NULL);
+    m_pRootAtom->Generate();
+
+    if (add_ftyp != 0) {
+        MakeFtypAtom(majorBrand, minorVersion,
+                     supportedBrands, supportedBrandsCount);
+    }
+
+    CacheProperties();
+
+    // create mdat, and insert it after ftyp, and before moov
+    (void)InsertChildAtom(m_pRootAtom, "mdat",
+                          add_ftyp != 0 ? 1 : 0);
+
+    // start writing
+    m_pRootAtom->BeginWrite();
+    if (add_iods != 0) {
+        (void)AddChildAtom("moov", "iods");
+    }
+}
+
+bool MP4File::Use64Bits (const char *atomName)
+{
+    uint32_t atomid = ATOMID(atomName);
+    if (atomid == ATOMID("mdat") || atomid == ATOMID("stbl")) {
+        return (m_createFlags & MP4_CREATE_64BIT_DATA) == MP4_CREATE_64BIT_DATA;
+    }
+    if (atomid == ATOMID("mvhd") ||
+            atomid == ATOMID("tkhd") ||
+            atomid == ATOMID("mdhd")) {
+        return (m_createFlags & MP4_CREATE_64BIT_TIME) == MP4_CREATE_64BIT_TIME;
+    }
+    return false;
+}
+
+void MP4File::Check64BitStatus (const char *atomName)
+{
+    uint32_t atomid = ATOMID(atomName);
+
+    if (atomid == ATOMID("mdat") || atomid == ATOMID("stbl")) {
+        m_createFlags |= MP4_CREATE_64BIT_DATA;
+    } else if (atomid == ATOMID("mvhd") ||
+               atomid == ATOMID("tkhd") ||
+               atomid == ATOMID("mdhd")) {
+        m_createFlags |= MP4_CREATE_64BIT_TIME;
+    }
+}
+
+
+bool MP4File::Modify( const char* fileName )
+{
+    Open( fileName, File::MODE_MODIFY, NULL );
+    ReadFromFile();
+
+    // find the moov atom
+    MP4Atom* pMoovAtom = m_pRootAtom->FindAtom("moov");
+    uint32_t numAtoms;
+
+    if (pMoovAtom == NULL) {
+        // there isn't one, odd but we can still proceed
+        log.warningf("%s: \"%s\": no moov atom, can't modify",
+                     __FUNCTION__, GetFilename().c_str());
+        return false;
+        //pMoovAtom = AddChildAtom(m_pRootAtom, "moov");
+    } else {
+        numAtoms = m_pRootAtom->GetNumberOfChildAtoms();
+
+        // work backwards thru the top level atoms
+        int32_t i;
+        bool lastAtomIsMoov = true;
+        MP4Atom* pLastAtom = NULL;
+
+        for (i = numAtoms - 1; i >= 0; i--) {
+            MP4Atom* pAtom = m_pRootAtom->GetChildAtom(i);
+            const char* type = pAtom->GetType();
+
+            // get rid of any trailing free or skips
+            if (!strcmp(type, "free") || !strcmp(type, "skip")) {
+                m_pRootAtom->DeleteChildAtom(pAtom);
+                continue;
+            }
+
+            if (strcmp(type, "moov")) {
+                if (pLastAtom == NULL) {
+                    pLastAtom = pAtom;
+                    lastAtomIsMoov = false;
+                }
+                continue;
+            }
+
+            // now at moov atom
+
+            // multiple moov atoms?!?
+            if (pAtom != pMoovAtom) {
+                throw new Exception(
+                    "Badly formed mp4 file, multiple moov atoms",
+                    __FILE__,__LINE__,__FUNCTION__);
+            }
+
+            if (lastAtomIsMoov) {
+                // position to start of moov atom,
+                // effectively truncating file
+                // prior to adding new mdat
+                SetPosition(pMoovAtom->GetStart());
+
+            } else { // last atom isn't moov
+                // need to place a free atom
+                MP4Atom* pFreeAtom = MP4Atom::CreateAtom(*this, NULL, "free");
+
+                // in existing position of the moov atom
+                m_pRootAtom->InsertChildAtom(pFreeAtom, i);
+                m_pRootAtom->DeleteChildAtom(pMoovAtom);
+                m_pRootAtom->AddChildAtom(pMoovAtom);
+
+                // write free atom to disk
+                SetPosition(pMoovAtom->GetStart());
+                pFreeAtom->SetSize(pMoovAtom->GetSize());
+                pFreeAtom->Write();
+
+                // finally set our file position to the end of the last atom
+                SetPosition(pLastAtom->GetEnd());
+            }
+
+            break;
+        }
+        ASSERT(i != -1);
+    }
+
+    CacheProperties();  // of moov atom
+
+    numAtoms = m_pRootAtom->GetNumberOfChildAtoms();
+
+    // insert another mdat prior to moov atom (the last atom)
+    MP4Atom* pMdatAtom = InsertChildAtom(m_pRootAtom, "mdat", numAtoms - 1);
+
+    // start writing new mdat
+    pMdatAtom->BeginWrite(Use64Bits("mdat"));
+    return true;
+}
+
+void MP4File::Optimize( const char* srcFileName, const char* dstFileName )
+{
+    File* src = NULL;
+    File* dst = NULL;
+
+    // compute destination filename
+    string dname;
+    if( dstFileName ) {
+        dname = dstFileName;
+    } else {
+        // No destination given, so let's kludge together a temporary file.
+        // We'll try to create it in the same directory as the srcFileName, since
+        // it's more likely that directory is writable.  In the absence of that,
+        // we'll create it in "./", which is the default pathnameTemp() provides.
+        string s(srcFileName);
+        size_t pos = s.find_last_of("\\/");
+        const char *d;
+        if (pos == string::npos) {
+            d = ".";
+        } else {
+            s = s.substr(0, pos);
+            d = s.c_str();
+        }
+        FileSystem::pathnameTemp( dname, d, "tmp", ".mp4" );
+    }
+
+    try {
+        // file source to optimize
+        Open( srcFileName, File::MODE_READ, NULL );
+        ReadFromFile();
+        CacheProperties(); // of moov atom
+
+        src = m_file;
+        m_file = NULL;
+
+        // optimized file destination
+        Open( dname.c_str(), File::MODE_CREATE, NULL );
+        dst = m_file;
+
+        SetIntegerProperty( "moov.mvhd.modificationTime", MP4GetAbsTimestamp() );
+
+        // writing meta info in the optimal order
+        ((MP4RootAtom*)m_pRootAtom)->BeginOptimalWrite();
+
+        // write data in optimal order
+        RewriteMdat( *src, *dst );
+
+        // finish writing
+        ((MP4RootAtom*)m_pRootAtom)->FinishOptimalWrite();
+
+    }
+    catch (...) {
+        // cleanup and rethrow.  Without this, we'd leak memory and an open file handle(s).
+       if(src == NULL && dst == NULL)
+            delete m_file;// We didn't make it far enough to have m_file go to src or dst.
+
+        m_file = NULL;
+        delete dst;
+        delete src;
+        throw;
+    }
+
+    // cleanup
+    delete dst;
+    delete src;
+    m_file = NULL;
+
+    // move temporary file into place
+    if( !dstFileName )
+        Rename( dname.c_str(), srcFileName );
+}
+
+void MP4File::RewriteMdat( File& src, File& dst )
+{
+    uint32_t numTracks = m_pTracks.Size();
+
+    MP4ChunkId* chunkIds = new MP4ChunkId[numTracks];
+    MP4ChunkId* maxChunkIds = new MP4ChunkId[numTracks];
+    MP4Timestamp* nextChunkTimes = new MP4Timestamp[numTracks];
+
+    for( uint32_t i = 0; i < numTracks; i++ ) {
+        chunkIds[i] = 1;
+        maxChunkIds[i] = m_pTracks[i]->GetNumberOfChunks();
+        nextChunkTimes[i] = MP4_INVALID_TIMESTAMP;
+    }
+
+    for( ;; ) {
+        uint32_t nextTrackIndex = (uint32_t)-1;
+        MP4Timestamp nextTime = MP4_INVALID_TIMESTAMP;
+
+        for( uint32_t i = 0; i < numTracks; i++ ) {
+            if( chunkIds[i] > maxChunkIds[i] )
+                continue;
+
+            if( nextChunkTimes[i] == MP4_INVALID_TIMESTAMP ) {
+                MP4Timestamp chunkTime = m_pTracks[i]->GetChunkTime( chunkIds[i] );
+                nextChunkTimes[i] = MP4ConvertTime( chunkTime, m_pTracks[i]->GetTimeScale(), GetTimeScale() );
+            }
+
+            // time is not earliest so far
+            if( nextChunkTimes[i] > nextTime )
+                continue;
+
+            // prefer hint tracks to media tracks if times are equal
+            if( nextChunkTimes[i] == nextTime && strcmp( m_pTracks[i]->GetType(), MP4_HINT_TRACK_TYPE ))
+                continue;
+
+            // this is our current choice of tracks
+            nextTime = nextChunkTimes[i];
+            nextTrackIndex = i;
+        }
+
+        if( nextTrackIndex == (uint32_t)-1 )
+            break;
+
+        uint8_t* pChunk;
+        uint32_t chunkSize;
+
+        // point into original mp4 file for read chunk call
+        m_file = &src;
+        m_pTracks[nextTrackIndex]->ReadChunk( chunkIds[nextTrackIndex], &pChunk, &chunkSize );
+
+        // point back at the new mp4 file for write chunk
+        m_file = &dst;
+        m_pTracks[nextTrackIndex]->RewriteChunk( chunkIds[nextTrackIndex], pChunk, chunkSize );
+
+        MP4Free( pChunk );
+
+        chunkIds[nextTrackIndex]++;
+        nextChunkTimes[nextTrackIndex] = MP4_INVALID_TIMESTAMP;
+    }
+
+    delete [] chunkIds;
+    delete [] maxChunkIds;
+    delete [] nextChunkTimes;
+}
+
+void MP4File::Open( const char* name, File::Mode mode, const MP4FileProvider* provider )
+{
+    ASSERT( !m_file );
+
+    m_file = new File( name, mode, provider ? new io::CustomFileProvider( *provider ) : NULL );
+    if( m_file->open() ) {
+        ostringstream msg;
+        msg << "open(" << name << ") failed";
+        throw new Exception( msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    switch( mode ) {
+        case File::MODE_READ:
+        case File::MODE_MODIFY:
+            m_fileOriginalSize = m_file->size;
+            break;
+
+        case File::MODE_CREATE:
+        default:
+            m_fileOriginalSize = 0;
+            break;
+    }
+}
+
+void MP4File::ReadFromFile()
+{
+    // ensure we start at beginning of file
+    SetPosition(0);
+
+    // create a new root atom
+    ASSERT(m_pRootAtom == NULL);
+    m_pRootAtom = MP4Atom::CreateAtom(*this, NULL, NULL);
+
+    uint64_t fileSize = GetSize();
+
+    m_pRootAtom->SetStart(0);
+    m_pRootAtom->SetSize(fileSize);
+    m_pRootAtom->SetEnd(fileSize);
+
+    m_pRootAtom->Read();
+
+    // create MP4Track's for any tracks in the file
+    GenerateTracks();
+}
+
+void MP4File::GenerateTracks()
+{
+    uint32_t trackIndex = 0;
+
+    while (true) {
+        char trackName[32];
+        snprintf(trackName, sizeof(trackName), "moov.trak[%u]", trackIndex);
+
+        // find next trak atom
+        MP4Atom* pTrakAtom = m_pRootAtom->FindAtom(trackName);
+
+        // done, no more trak atoms
+        if (pTrakAtom == NULL) {
+            break;
+        }
+
+        // find track id property
+        MP4Integer32Property* pTrackIdProperty = NULL;
+        (void)pTrakAtom->FindProperty(
+            "trak.tkhd.trackId",
+            (MP4Property**)&pTrackIdProperty);
+
+        // find track type property
+        MP4StringProperty* pTypeProperty = NULL;
+        (void)pTrakAtom->FindProperty(
+            "trak.mdia.hdlr.handlerType",
+            (MP4Property**)&pTypeProperty);
+
+        // ensure we have the basics properties
+        if (pTrackIdProperty && pTypeProperty) {
+
+            m_trakIds.Add(pTrackIdProperty->GetValue());
+
+            MP4Track* pTrack = NULL;
+            try {
+                if (!strcmp(pTypeProperty->GetValue(), MP4_HINT_TRACK_TYPE)) {
+                    pTrack = new MP4RtpHintTrack(*this, *pTrakAtom);
+                } else {
+                    pTrack = new MP4Track(*this, *pTrakAtom);
+                }
+                m_pTracks.Add(pTrack);
+            }
+            catch( Exception* x ) {
+                log.errorf(*x);
+                delete x;
+            }
+
+            // remember when we encounter the OD track
+            if (pTrack && !strcmp(pTrack->GetType(), MP4_OD_TRACK_TYPE)) {
+                if (m_odTrackId == MP4_INVALID_TRACK_ID) {
+                    m_odTrackId = pTrackIdProperty->GetValue();
+                } else {
+                    log.warningf("%s: \"%s\": multiple OD tracks present",
+                                 __FUNCTION__, GetFilename().c_str() );
+                }
+            }
+        } else {
+            m_trakIds.Add(0);
+        }
+
+        trackIndex++;
+    }
+}
+
+void MP4File::CacheProperties()
+{
+    FindIntegerProperty("moov.mvhd.modificationTime",
+                        (MP4Property**)&m_pModificationProperty);
+
+    FindIntegerProperty("moov.mvhd.timeScale",
+                        (MP4Property**)&m_pTimeScaleProperty);
+
+    FindIntegerProperty("moov.mvhd.duration",
+                        (MP4Property**)&m_pDurationProperty);
+}
+
+void MP4File::BeginWrite()
+{
+    m_pRootAtom->BeginWrite();
+}
+
+void MP4File::FinishWrite(uint32_t options)
+{
+    // remove empty moov.udta.meta.ilst
+    {
+        MP4Atom* ilst = FindAtom( "moov.udta.meta.ilst" );
+        if( ilst ) {
+            if( ilst->GetNumberOfChildAtoms() == 0 ) {
+                ilst->GetParentAtom()->DeleteChildAtom( ilst );
+                delete ilst;
+            }
+        }
+    }
+
+    // remove empty moov.udta.meta
+    {
+        MP4Atom* meta = FindAtom( "moov.udta.meta" );
+        if( meta ) {
+            if( meta->GetNumberOfChildAtoms() == 0 ) {
+                meta->GetParentAtom()->DeleteChildAtom( meta );
+                delete meta;
+            }
+            else if( meta->GetNumberOfChildAtoms() == 1 ) {
+                if( ATOMID( meta->GetChildAtom( 0 )->GetType() ) == ATOMID( "hdlr" )) {
+                    meta->GetParentAtom()->DeleteChildAtom( meta );
+                    delete meta;
+                }
+            }
+        }
+    }
+
+    // remove empty moov.udta.name
+    {
+        MP4Atom* name = FindAtom( "moov.udta.name" );
+        if( name ) {
+            unsigned char *val = NULL;
+            uint32_t valSize = 0;
+            GetBytesProperty("moov.udta.name.value", (uint8_t**)&val, &valSize);
+            if( valSize == 0 ) {
+                name->GetParentAtom()->DeleteChildAtom( name );
+                delete name;
+            }
+        }
+    }
+
+    // remove empty moov.udta
+    {
+        MP4Atom* udta = FindAtom( "moov.udta" );
+        if( udta ) {
+            if( udta->GetNumberOfChildAtoms() == 0 ) {
+                udta->GetParentAtom()->DeleteChildAtom( udta );
+                delete udta;
+            }
+        }
+    }
+
+    // for all tracks, flush chunking buffers
+    for( uint32_t i = 0; i < m_pTracks.Size(); i++ ) {
+        ASSERT( m_pTracks[i] );
+        m_pTracks[i]->FinishWrite(options);
+    }
+
+    // ask root atom to write
+    m_pRootAtom->FinishWrite();
+
+    // finished all writes, if position < size then file has shrunk and
+    // we mark remaining bytes as free atom; otherwise trailing garbage remains.
+    if( GetPosition() < GetSize() ) {
+        MP4RootAtom* root = (MP4RootAtom*)FindAtom( "" );
+        ASSERT( root );
+
+        // compute size of free atom; always has 8 bytes of overhead
+        uint64_t size = GetSize() - GetPosition();
+        if( size < 8 )
+            size = 0;
+        else
+            size -= 8;
+
+        MP4FreeAtom* freeAtom = (MP4FreeAtom*)MP4Atom::CreateAtom( *this, NULL, "free" );
+        ASSERT( freeAtom );
+        freeAtom->SetSize( size );
+        root->AddChildAtom( freeAtom );
+        freeAtom->Write();
+    }
+}
+
+void MP4File::UpdateDuration(MP4Duration duration)
+{
+    MP4Duration currentDuration = GetDuration();
+    if (duration > currentDuration) {
+        SetDuration(duration);
+    }
+}
+
+void MP4File::Dump( bool dumpImplicits )
+{
+    log.dump(0, MP4_LOG_VERBOSE1, "\"%s\": Dumping meta-information...", m_file->name.c_str() );
+    m_pRootAtom->Dump( 0, dumpImplicits);
+}
+
+void MP4File::Close(uint32_t options)
+{
+    if( IsWriteMode() ) {
+        SetIntegerProperty( "moov.mvhd.modificationTime", MP4GetAbsTimestamp() );
+        FinishWrite(options);
+    }
+
+    delete m_file;
+    m_file = NULL;
+}
+
+void MP4File::Rename(const char* oldFileName, const char* newFileName)
+{
+    if( FileSystem::rename( oldFileName, newFileName ))
+        throw new PlatformException( sys::getLastErrorStr(), sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ );
+}
+
+void MP4File::ProtectWriteOperation(const char* file,
+                                    int         line,
+                                    const char* func )
+{
+    if( !IsWriteMode() )
+        throw new Exception( "operation not permitted in read mode", file, line, func );
+}
+
+MP4Track* MP4File::GetTrack(MP4TrackId trackId)
+{
+    return m_pTracks[FindTrackIndex(trackId)];
+}
+
+MP4Atom* MP4File::FindAtom(const char* name)
+{
+    MP4Atom* pAtom = NULL;
+    if (!name || !strcmp(name, "")) {
+        pAtom = m_pRootAtom;
+    } else {
+        pAtom = m_pRootAtom->FindAtom(name);
+    }
+    return pAtom;
+}
+
+MP4Atom* MP4File::AddChildAtom(
+    const char* parentName,
+    const char* childName)
+{
+    return AddChildAtom(FindAtom(parentName), childName);
+}
+
+MP4Atom* MP4File::AddChildAtom(
+    MP4Atom* pParentAtom,
+    const char* childName)
+{
+    return InsertChildAtom(pParentAtom, childName,
+                           pParentAtom->GetNumberOfChildAtoms());
+}
+
+MP4Atom* MP4File::InsertChildAtom(
+    const char* parentName,
+    const char* childName,
+    uint32_t index)
+{
+    return InsertChildAtom(FindAtom(parentName), childName, index);
+}
+
+MP4Atom* MP4File::InsertChildAtom(
+    MP4Atom* pParentAtom,
+    const char* childName,
+    uint32_t index)
+{
+    MP4Atom* pChildAtom = MP4Atom::CreateAtom(*this, pParentAtom, childName);
+
+    ASSERT(pParentAtom);
+    pParentAtom->InsertChildAtom(pChildAtom, index);
+
+    pChildAtom->Generate();
+
+    return pChildAtom;
+}
+
+MP4Atom* MP4File::AddDescendantAtoms(
+    const char* ancestorName,
+    const char* descendantNames)
+{
+    return AddDescendantAtoms(FindAtom(ancestorName), descendantNames);
+}
+
+MP4Atom* MP4File::AddDescendantAtoms(
+    MP4Atom* pAncestorAtom, const char* descendantNames)
+{
+    ASSERT(pAncestorAtom);
+
+    MP4Atom* pParentAtom = pAncestorAtom;
+    MP4Atom* pChildAtom = NULL;
+
+    while (true) {
+        char* childName = MP4NameFirst(descendantNames);
+
+        if (childName == NULL) {
+            break;
+        }
+
+        descendantNames = MP4NameAfterFirst(descendantNames);
+
+        pChildAtom = pParentAtom->FindChildAtom(childName);
+
+        if (pChildAtom == NULL) {
+            pChildAtom = AddChildAtom(pParentAtom, childName);
+        }
+
+        pParentAtom = pChildAtom;
+
+        MP4Free(childName);
+    }
+
+    return pChildAtom;
+}
+
+bool MP4File::FindProperty(const char* name,
+                           MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if( pIndex )
+        *pIndex = 0; // set the default answer for index
+    return m_pRootAtom->FindProperty(name, ppProperty, pIndex);
+}
+
+void MP4File::FindIntegerProperty(const char* name,
+                                  MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        ostringstream msg;
+        msg << "no such property - " << name;
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    switch ((*ppProperty)->GetType()) {
+    case Integer8Property:
+    case Integer16Property:
+    case Integer24Property:
+    case Integer32Property:
+    case Integer64Property:
+        break;
+    default:
+        ostringstream msg;
+        msg << "type mismatch - property " << name << " type " << (*ppProperty)->GetType();
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+uint64_t MP4File::GetIntegerProperty(const char* name)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindIntegerProperty(name, &pProperty, &index);
+
+    return ((MP4IntegerProperty*)pProperty)->GetValue(index);
+}
+
+void MP4File::SetIntegerProperty(const char* name, uint64_t value)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Property* pProperty = NULL;
+    uint32_t index = 0;
+
+    FindIntegerProperty(name, &pProperty, &index);
+
+    ((MP4IntegerProperty*)pProperty)->SetValue(value, index);
+}
+
+void MP4File::FindFloatProperty(const char* name,
+                                MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        ostringstream msg;
+        msg << "no such property - " << name;
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+    if ((*ppProperty)->GetType() != Float32Property) {
+        ostringstream msg;
+        msg << "type mismatch - property " << name << " type " << (*ppProperty)->GetType();
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+float MP4File::GetFloatProperty(const char* name)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindFloatProperty(name, &pProperty, &index);
+
+    return ((MP4Float32Property*)pProperty)->GetValue(index);
+}
+
+void MP4File::SetFloatProperty(const char* name, float value)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindFloatProperty(name, &pProperty, &index);
+
+    ((MP4Float32Property*)pProperty)->SetValue(value, index);
+}
+
+void MP4File::FindStringProperty(const char* name,
+                                 MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        ostringstream msg;
+        msg << "no such property - " << name;
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+    if ((*ppProperty)->GetType() != StringProperty) {
+        ostringstream msg;
+        msg << "type mismatch - property " << name << " type " << (*ppProperty)->GetType();
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+const char* MP4File::GetStringProperty(const char* name)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindStringProperty(name, &pProperty, &index);
+
+    return ((MP4StringProperty*)pProperty)->GetValue(index);
+}
+
+void MP4File::SetStringProperty(const char* name, const char* value)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindStringProperty(name, &pProperty, &index);
+
+    ((MP4StringProperty*)pProperty)->SetValue(value, index);
+}
+
+void MP4File::FindBytesProperty(const char* name,
+                                MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (!FindProperty(name, ppProperty, pIndex)) {
+        ostringstream msg;
+        msg << "no such property " << name;
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+    if ((*ppProperty)->GetType() != BytesProperty) {
+        ostringstream msg;
+        msg << "type mismatch - property " << name << " - type " <<  (*ppProperty)->GetType();
+        throw new Exception(msg.str(), __FILE__, __LINE__, __FUNCTION__);
+    }
+}
+
+void MP4File::GetBytesProperty(const char* name,
+                               uint8_t** ppValue, uint32_t* pValueSize)
+{
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindBytesProperty(name, &pProperty, &index);
+
+    ((MP4BytesProperty*)pProperty)->GetValue(ppValue, pValueSize, index);
+}
+
+void MP4File::SetBytesProperty(const char* name,
+                               const uint8_t* pValue, uint32_t valueSize)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Property* pProperty;
+    uint32_t index;
+
+    FindBytesProperty(name, &pProperty, &index);
+
+    ((MP4BytesProperty*)pProperty)->SetValue(pValue, valueSize, index);
+}
+
+
+// track functions
+
+MP4TrackId MP4File::AddTrack(const char* type, uint32_t timeScale)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    // create and add new trak atom
+    MP4Atom* pTrakAtom = AddChildAtom("moov", "trak");
+    ASSERT(pTrakAtom);
+
+    // allocate a new track id
+    MP4TrackId trackId = AllocTrackId();
+
+    m_trakIds.Add(trackId);
+
+    // set track id
+    MP4Integer32Property* pInteger32Property = NULL;
+    (void)pTrakAtom->FindProperty("trak.tkhd.trackId",
+                                  (MP4Property**)&pInteger32Property);
+    ASSERT(pInteger32Property);
+    pInteger32Property->SetValue(trackId);
+
+    // set track type
+    const char* normType = MP4NormalizeTrackType(type);
+
+    // sanity check for user defined types
+    if (strlen(normType) > 4) {
+        log.warningf("%s: \"%s\": type truncated to four characters",
+                     __FUNCTION__, GetFilename().c_str());
+        // StringProperty::SetValue() will do the actual truncation
+    }
+
+    MP4StringProperty* pStringProperty = NULL;
+    (void)pTrakAtom->FindProperty("trak.mdia.hdlr.handlerType",
+                                  (MP4Property**)&pStringProperty);
+    ASSERT(pStringProperty);
+    pStringProperty->SetValue(normType);
+
+    // set track time scale
+    pInteger32Property = NULL;
+    (void)pTrakAtom->FindProperty("trak.mdia.mdhd.timeScale",
+                                  (MP4Property**)&pInteger32Property);
+    ASSERT(pInteger32Property);
+    pInteger32Property->SetValue(timeScale ? timeScale : 1000);
+
+    // now have enough to create MP4Track object
+    MP4Track* pTrack = NULL;
+    if (!strcmp(normType, MP4_HINT_TRACK_TYPE)) {
+        pTrack = new MP4RtpHintTrack(*this, *pTrakAtom);
+    } else {
+        pTrack = new MP4Track(*this, *pTrakAtom);
+    }
+    m_pTracks.Add(pTrack);
+
+    // mark non-hint tracks as enabled
+    if (strcmp(normType, MP4_HINT_TRACK_TYPE)) {
+        SetTrackIntegerProperty(trackId, "tkhd.flags", 1);
+    }
+
+    // mark track as contained in this file
+    // LATER will provide option for external data references
+    AddDataReference(trackId, NULL);
+
+    return trackId;
+}
+
+void MP4File::AddTrackToIod(MP4TrackId trackId)
+{
+    MP4DescriptorProperty* pDescriptorProperty = NULL;
+    (void)m_pRootAtom->FindProperty("moov.iods.esIds",
+                                    (MP4Property**)&pDescriptorProperty);
+    ASSERT(pDescriptorProperty);
+
+    MP4Descriptor* pDescriptor =
+        pDescriptorProperty->AddDescriptor(MP4ESIDIncDescrTag);
+    ASSERT(pDescriptor);
+
+    MP4Integer32Property* pIdProperty = NULL;
+    (void)pDescriptor->FindProperty("id",
+                                    (MP4Property**)&pIdProperty);
+    ASSERT(pIdProperty);
+
+    pIdProperty->SetValue(trackId);
+}
+
+void MP4File::RemoveTrackFromIod(MP4TrackId trackId, bool shallHaveIods)
+{
+    MP4DescriptorProperty* pDescriptorProperty = NULL;
+    if (!m_pRootAtom->FindProperty("moov.iods.esIds",(MP4Property**)&pDescriptorProperty)
+        || pDescriptorProperty == NULL)
+        return;
+
+    for (uint32_t i = 0; i < pDescriptorProperty->GetCount(); i++) {
+        /* static */
+        char name[32];
+        snprintf(name, sizeof(name), "esIds[%u].id", i);
+
+        MP4Integer32Property* pIdProperty = NULL;
+        (void)pDescriptorProperty->FindProperty(name,
+                                                (MP4Property**)&pIdProperty);
+        // wmay ASSERT(pIdProperty);
+
+        if (pIdProperty != NULL &&
+                pIdProperty->GetValue() == trackId) {
+            pDescriptorProperty->DeleteDescriptor(i);
+            break;
+        }
+    }
+}
+
+void MP4File::AddTrackToOd(MP4TrackId trackId)
+{
+    if (!m_odTrackId) {
+        return;
+    }
+
+    AddTrackReference(MakeTrackName(m_odTrackId, "tref.mpod"), trackId);
+}
+
+void MP4File::RemoveTrackFromOd(MP4TrackId trackId)
+{
+    if (!m_odTrackId) {
+        return;
+    }
+
+    RemoveTrackReference(MakeTrackName(m_odTrackId, "tref.mpod"), trackId);
+}
+
+/*
+ * Try to obtain the properties of this reference track, if not found then return
+ * NULL in *ppCountProperty and *ppTrackIdProperty.
+ */
+void MP4File::GetTrackReferenceProperties(const char* trefName,
+        MP4Property** ppCountProperty, MP4Property** ppTrackIdProperty)
+{
+    char propName[1024];
+
+    snprintf(propName, sizeof(propName), "%s.%s", trefName, "entryCount");
+    (void)m_pRootAtom->FindProperty(propName, ppCountProperty);
+
+    snprintf(propName, sizeof(propName), "%s.%s", trefName, "entries.trackId");
+    (void)m_pRootAtom->FindProperty(propName, ppTrackIdProperty);
+}
+
+void MP4File::AddTrackReference(const char* trefName, MP4TrackId refTrackId)
+{
+    MP4Integer32Property* pCountProperty = NULL;
+    MP4Integer32Property* pTrackIdProperty = NULL;
+
+    GetTrackReferenceProperties(trefName,
+                                (MP4Property**)&pCountProperty,
+                                (MP4Property**)&pTrackIdProperty);
+
+    if (pCountProperty && pTrackIdProperty) {
+        pTrackIdProperty->AddValue(refTrackId);
+        pCountProperty->IncrementValue();
+    }
+}
+
+uint32_t MP4File::FindTrackReference(const char* trefName,
+                                     MP4TrackId refTrackId)
+{
+    MP4Integer32Property* pCountProperty = NULL;
+    MP4Integer32Property* pTrackIdProperty = NULL;
+
+    GetTrackReferenceProperties(trefName,
+                                (MP4Property**)&pCountProperty,
+                                (MP4Property**)&pTrackIdProperty);
+
+    if (pCountProperty && pTrackIdProperty) {
+        for (uint32_t i = 0; i < pCountProperty->GetValue(); i++) {
+            if (refTrackId == pTrackIdProperty->GetValue(i)) {
+                return i + 1;   // N.B. 1 not 0 based index
+            }
+        }
+    }
+    return 0;
+}
+
+void MP4File::RemoveTrackReference(const char* trefName, MP4TrackId refTrackId)
+{
+    MP4Integer32Property* pCountProperty = NULL;
+    MP4Integer32Property* pTrackIdProperty = NULL;
+
+    GetTrackReferenceProperties(trefName,
+                                (MP4Property**)&pCountProperty,
+                                (MP4Property**)&pTrackIdProperty);
+
+    if (pCountProperty && pTrackIdProperty) {
+        for (uint32_t i = 0; i < pCountProperty->GetValue(); i++) {
+            if (refTrackId == pTrackIdProperty->GetValue(i)) {
+                pTrackIdProperty->DeleteValue(i);
+                pCountProperty->IncrementValue(-1);
+            }
+        }
+    }
+}
+
+void MP4File::AddDataReference(MP4TrackId trackId, const char* url)
+{
+    MP4Atom* pDrefAtom =
+        FindAtom(MakeTrackName(trackId, "mdia.minf.dinf.dref"));
+    ASSERT(pDrefAtom);
+
+    MP4Integer32Property* pCountProperty = NULL;
+    (void)pDrefAtom->FindProperty("dref.entryCount",
+                                  (MP4Property**)&pCountProperty);
+    ASSERT(pCountProperty);
+    pCountProperty->IncrementValue();
+
+    MP4Atom* pUrlAtom = AddChildAtom(pDrefAtom, "url ");
+
+    if (url && url[0] != '\0') {
+        pUrlAtom->SetFlags(pUrlAtom->GetFlags() & 0xFFFFFE);
+
+        MP4StringProperty* pUrlProperty = NULL;
+        (void)pUrlAtom->FindProperty("url .location",
+                                     (MP4Property**)&pUrlProperty);
+        ASSERT(pUrlProperty);
+        pUrlProperty->SetValue(url);
+    } else {
+        pUrlAtom->SetFlags(pUrlAtom->GetFlags() | 1);
+    }
+}
+
+MP4TrackId MP4File::AddSystemsTrack(const char* type, uint32_t timeScale)
+{
+    const char* normType = MP4NormalizeTrackType(type);
+
+    // TBD if user type, fix name to four chars, and warn
+
+    MP4TrackId trackId = AddTrack(type, timeScale);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "mp4s");
+
+    AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4s atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.ESID",
+                            0
+                           );
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId",
+                            MP4SystemsV1ObjectType);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.streamType",
+                            ConvertTrackTypeToStreamType(normType));
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddODTrack()
+{
+    // until a demonstrated need emerges
+    // we limit ourselves to one object description track
+    if (m_odTrackId != MP4_INVALID_TRACK_ID) {
+        throw new Exception("object description track already exists",__FILE__, __LINE__, __FUNCTION__);
+    }
+
+    m_odTrackId = AddSystemsTrack(MP4_OD_TRACK_TYPE);
+
+    AddTrackToIod(m_odTrackId);
+
+    (void)AddDescendantAtoms(MakeTrackName(m_odTrackId, NULL), "tref.mpod");
+
+    return m_odTrackId;
+}
+
+MP4TrackId MP4File::AddSceneTrack()
+{
+    MP4TrackId trackId = AddSystemsTrack(MP4_SCENE_TRACK_TYPE);
+
+    AddTrackToIod(trackId);
+    AddTrackToOd(trackId);
+
+    return trackId;
+}
+
+bool MP4File::ShallHaveIods()
+{
+    // NULL terminated list of brands which require the IODS atom
+    const char* brandsWithIods[] = {
+        "mp42",
+        "isom",
+        NULL
+    };
+
+    MP4FtypAtom* ftyp = (MP4FtypAtom*)m_pRootAtom->FindAtom( "ftyp" );
+    if( !ftyp )
+        return false;
+
+    // check major brand
+    const char* brand = ftyp->majorBrand.GetValue();
+    for( uint32_t i = 0; brandsWithIods[i] != NULL; i++ ) {
+        if( !strcasecmp( brandsWithIods[i], brand ))
+            return true;
+    }
+
+    // check compatible brands
+    uint32_t max = ftyp->compatibleBrands.GetCount();
+    for( uint32_t i = 0; i < max; i++ ) {
+        brand = ftyp->compatibleBrands.GetValue( i );
+        for( uint32_t j = 0; brandsWithIods[j] != NULL ; j++) {
+            if( !strcasecmp( brandsWithIods[j], brand ))
+                return true;
+        }
+    }
+
+    return false;
+}
+
+void MP4File::SetAmrVendor(
+    MP4TrackId trackId,
+    uint32_t vendor)
+{
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.damr.vendor",
+                            vendor);
+}
+
+void MP4File::SetAmrDecoderVersion(
+    MP4TrackId trackId,
+    uint8_t decoderVersion)
+{
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.damr.decoderVersion",
+                            decoderVersion);
+}
+
+void MP4File::SetAmrModeSet(
+    MP4TrackId trackId,
+    uint16_t modeSet)
+{
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.damr.modeSet",
+                            modeSet);
+}
+uint16_t MP4File::GetAmrModeSet(MP4TrackId trackId)
+{
+    return GetTrackIntegerProperty(trackId,
+                                   "mdia.minf.stbl.stsd.*.damr.modeSet");
+}
+
+MP4TrackId MP4File::AddAmrAudioTrack(
+    uint32_t timeScale,
+    uint16_t modeSet,
+    uint8_t modeChangePeriod,
+    uint8_t framesPerSample,
+    bool isAmrWB)
+{
+
+    uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample
+
+    MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), isAmrWB ? "sawb" : "samr");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4a atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.timeScale",
+                            timeScale);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.damr.modeSet",
+                            modeSet);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.damr.modeChangePeriod",
+                            modeChangePeriod);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.*.damr.framesPerSample",
+                            framesPerSample);
+
+
+    m_pTracks[FindTrackIndex(trackId)]->
+    SetFixedSampleDuration(fixedSampleDuration);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddULawAudioTrack(    uint32_t timeScale)
+{
+    uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample
+
+    MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "ulaw");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4a atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.ulaw.timeScale",
+                            timeScale<<16);
+
+    m_pTracks[FindTrackIndex(trackId)]->SetFixedSampleDuration(fixedSampleDuration);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddALawAudioTrack(    uint32_t timeScale)
+{
+    uint32_t fixedSampleDuration = (timeScale * 20)/1000; // 20mSec/Sample
+
+    MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "alaw");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4a atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.alaw.timeScale",
+                            timeScale<<16);
+
+    m_pTracks[FindTrackIndex(trackId)]->SetFixedSampleDuration(fixedSampleDuration);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddAudioTrack(
+    uint32_t timeScale,
+    MP4Duration sampleDuration,
+    uint8_t audioType)
+{
+    MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "mp4a");
+
+    AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4a atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4a.timeScale", timeScale << 16);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4a.esds.ESID",
+                            0
+                           );
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.objectTypeId",
+                            audioType);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4a.esds.decConfigDescr.streamType",
+                            MP4AudioStreamType);
+
+    m_pTracks[FindTrackIndex(trackId)]->
+    SetFixedSampleDuration(sampleDuration);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddAC3AudioTrack(
+    uint32_t samplingRate,
+    uint8_t fscod,
+    uint8_t bsid,
+    uint8_t bsmod,
+    uint8_t acmod,
+    uint8_t lfeon,
+    uint8_t bit_rate_code)
+{
+    MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, samplingRate);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
+
+    InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
+
+    AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "ac-3");
+
+    // Set Ac3 settings
+    MP4Integer16Property* pSampleRateProperty = NULL;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.samplingRate"),
+        (MP4Property**)&pSampleRateProperty);
+    if (pSampleRateProperty) {
+        pSampleRateProperty->SetValue(samplingRate);
+    } else {
+        throw new Exception("no ac-3.samplingRate property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    MP4BitfieldProperty* pBitfieldProperty = NULL;
+
+    FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.fscod"),
+                               (MP4Property**)&pBitfieldProperty);
+    if (pBitfieldProperty) {
+        pBitfieldProperty->SetValue(fscod);
+        pBitfieldProperty = NULL;
+    } else {
+        throw new Exception("no dac3.fscod property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.bsid"),
+                               (MP4Property**)&pBitfieldProperty);
+    if (pBitfieldProperty) {
+        pBitfieldProperty->SetValue(bsid);
+        pBitfieldProperty = NULL;
+    } else {
+        throw new Exception("no dac3.bsid property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.bsmod"),
+                               (MP4Property**)&pBitfieldProperty);
+    if (pBitfieldProperty) {
+        pBitfieldProperty->SetValue(bsmod);
+        pBitfieldProperty = NULL;
+    } else {
+        throw new Exception("no dac3.bsmod property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.acmod"),
+                               (MP4Property**)&pBitfieldProperty);
+    if (pBitfieldProperty) {
+        pBitfieldProperty->SetValue(acmod);
+        pBitfieldProperty = NULL;
+    } else {
+        throw new Exception("no dac3.acmod property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.lfeon"),
+                               (MP4Property**)&pBitfieldProperty);
+    if (pBitfieldProperty) {
+        pBitfieldProperty->SetValue(lfeon);
+        pBitfieldProperty = NULL;
+    } else {
+        throw new Exception("no dac3.lfeon property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    FindProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.ac-3.dac3.bit_rate_code"),
+                               (MP4Property**)&pBitfieldProperty);
+    if (pBitfieldProperty) {
+        pBitfieldProperty->SetValue(bit_rate_code);
+        pBitfieldProperty = NULL;
+    } else {
+        throw new Exception("no dac3.bit_rate_code property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4a atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    m_pTracks[FindTrackIndex(trackId)]->
+        SetFixedSampleDuration(1536);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::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
+                                    )
+{
+    uint32_t original_fmt = 0;
+
+    MP4TrackId trackId = AddTrack(MP4_AUDIO_TRACK_TYPE, timeScale);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.volume", 1.0);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "smhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "enca");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the enca atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+                        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+
+    /* set all the ismacryp-specific values */
+    // original format is mp4a
+    if (use_ismacryp) {
+        original_fmt = ATOMID("mp4a");
+        SetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.enca.sinf.frma.data-format",
+                                original_fmt);
+
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf"),
+                           "schm");
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf"),
+                           "schi");
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf.schi"),
+                           "iKMS");
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.enca.sinf.schi"),
+                           "iSFM");
+        SetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.enca.sinf.schm.scheme_type",
+                                scheme_type);
+
+        SetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.enca.sinf.schm.scheme_version",
+                                scheme_version);
+
+        SetTrackStringProperty(trackId,
+                               "mdia.minf.stbl.stsd.enca.sinf.schi.iKMS.kms_URI",
+                               kms_uri);
+
+        SetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.enca.sinf.schi.iSFM.selective-encryption",
+                                selective_enc);
+
+        SetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.enca.sinf.schi.iSFM.key-indicator-length",
+                                key_ind_len);
+
+        SetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.enca.sinf.schi.iSFM.IV-length",
+                                iv_len);
+        /* end ismacryp */
+    }
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.enca.timeScale", timeScale);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.enca.esds.ESID",
+                            0
+                           );
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.enca.esds.decConfigDescr.objectTypeId",
+                            audioType);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.enca.esds.decConfigDescr.streamType",
+                            MP4AudioStreamType);
+
+    m_pTracks[FindTrackIndex(trackId)]->
+    SetFixedSampleDuration(sampleDuration);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddCntlTrackDefault (uint32_t timeScale,
+        MP4Duration sampleDuration,
+        const char *type)
+{
+    MP4TrackId trackId = AddTrack(MP4_CNTL_TRACK_TYPE, timeScale);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), type);
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4v atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsz.sampleSize", sampleDuration);
+
+    m_pTracks[FindTrackIndex(trackId)]->
+    SetFixedSampleDuration(sampleDuration);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddHrefTrack (uint32_t timeScale,
+                                  MP4Duration sampleDuration,
+                                  const char *base_url)
+{
+    MP4TrackId trackId = AddCntlTrackDefault(timeScale, sampleDuration, "href");
+
+    if (base_url != NULL) {
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.href"),
+                           "burl");
+        SetTrackStringProperty(trackId, "mdia.minf.stbl.stsd.href.burl.base_url",
+                               base_url);
+    }
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddVideoTrackDefault(
+    uint32_t timeScale,
+    MP4Duration sampleDuration,
+    uint16_t width,
+    uint16_t height,
+    const char *videoType)
+{
+    MP4TrackId trackId = AddTrack(MP4_VIDEO_TRACK_TYPE, timeScale);
+
+    AddTrackToOd(trackId);
+
+    SetTrackFloatProperty(trackId, "tkhd.width", width);
+    SetTrackFloatProperty(trackId, "tkhd.height", height);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "vmhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), videoType);
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4v atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsz.sampleSize", sampleDuration);
+
+    m_pTracks[FindTrackIndex(trackId)]->
+    SetFixedSampleDuration(sampleDuration);
+
+    return trackId;
+}
+MP4TrackId MP4File::AddMP4VideoTrack(
+    uint32_t timeScale,
+    MP4Duration sampleDuration,
+    uint16_t width,
+    uint16_t height,
+    uint8_t videoType)
+{
+    MP4TrackId trackId = AddVideoTrackDefault(timeScale,
+                         sampleDuration,
+                         width,
+                         height,
+                         "mp4v");
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4v.width", width);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4v.height", height);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4v.esds.ESID",
+                            0
+                           );
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4v.esds.decConfigDescr.objectTypeId",
+                            videoType);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4v.esds.decConfigDescr.streamType",
+                            MP4VisualStreamType);
+
+    return trackId;
+}
+
+// ismacrypted
+MP4TrackId MP4File::AddEncVideoTrack(uint32_t timeScale,
+                                     MP4Duration sampleDuration,
+                                     uint16_t width,
+                                     uint16_t height,
+                                     uint8_t videoType,
+                                     mp4v2_ismacrypParams *icPp,
+                                     const char *oFormat
+                                    )
+{
+    uint32_t original_fmt = 0;
+
+    MP4TrackId trackId = AddVideoTrackDefault(timeScale,
+                         sampleDuration,
+                         width,
+                         height,
+                         "encv");
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.width", width);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.height", height);
+
+    /* set all the ismacryp-specific values */
+
+    original_fmt = ATOMID(oFormat);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.sinf.frma.data-format",
+                            original_fmt);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"),
+                       "schm");
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"),
+                       "schi");
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"),
+                       "iKMS");
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"),
+                       "iSFM");
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_type",
+                            icPp->scheme_type);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_version",
+                            icPp->scheme_version);
+
+    SetTrackStringProperty(trackId,
+                           "mdia.minf.stbl.stsd.encv.sinf.schi.iKMS.kms_URI",
+                           icPp->kms_uri);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.selective-encryption",
+                            icPp->selective_enc);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.key-indicator-length",
+                            icPp->key_ind_len);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.IV-length",
+                            icPp->iv_len);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.esds.ESID",
+                            0
+                           );
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.esds.decConfigDescr.objectTypeId",
+                            videoType);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.encv.esds.decConfigDescr.streamType",
+                            MP4VisualStreamType);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::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)
+{
+    MP4TrackId trackId = AddVideoTrackDefault(timeScale,
+                         sampleDuration,
+                         width,
+                         height,
+                         "avc1");
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.avc1.width", width);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.avc1.height", height);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.avc1.avcC.AVCProfileIndication",
+                            AVCProfileIndication);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.avc1.avcC.profile_compatibility",
+                            profile_compat);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.avc1.avcC.AVCLevelIndication",
+                            AVCLevelIndication);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.avc1.avcC.lengthSizeMinusOne",
+                            sampleLenFieldSizeMinusOne);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddEncH264VideoTrack(
+    uint32_t timeScale,
+    MP4Duration sampleDuration,
+    uint16_t width,
+    uint16_t height,
+    MP4Atom *srcAtom,
+    mp4v2_ismacrypParams *icPp)
+
+{
+
+    uint32_t original_fmt = 0;
+    MP4Atom *avcCAtom;
+
+    MP4TrackId trackId = AddVideoTrackDefault(timeScale,
+                         sampleDuration,
+                         width,
+                         height,
+                         "encv");
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.width", width);
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.height", height);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv"), "avcC");
+
+    // create default values
+    avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.avcC"));
+
+    // export source atom
+    ((MP4AvcCAtom *) srcAtom)->Clone((MP4AvcCAtom *)avcCAtom);
+
+    /* set all the ismacryp-specific values */
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"), "schm");
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf"), "schi");
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"), "iKMS");
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi"), "iSFM");
+
+    // per ismacrypt E&A V1.1 section 9.1.2.1 'avc1' is renamed '264b'
+    // avc1 must not appear as a sample entry name or original format name
+    original_fmt = ATOMID("264b");
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.frma.data-format",
+                            original_fmt);
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_type",
+                            icPp->scheme_type);
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schm.scheme_version",
+                            icPp->scheme_version);
+
+    SetTrackStringProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iKMS.kms_URI",
+                           icPp->kms_uri);
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.selective-encryption",
+                            icPp->selective_enc);
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.key-indicator-length",
+                            icPp->key_ind_len);
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.encv.sinf.schi.iSFM.IV-length",
+                            icPp->iv_len);
+
+
+    return trackId;
+}
+
+
+void MP4File::AddH264SequenceParameterSet (MP4TrackId trackId,
+        const uint8_t *pSequence,
+        uint16_t sequenceLen)
+{
+    const char *format;
+    MP4Atom *avcCAtom;
+
+    // get 4cc media format - can be avc1 or encv for ismacrypted track
+    format = GetTrackMediaDataName(trackId);
+
+    if (!strcasecmp(format, "avc1"))
+        avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1.avcC"));
+    else if (!strcasecmp(format, "encv"))
+        avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.avcC"));
+    else
+        // huh?  unknown track format
+        return;
+
+
+    MP4BitfieldProperty *pCount;
+    MP4Integer16Property *pLength;
+    MP4BytesProperty *pUnit;
+    if ((avcCAtom->FindProperty("avcC.numOfSequenceParameterSets",
+                                (MP4Property **)&pCount) == false) ||
+            (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetLength",
+                                    (MP4Property **)&pLength) == false) ||
+            (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetNALUnit",
+                                    (MP4Property **)&pUnit) == false)) {
+        log.errorf("%s: \"%s\": Could not find avcC properties",
+                   __FUNCTION__, GetFilename().c_str() );
+        return;
+    }
+    uint32_t count = pCount->GetValue();
+
+    if (count > 0) {
+        // see if we already exist
+        for (uint32_t index = 0; index < count; index++) {
+            if (pLength->GetValue(index) == sequenceLen) {
+                uint8_t *seq;
+                uint32_t seqlen;
+                pUnit->GetValue(&seq, &seqlen, index);
+                if (memcmp(seq, pSequence, sequenceLen) == 0) {
+                    free(seq);
+                    return;
+                }
+                free(seq);
+            }
+        }
+    }
+    pLength->AddValue(sequenceLen);
+    pUnit->AddValue(pSequence, sequenceLen);
+    pCount->IncrementValue();
+
+    return;
+}
+void MP4File::AddH264PictureParameterSet (MP4TrackId trackId,
+        const uint8_t *pPict,
+        uint16_t pictLen)
+{
+    MP4Atom *avcCAtom =
+        FindAtom(MakeTrackName(trackId,
+                               "mdia.minf.stbl.stsd.avc1.avcC"));
+    MP4Integer8Property *pCount;
+    MP4Integer16Property *pLength;
+    MP4BytesProperty *pUnit;
+    if ((avcCAtom->FindProperty("avcC.numOfPictureParameterSets",
+                                (MP4Property **)&pCount) == false) ||
+            (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetLength",
+                                    (MP4Property **)&pLength) == false) ||
+            (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetNALUnit",
+                                    (MP4Property **)&pUnit) == false)) {
+        log.errorf("%s: \"%s\": Could not find avcC picture table properties",
+                   __FUNCTION__, GetFilename().c_str());
+        return;
+    }
+
+    ASSERT(pCount);
+    uint32_t count = pCount->GetValue();
+
+    if (count > 0) {
+        // see if we already exist
+        for (uint32_t index = 0; index < count; index++) {
+            if (pLength->GetValue(index) == pictLen) {
+                uint8_t *seq;
+                uint32_t seqlen;
+                pUnit->GetValue(&seq, &seqlen, index);
+                if (memcmp(seq, pPict, pictLen) == 0) {
+                    log.verbose1f("\"%s\": picture matches %d", 
+                                  GetFilename().c_str(), index);
+                    free(seq);
+                    return;
+                }
+                free(seq);
+            }
+        }
+    }
+    pLength->AddValue(pictLen);
+    pUnit->AddValue(pPict, pictLen);
+    pCount->IncrementValue();
+    log.verbose1f("\"%s\": new picture added %d", GetFilename().c_str(),
+                  pCount->GetValue());
+
+    return;
+}
+void  MP4File::SetH263Vendor(
+    MP4TrackId trackId,
+    uint32_t vendor)
+{
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.vendor",
+                            vendor);
+}
+
+void MP4File::SetH263DecoderVersion(
+    MP4TrackId trackId,
+    uint8_t decoderVersion)
+{
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.decoderVersion",
+                            decoderVersion);
+}
+
+void MP4File::SetH263Bitrates(
+    MP4TrackId trackId,
+    uint32_t avgBitrate,
+    uint32_t maxBitrate)
+{
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.bitr.avgBitrate",
+                            avgBitrate);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.bitr.maxBitrate",
+                            maxBitrate);
+
+}
+
+MP4TrackId MP4File::AddH263VideoTrack(
+    uint32_t timeScale,
+    MP4Duration sampleDuration,
+    uint16_t width,
+    uint16_t height,
+    uint8_t h263Level,
+    uint8_t h263Profile,
+    uint32_t avgBitrate,
+    uint32_t maxBitrate)
+
+{
+    MP4TrackId trackId = AddVideoTrackDefault(timeScale,
+                         sampleDuration,
+                         width,
+                         height,
+                         "s263");
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.width", width);
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.height", height);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.h263Level", h263Level);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.h263Profile", h263Profile);
+
+    // Add the bitr atom
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.s263.d263"),
+                       "bitr");
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.bitr.avgBitrate", avgBitrate);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.s263.d263.bitr.maxBitrate", maxBitrate);
+
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsz.sampleSize", sampleDuration);
+
+    return trackId;
+
+}
+
+MP4TrackId MP4File::AddHintTrack(MP4TrackId refTrackId)
+{
+    // validate reference track id
+    (void)FindTrackIndex(refTrackId);
+
+    MP4TrackId trackId =
+        AddTrack(MP4_HINT_TRACK_TYPE, GetTrackTimeScale(refTrackId));
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "hmhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "rtp ");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the rtp atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.rtp .tims.timeScale",
+                            GetTrackTimeScale(trackId));
+
+    (void)AddDescendantAtoms(MakeTrackName(trackId, NULL), "tref.hint");
+
+    AddTrackReference(MakeTrackName(trackId, "tref.hint"), refTrackId);
+
+    (void)AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.hnti.sdp ");
+
+    (void)AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.hinf");
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddTextTrack(MP4TrackId refTrackId)
+{
+    // validate reference track id
+    (void)FindTrackIndex(refTrackId);
+
+    MP4TrackId trackId =
+        AddTrack(MP4_TEXT_TRACK_TYPE, GetTrackTimeScale(refTrackId));
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "gmhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "text");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the text atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddSubtitleTrack(uint32_t timescale,
+                                     uint16_t width,
+                                     uint16_t height)
+{
+    MP4TrackId trackId =
+        AddTrack(MP4_SUBTITLE_TRACK_TYPE, timescale);
+
+    InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
+
+    AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "tx3g");
+
+    SetTrackFloatProperty(trackId, "tkhd.width", width);
+    SetTrackFloatProperty(trackId, "tkhd.height", height);
+
+    // Hardcoded crap... add the ftab atom and add one font entry
+    MP4Atom* pFtabAtom = AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.tx3g"), "ftab");
+
+    ((MP4Integer16Property*)pFtabAtom->GetProperty(0))->IncrementValue();
+
+    MP4Integer16Property* pfontID = (MP4Integer16Property*)((MP4TableProperty*)pFtabAtom->GetProperty(1))->GetProperty(0);
+    pfontID->AddValue(1);
+
+    MP4StringProperty* pName = (MP4StringProperty*)((MP4TableProperty*)pFtabAtom->GetProperty(1))->GetProperty(1);
+    pName->AddValue("Arial");
+
+    SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.tx3g.fontID", 1);
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the tx3g atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddSubpicTrack(uint32_t timescale,
+                                     uint16_t width,
+                                     uint16_t height)
+{
+    MP4TrackId trackId =
+        AddTrack(MP4_SUBPIC_TRACK_TYPE, timescale);
+
+    InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "nmhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "mp4s");
+
+    SetTrackFloatProperty(trackId, "tkhd.width", width);
+    SetTrackFloatProperty(trackId, "tkhd.height", height);
+    SetTrackIntegerProperty(trackId, "tkhd.layer", 0);
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the mp4s atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.ESID",
+                            0
+                           );
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.objectTypeId",
+                            MP4SubpicObjectType);
+
+    SetTrackIntegerProperty(trackId,
+                            "mdia.minf.stbl.stsd.mp4s.esds.decConfigDescr.streamType",
+                            MP4NeroSubpicStreamType);
+    return trackId;
+}
+
+MP4TrackId MP4File::AddChapterTextTrack(MP4TrackId refTrackId, uint32_t timescale)
+{
+    // validate reference track id
+    (void)FindTrackIndex(refTrackId);
+
+    if (0 == timescale)
+    {
+        timescale = GetTrackTimeScale(refTrackId);
+    }
+
+    MP4TrackId trackId = AddTrack(MP4_TEXT_TRACK_TYPE, timescale);
+
+    (void)InsertChildAtom(MakeTrackName(trackId, "mdia.minf"), "gmhd", 0);
+
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd"), "text");
+
+    // stsd is a unique beast in that it has a count of the number
+    // of child atoms that needs to be incremented after we add the text atom
+    MP4Integer32Property* pStsdCountProperty;
+    FindIntegerProperty(
+        MakeTrackName(trackId, "mdia.minf.stbl.stsd.entryCount"),
+        (MP4Property**)&pStsdCountProperty);
+    pStsdCountProperty->IncrementValue();
+
+    // add a "text" atom to the generic media header
+    // this is different to the stsd "text" atom added above
+    // truth be told, it's not clear what this second "text" atom does,
+    // but all iTunes Store movies (with chapter markers) have it,
+    // as do all movies with chapter tracks made by hand in QuickTime Pro
+    (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.gmhd"), "text");
+
+    // disable the chapter text track
+    // it won't display anyway, as it has zero display size,
+    // but nonetheless it's good to disable it
+    // the track still operates as a chapter track when disabled
+    MP4Atom *pTkhdAtom = FindAtom(MakeTrackName(trackId, "tkhd"));
+    if (pTkhdAtom) {
+        pTkhdAtom->SetFlags(0xE);
+    }
+
+    // add a "chapter" track reference to our reference track,
+    // pointing to this new chapter track
+    (void)AddDescendantAtoms(MakeTrackName(refTrackId, NULL), "tref.chap");
+    AddTrackReference(MakeTrackName(refTrackId, "tref.chap"), trackId);
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddPixelAspectRatio(MP4TrackId trackId, uint32_t hSpacing, uint32_t vSpacing)
+{
+    // validate reference track id
+    (void)FindTrackIndex(trackId);
+    const char *format = GetTrackMediaDataName (trackId);
+
+    if (!strcasecmp(format, "avc1"))
+    {
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1"), "pasp");
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.pasp.hSpacing", hSpacing);
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.pasp.vSpacing", vSpacing);
+    }
+    else if (!strcasecmp(format, "mp4v"))
+    {
+        (void)AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.mp4v"), "pasp");
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.pasp.hSpacing", hSpacing);
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.pasp.vSpacing", vSpacing);
+    }
+
+    return trackId;
+}
+
+MP4TrackId MP4File::AddColr(MP4TrackId trackId,
+                            uint16_t primariesIndex,
+                            uint16_t transferFunctionIndex,
+                            uint16_t matrixIndex)
+{
+    // validate reference track id
+    (void)FindTrackIndex(trackId);
+    const char *format = GetTrackMediaDataName (trackId);
+
+    if (!strcasecmp(format, "avc1"))
+    {
+        AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1"), "colr");
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.colr.primariesIndex", primariesIndex);
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.colr.transferFunctionIndex", transferFunctionIndex);
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.avc1.colr.matrixIndex", matrixIndex);
+    }
+    else if (!strcasecmp(format, "mp4v"))
+    {
+        AddChildAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.mp4v"), "colr");
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.colr.primariesIndex", primariesIndex);
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.colr.transferFunctionIndex", transferFunctionIndex);
+        SetTrackIntegerProperty(trackId, "mdia.minf.stbl.stsd.mp4v.colr.matrixIndex", matrixIndex);
+    }
+
+    return trackId;
+}
+
+
+void MP4File::AddChapter(MP4TrackId chapterTrackId, MP4Duration chapterDuration, const char *chapterTitle)
+{
+    if (MP4_INVALID_TRACK_ID == chapterTrackId)
+    {
+        throw new Exception("No chapter track given",__FILE__, __LINE__, __FUNCTION__);
+    }
+
+    uint32_t sampleLength = 0;
+    uint8_t  sample[1040] = {0};
+    int textLen = 0;
+    char *text = (char *)&(sample[2]);
+
+    if(chapterTitle != NULL)
+    {
+        textLen = min((uint32_t)strlen(chapterTitle), (uint32_t)MP4V2_CHAPTER_TITLE_MAX);
+        if (0 < textLen)
+        {
+            strncpy(text, chapterTitle, textLen);
+        }
+    }
+    else
+    {
+        MP4Track * pChapterTrack = GetTrack(chapterTrackId);
+        snprintf( text, 1023, "Chapter %03d", pChapterTrack->GetNumberOfSamples() + 1 );
+        textLen = (uint32_t)strlen(text);
+    }
+
+    sampleLength = textLen + 2 + 12; // Account for text length code and other marker
+
+    // 2-byte length marker
+    sample[0] = (textLen >> 8) & 0xff;
+    sample[1] = textLen & 0xff;
+
+    int x = 2 + textLen;
+
+    // Modifier Length Marker
+    sample[x] = 0x00;
+    sample[x+1] = 0x00;
+    sample[x+2] = 0x00;
+    sample[x+3] = 0x0C;
+
+    // Modifier Type Code
+    sample[x+4] = 'e';
+    sample[x+5] = 'n';
+    sample[x+6] = 'c';
+    sample[x+7] = 'd';
+
+    // Modifier Value
+    sample[x+8] = 0x00;
+    sample[x+9] = 0x00;
+    sample[x+10] = (256 >> 8) & 0xff;
+    sample[x+11] = 256 & 0xff;
+
+    WriteSample(chapterTrackId, sample, sampleLength, chapterDuration);
+}
+
+void MP4File::AddNeroChapter(MP4Timestamp chapterStart, const char * chapterTitle)
+{
+    MP4Atom * pChpl = FindAtom("moov.udta.chpl");
+    if (!pChpl)
+    {
+        pChpl = AddDescendantAtoms("", "moov.udta.chpl");
+    }
+
+    MP4Integer32Property * pCount = (MP4Integer32Property*)pChpl->GetProperty(3);
+    pCount->IncrementValue();
+
+    char buffer[256];
+
+    if (0 == chapterTitle)
+    {
+        snprintf( buffer, 255, "Chapter %03d", pCount->GetValue() );
+    }
+    else
+    {
+        int len = min((uint32_t)strlen(chapterTitle), (uint32_t)255);
+        strncpy( buffer, chapterTitle, len );
+        buffer[len] = 0;
+    }
+
+    MP4TableProperty * pTable;
+    if (pChpl->FindProperty("chpl.chapters", (MP4Property **)&pTable))
+    {
+        MP4Integer64Property * pStartTime = (MP4Integer64Property *) pTable->GetProperty(0);
+        MP4StringProperty * pName = (MP4StringProperty *) pTable->GetProperty(1);
+        if (pStartTime && pTable)
+        {
+            pStartTime->AddValue(chapterStart);
+            pName->AddValue(buffer);
+        }
+    }
+}
+
+MP4TrackId MP4File::FindChapterReferenceTrack(MP4TrackId chapterTrackId, char * trackName, int trackNameSize)
+{
+    for (uint32_t i = 0; i < m_pTracks.Size(); i++)
+    {
+        if( MP4_IS_VIDEO_TRACK_TYPE( m_pTracks[i]->GetType() ) ||
+            MP4_IS_AUDIO_TRACK_TYPE( m_pTracks[i]->GetType() ) )
+        {
+            MP4TrackId refTrackId = m_pTracks[i]->GetId();
+            char *name = MakeTrackName(refTrackId, "tref.chap");
+            if( FindTrackReference( name, chapterTrackId ) )
+            {
+                if( 0 != trackName )
+                {
+                    int nameLen = min((uint32_t)strlen(name), (uint32_t)trackNameSize);
+                    strncpy(trackName, name, nameLen);
+                    trackName[nameLen] = 0;
+                }
+
+                return m_pTracks[i]->GetId();
+            }
+        }
+    }
+
+    return MP4_INVALID_TRACK_ID;
+}
+
+MP4TrackId MP4File::FindChapterTrack(char * trackName, int trackNameSize)
+{
+    for (uint32_t i = 0; i < m_pTracks.Size(); i++)
+    {
+        if( !strcasecmp(MP4_TEXT_TRACK_TYPE, m_pTracks[i]->GetType()) )
+        {
+            MP4TrackId refTrackId = FindChapterReferenceTrack(m_pTracks[i]->GetId(), trackName, trackNameSize);
+            if (MP4_INVALID_TRACK_ID != refTrackId)
+            {
+                return m_pTracks[i]->GetId();
+            }
+        }
+    }
+
+    return MP4_INVALID_TRACK_ID;
+}
+
+MP4ChapterType MP4File::DeleteChapters(MP4ChapterType chapterType, MP4TrackId chapterTrackId)
+{
+    MP4ChapterType deletedType = MP4ChapterTypeNone;
+
+    if (MP4ChapterTypeAny == chapterType || MP4ChapterTypeNero == chapterType)
+    {
+        MP4Atom * pChpl = FindAtom("moov.udta.chpl");
+        if (pChpl)
+        {
+            MP4Atom * pParent = pChpl->GetParentAtom();
+            pParent->DeleteChildAtom(pChpl);
+            deletedType = MP4ChapterTypeNero;
+        }
+    }
+
+    if (MP4ChapterTypeAny == chapterType || MP4ChapterTypeQt == chapterType)
+    {
+        char trackName[128] = {0};
+
+        // no text track given, find a suitable
+        if (MP4_INVALID_TRACK_ID == chapterTrackId)
+        {
+            chapterTrackId = FindChapterTrack(trackName, 127);
+        }
+
+        if (MP4_INVALID_TRACK_ID != chapterTrackId)
+        {
+            FindChapterReferenceTrack(chapterTrackId, trackName, 127);
+        }
+
+        if (MP4_INVALID_TRACK_ID != chapterTrackId && 0 != trackName[0])
+        {
+            // remove the reference
+            MP4Atom * pChap = FindAtom( trackName );
+            if( pChap )
+            {
+                MP4Atom * pTref = pChap->GetParentAtom();
+                if( pTref )
+                {
+                    pTref->DeleteChildAtom( pChap );
+
+                    MP4Atom* pParent = pTref->GetParentAtom();
+                    pParent->DeleteChildAtom( pTref );
+                }
+            }
+
+            // remove the chapter track
+            DeleteTrack(chapterTrackId);
+            deletedType = MP4ChapterTypeNone == deletedType ? MP4ChapterTypeQt : MP4ChapterTypeAny;
+        }
+    }
+    return deletedType;
+}
+
+MP4ChapterType MP4File::GetChapters(MP4Chapter_t ** chapterList, uint32_t * chapterCount, MP4ChapterType fromChapterType)
+{
+    *chapterList = 0;
+    *chapterCount = 0;
+
+    if (MP4ChapterTypeAny == fromChapterType || MP4ChapterTypeQt == fromChapterType)
+    {
+        uint8_t * sample = 0;
+        uint32_t sampleSize = 0;
+        MP4Timestamp startTime = 0;
+        MP4Duration duration = 0;
+
+        // get the chapter track
+        MP4TrackId chapterTrackId = FindChapterTrack();
+        if (MP4_INVALID_TRACK_ID == chapterTrackId)
+        {
+            if (MP4ChapterTypeQt == fromChapterType)
+            {
+                return MP4ChapterTypeNone;
+            }
+        }
+        else
+        {
+            // get infos about the chapters
+            MP4Track * pChapterTrack = GetTrack(chapterTrackId);
+            uint32_t counter = pChapterTrack->GetNumberOfSamples();
+
+            if (0 < counter)
+            {
+                uint32_t timescale = pChapterTrack->GetTimeScale();
+                MP4Chapter_t * chapters = (MP4Chapter_t*)MP4Malloc(sizeof(MP4Chapter_t) * counter);
+
+                // process all chapter sample
+                for (uint32_t i = 0; i < counter; ++i)
+                {
+                    // get the sample corresponding to the starttime
+                    MP4SampleId sampleId = pChapterTrack->GetSampleIdFromTime(startTime + duration, true);
+                    pChapterTrack->ReadSample(sampleId, &sample, &sampleSize);
+
+                    // get the starttime and duration
+                    pChapterTrack->GetSampleTimes(sampleId, &startTime, &duration);
+
+                    // we know that sample+2 contains the title (sample[0] and sample[1] is the length)
+                    const char * title = (const char *)&(sample[2]);
+                    int titleLen = min((uint32_t)((sample[0] << 8) | sample[1]), (uint32_t)MP4V2_CHAPTER_TITLE_MAX);
+                    strncpy(chapters[i].title, title, titleLen);
+                    chapters[i].title[titleLen] = 0;
+
+                    // write the duration (in milliseconds)
+                    chapters[i].duration = MP4ConvertTime(duration, timescale, MP4_MILLISECONDS_TIME_SCALE);
+
+                    // we're done with this sample
+                    MP4Free(sample);
+                    sample = 0;
+                }
+
+                *chapterList = chapters;
+                *chapterCount = counter;
+
+                // we got chapters so we are done
+                return MP4ChapterTypeQt;
+            }
+        }
+    }
+
+    if (MP4ChapterTypeAny == fromChapterType || MP4ChapterTypeNero == fromChapterType)
+    {
+        MP4Atom * pChpl = FindAtom("moov.udta.chpl");
+        if (!pChpl)
+        {
+            return MP4ChapterTypeNone;
+        }
+
+        MP4Integer32Property * pCounter = 0;
+        if (!pChpl->FindProperty("chpl.chaptercount", (MP4Property **)&pCounter))
+        {
+            log.warningf("%s: \"%s\": Nero chapter count does not exist",
+                         __FUNCTION__, GetFilename().c_str());
+            return MP4ChapterTypeNone;
+        }
+
+        uint32_t counter = pCounter->GetValue();
+        if (0 == counter)
+        {
+            log.warningf("%s: \"%s\": No Nero chapters available",
+                         __FUNCTION__, GetFilename().c_str());
+            return MP4ChapterTypeNone;
+        }
+
+        MP4TableProperty * pTable = 0;
+        MP4Integer64Property * pStartTime = 0;
+        MP4StringProperty * pName = 0;
+        MP4Duration chapterDurationSum = 0;
+        const char * name = 0;
+
+        if (!pChpl->FindProperty("chpl.chapters", (MP4Property **)&pTable))
+        {
+            log.warningf("%s: \"%s\": Nero chapter list does not exist",
+                         __FUNCTION__, GetFilename().c_str());
+            return MP4ChapterTypeNone;
+        }
+
+        if (0 == (pStartTime = (MP4Integer64Property *) pTable->GetProperty(0)))
+        {
+            log.warningf("%s: \"%s\": List of Chapter starttimes does not exist",
+                         __FUNCTION__, GetFilename().c_str());
+            return MP4ChapterTypeNone;
+        }
+        if (0 == (pName = (MP4StringProperty *) pTable->GetProperty(1)))
+        {
+            log.warningf("%s: \"%s\": List of Chapter titles does not exist",
+                         __FUNCTION__, GetFilename().c_str());
+            return MP4ChapterTypeNone;
+        }
+
+        MP4Chapter_t * chapters = (MP4Chapter_t*)MP4Malloc(sizeof(MP4Chapter_t) * counter);
+
+        // get the name of the first chapter
+        name = pName->GetValue();
+
+        // process remaining chapters
+        uint32_t i, j;
+        for (i = 0, j = 1; i < counter; ++i, ++j)
+        {
+            // insert the chapter title
+            uint32_t len = min((uint32_t)strlen(name), (uint32_t)MP4V2_CHAPTER_TITLE_MAX);
+            strncpy(chapters[i].title, name, len);
+            chapters[i].title[len] = 0;
+
+            // calculate the duration
+            MP4Duration duration = 0;
+            if (j < counter)
+            {
+                duration = MP4ConvertTime(pStartTime->GetValue(j),
+                                          (MP4_NANOSECONDS_TIME_SCALE / 100),
+                                          MP4_MILLISECONDS_TIME_SCALE) - chapterDurationSum;
+
+                // now get the name of the chapter (to be written next)
+                name = pName->GetValue(j);
+            }
+            else
+            {
+                // last chapter
+                duration = MP4ConvertTime(GetDuration(), GetTimeScale(), MP4_MILLISECONDS_TIME_SCALE) - chapterDurationSum;
+            }
+
+            // sum up the chapter duration
+            chapterDurationSum += duration;
+
+            // insert the chapter duration
+            chapters[i].duration = duration;
+        }
+
+        *chapterList = chapters;
+        *chapterCount = counter;
+
+        return MP4ChapterTypeNero;
+    }
+
+    return MP4ChapterTypeNone;
+}
+
+MP4ChapterType MP4File::SetChapters(MP4Chapter_t * chapterList, uint32_t chapterCount, MP4ChapterType toChapterType)
+{
+    MP4ChapterType setType = MP4ChapterTypeNone;
+
+    // first remove any existing chapters
+    DeleteChapters(toChapterType, MP4_INVALID_TRACK_ID);
+
+    if( MP4ChapterTypeAny == toChapterType || MP4ChapterTypeNero == toChapterType )
+    {
+        MP4Duration duration = 0;
+        for( uint32_t i = 0; i < chapterCount; ++i )
+        {
+            AddNeroChapter(duration, chapterList[i].title);
+            duration += 10 * MP4_MILLISECONDS_TIME_SCALE * chapterList[i].duration;
+        }
+
+        setType = MP4ChapterTypeNero;
+    }
+
+    if (MP4ChapterTypeAny == toChapterType || MP4ChapterTypeQt == toChapterType)
+    {
+        // find the first video or audio track
+        MP4TrackId refTrack = MP4_INVALID_TRACK_ID;
+        for( uint32_t i = 0; i < m_pTracks.Size(); i++ )
+        {
+            if( MP4_IS_VIDEO_TRACK_TYPE( m_pTracks[i]->GetType() ) ||
+                MP4_IS_AUDIO_TRACK_TYPE( m_pTracks[i]->GetType() ) )
+            {
+                refTrack = m_pTracks[i]->GetId();
+                break;
+            }
+        }
+
+        if( refTrack == MP4_INVALID_TRACK_ID )
+        {
+            return setType;
+        }
+
+        // create the chapter track
+        MP4TrackId chapterTrack = AddChapterTextTrack(refTrack, MP4_MILLISECONDS_TIME_SCALE);
+
+        for( uint32_t i = 0 ; i < chapterCount; ++i )
+        {
+            // create and write the chapter track sample
+            AddChapter( chapterTrack, chapterList[i].duration, chapterList[i].title );
+        }
+
+        setType = MP4ChapterTypeNone == setType ? MP4ChapterTypeQt : MP4ChapterTypeAny;
+    }
+
+    return setType;
+}
+
+MP4ChapterType MP4File::ConvertChapters(MP4ChapterType toChapterType)
+{
+    MP4ChapterType sourceType = MP4ChapterTypeNone;
+    const char* errMsg = 0;
+
+    if( MP4ChapterTypeQt == toChapterType )
+    {
+        sourceType = MP4ChapterTypeNero;
+        errMsg = "Could not find Nero chapter markers";
+    }
+    else if( MP4ChapterTypeNero == toChapterType )
+    {
+        sourceType = MP4ChapterTypeQt;
+        errMsg = "Could not find QuickTime chapter markers";
+    }
+    else
+    {
+        return MP4ChapterTypeNone;
+    }
+
+    MP4Chapter_t * chapters = 0;
+    uint32_t chapterCount = 0;
+
+    GetChapters(&chapters, &chapterCount, sourceType);
+    if (0 == chapterCount)
+    {
+        log.warningf("%s: \"%s\": %s", __FUNCTION__, GetFilename().c_str(),
+                     errMsg);
+        return MP4ChapterTypeNone;
+    }
+
+    SetChapters(chapters, chapterCount, toChapterType);
+
+    MP4Free(chapters);
+
+    return toChapterType;
+}
+
+void MP4File::ChangeMovieTimeScale(uint32_t timescale)
+{
+    uint32_t origTimeScale = GetTimeScale();
+    if (timescale == origTimeScale) {
+        // already done
+        return;
+    }
+
+    MP4Duration movieDuration = GetDuration();
+
+    // set movie header timescale and duration
+    SetTimeScale(timescale);
+    SetDuration(MP4ConvertTime(movieDuration, origTimeScale, timescale));
+
+    // set track header duration (calculated with movie header timescale)
+    uint32_t trackCount = GetNumberOfTracks();
+    for (uint32_t i = 0; i < trackCount; ++i)
+    {
+        MP4Track * track = GetTrack(FindTrackId(i));
+        MP4Atom & trackAtom = track->GetTrakAtom();
+        MP4IntegerProperty * duration;
+
+        if (trackAtom.FindProperty("trak.tkhd.duration", (MP4Property**)&duration))
+        {
+            duration->SetValue(MP4ConvertTime(duration->GetValue(), origTimeScale, timescale));
+        }
+    }
+}
+
+void MP4File::DeleteTrack(MP4TrackId trackId)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    uint32_t trakIndex = FindTrakAtomIndex(trackId);
+    uint16_t trackIndex = FindTrackIndex(trackId);
+    MP4Track* pTrack = m_pTracks[trackIndex];
+
+    MP4Atom& trakAtom = pTrack->GetTrakAtom();
+
+    MP4Atom* pMoovAtom = FindAtom("moov");
+    ASSERT(pMoovAtom);
+
+    RemoveTrackFromIod(trackId, ShallHaveIods());
+    RemoveTrackFromOd(trackId);
+
+    if (trackId == m_odTrackId) {
+        m_odTrackId = 0;
+    }
+
+    pMoovAtom->DeleteChildAtom(&trakAtom);
+
+    m_trakIds.Delete(trakIndex);
+
+    m_pTracks.Delete(trackIndex);
+
+    delete pTrack;
+    delete &trakAtom;
+}
+
+uint32_t MP4File::GetNumberOfTracks(const char* type, uint8_t subType)
+{
+    if (type == NULL) {
+        return m_pTracks.Size();
+    }
+
+    uint32_t typeSeen = 0;
+    const char* normType = MP4NormalizeTrackType(type);
+
+    for (uint32_t i = 0; i < m_pTracks.Size(); i++) {
+        if (!strcmp(normType, m_pTracks[i]->GetType())) {
+            if (subType) {
+                if (strcmp(normType, MP4_AUDIO_TRACK_TYPE) == 0) {
+                    if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
+                        continue;
+                    }
+                } else if (strcmp(normType, MP4_VIDEO_TRACK_TYPE)) {
+                    if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
+                        continue;
+                    }
+                }
+                // else unknown subtype, ignore it
+            }
+            typeSeen++;
+        }
+    }
+    return typeSeen;
+}
+
+MP4TrackId MP4File::AllocTrackId()
+{
+    MP4TrackId trackId =
+        GetIntegerProperty("moov.mvhd.nextTrackId");
+
+    if (trackId <= 0xFFFF) {
+        // check that nextTrackid is correct
+        try {
+            (void)FindTrackIndex(trackId);
+            // ERROR, this trackId is in use
+        }
+        catch (Exception* x) {
+            // OK, this trackId is not in use, proceed
+            delete x;
+            SetIntegerProperty("moov.mvhd.nextTrackId", trackId + 1);
+            return trackId;
+        }
+    }
+
+    // we need to search for a track id
+    for (trackId = 1; trackId <= 0xFFFF; trackId++) {
+        try {
+            (void)FindTrackIndex(trackId);
+            // KEEP LOOKING, this trackId is in use
+        }
+        catch (Exception* x) {
+            // OK, this trackId is not in use, proceed
+            delete x;
+            return trackId;
+        }
+    }
+
+    // extreme case where mp4 file has 2^16 tracks in it
+    throw new Exception("too many existing tracks", __FILE__, __LINE__, __FUNCTION__);
+    return MP4_INVALID_TRACK_ID;        // to keep MSVC happy
+}
+
+MP4TrackId MP4File::FindTrackId(uint16_t trackIndex,
+                                const char* type, uint8_t subType)
+{
+    if (type == NULL) {
+        return m_pTracks[trackIndex]->GetId();
+    }
+
+    uint32_t typeSeen = 0;
+    const char* normType = MP4NormalizeTrackType(type);
+
+    for (uint32_t i = 0; i < m_pTracks.Size(); i++) {
+        if (!strcmp(normType, m_pTracks[i]->GetType())) {
+            if (subType) {
+                if (strcmp(normType, MP4_AUDIO_TRACK_TYPE) == 0) {
+                    if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
+                        continue;
+                    }
+                } else if (strcmp(normType, MP4_VIDEO_TRACK_TYPE) == 0) {
+                    if (subType != GetTrackEsdsObjectTypeId(m_pTracks[i]->GetId())) {
+                        continue;
+                    }
+                }
+                // else unknown subtype, ignore it
+            }
+
+            if (trackIndex == typeSeen) {
+                return m_pTracks[i]->GetId();
+            }
+
+            typeSeen++;
+        }
+    }
+
+    ostringstream msg;
+    msg << "Track index doesn't exist - track " << trackIndex << " type " << type;
+    throw new Exception(msg.str(),__FILE__, __LINE__, __FUNCTION__);
+    return MP4_INVALID_TRACK_ID; // satisfy MS compiler
+}
+
+uint16_t MP4File::FindTrackIndex(MP4TrackId trackId)
+{
+    for (uint32_t i = 0; i < m_pTracks.Size() && i <= 0xFFFF; i++) {
+        if (m_pTracks[i]->GetId() == trackId) {
+            return (uint16_t)i;
+        }
+    }
+
+    ostringstream msg;
+    msg << "Track id " << trackId << " doesn't exist";
+    throw new Exception(msg.str(),__FILE__, __LINE__, __FUNCTION__);
+    return (uint16_t)-1; // satisfy MS compiler
+}
+
+uint16_t MP4File::FindTrakAtomIndex(MP4TrackId trackId)
+{
+    if (trackId) {
+        for (uint32_t i = 0; i < m_trakIds.Size(); i++) {
+            if (m_trakIds[i] == trackId) {
+                return i;
+            }
+        }
+    }
+
+    ostringstream msg;
+    msg << "Track id " << trackId << " doesn't exist";
+    throw new Exception(msg.str(),__FILE__, __LINE__, __FUNCTION__);
+    return (uint16_t)-1; // satisfy MS compiler
+}
+
+uint32_t MP4File::GetSampleSize(MP4TrackId trackId, MP4SampleId sampleId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetSampleSize(sampleId);
+}
+
+uint32_t MP4File::GetTrackMaxSampleSize(MP4TrackId trackId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetMaxSampleSize();
+}
+
+MP4SampleId MP4File::GetSampleIdFromTime(MP4TrackId trackId,
+        MP4Timestamp when, bool wantSyncSample)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->
+           GetSampleIdFromTime(when, wantSyncSample);
+}
+
+MP4Timestamp MP4File::GetSampleTime(
+    MP4TrackId trackId, MP4SampleId sampleId)
+{
+    MP4Timestamp timestamp;
+    m_pTracks[FindTrackIndex(trackId)]->
+    GetSampleTimes(sampleId, &timestamp, NULL);
+    return timestamp;
+}
+
+MP4Duration MP4File::GetSampleDuration(
+    MP4TrackId trackId, MP4SampleId sampleId)
+{
+    MP4Duration duration;
+    m_pTracks[FindTrackIndex(trackId)]->
+    GetSampleTimes(sampleId, NULL, &duration);
+    return duration;
+}
+
+MP4Duration MP4File::GetSampleRenderingOffset(
+    MP4TrackId trackId, MP4SampleId sampleId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->
+           GetSampleRenderingOffset(sampleId);
+}
+
+bool MP4File::GetSampleSync(MP4TrackId trackId, MP4SampleId sampleId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->IsSyncSample(sampleId);
+}
+
+void MP4File::ReadSample(
+    MP4TrackId    trackId,
+    MP4SampleId   sampleId,
+    uint8_t**     ppBytes,
+    uint32_t*     pNumBytes,
+    MP4Timestamp* pStartTime,
+    MP4Duration*  pDuration,
+    MP4Duration*  pRenderingOffset,
+    bool*         pIsSyncSample,
+    bool*         hasDependencyFlags,
+    uint32_t*     dependencyFlags )
+{
+    m_pTracks[FindTrackIndex(trackId)]->ReadSample(
+        sampleId,
+        ppBytes,
+        pNumBytes,
+        pStartTime,
+        pDuration,
+        pRenderingOffset,
+        pIsSyncSample,
+        hasDependencyFlags,
+        dependencyFlags );
+}
+
+void MP4File::WriteSample(
+    MP4TrackId     trackId,
+    const uint8_t* pBytes,
+    uint32_t       numBytes,
+    MP4Duration    duration,
+    MP4Duration    renderingOffset,
+    bool           isSyncSample )
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+    m_pTracks[FindTrackIndex(trackId)]->WriteSample(
+        pBytes, numBytes, duration, renderingOffset, isSyncSample );
+    m_pModificationProperty->SetValue( MP4GetAbsTimestamp() );
+}
+
+void MP4File::WriteSampleDependency(
+    MP4TrackId     trackId,
+    const uint8_t* pBytes, 
+    uint32_t       numBytes,
+    MP4Duration    duration,
+    MP4Duration    renderingOffset,
+    bool           isSyncSample,
+    uint32_t       dependencyFlags )
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+    m_pTracks[FindTrackIndex(trackId)]->WriteSampleDependency(
+        pBytes, numBytes, duration, renderingOffset, isSyncSample, dependencyFlags );
+    m_pModificationProperty->SetValue( MP4GetAbsTimestamp() );
+}
+
+void MP4File::SetSampleRenderingOffset(MP4TrackId trackId,
+                                       MP4SampleId sampleId, MP4Duration renderingOffset)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+    m_pTracks[FindTrackIndex(trackId)]->
+    SetSampleRenderingOffset(sampleId, renderingOffset);
+
+    m_pModificationProperty->SetValue(MP4GetAbsTimestamp());
+}
+
+void MP4File::MakeFtypAtom(
+    char*    majorBrand,
+    uint32_t minorVersion,
+    char**   compatibleBrands,
+    uint32_t compatibleBrandsCount)
+{
+    MP4FtypAtom* ftyp = (MP4FtypAtom*)m_pRootAtom->FindAtom( "ftyp" );
+    if (ftyp == NULL)
+        ftyp = (MP4FtypAtom*)InsertChildAtom( m_pRootAtom, "ftyp", 0 );
+
+    // bail if majorbrand is not specified; defaults suffice.
+    if (majorBrand == NULL)
+        return;
+
+    ftyp->majorBrand.SetValue( majorBrand );
+    ftyp->minorVersion.SetValue( minorVersion );
+
+    ftyp->compatibleBrands.SetCount( compatibleBrandsCount );
+    for( uint32_t i = 0; i < compatibleBrandsCount; i++ )
+        ftyp->compatibleBrands.SetValue( compatibleBrands[i], i );
+}
+
+char* MP4File::MakeTrackName(MP4TrackId trackId, const char* name)
+{
+    uint16_t trakIndex = FindTrakAtomIndex(trackId);
+
+    if (name == NULL || name[0] == '\0') {
+        snprintf(m_trakName, sizeof(m_trakName),
+                 "moov.trak[%u]", trakIndex);
+    } else {
+        snprintf(m_trakName, sizeof(m_trakName),
+                 "moov.trak[%u].%s", trakIndex, name);
+    }
+    return m_trakName;
+}
+
+MP4Atom *MP4File::FindTrackAtom (MP4TrackId trackId, const char *name)
+{
+    return FindAtom(MakeTrackName(trackId, name));
+}
+
+uint64_t MP4File::GetTrackIntegerProperty(MP4TrackId trackId, const char* name)
+{
+    return GetIntegerProperty(MakeTrackName(trackId, name));
+}
+
+void MP4File::SetTrackIntegerProperty(MP4TrackId trackId, const char* name,
+                                      int64_t value)
+{
+    SetIntegerProperty(MakeTrackName(trackId, name), value);
+}
+
+float MP4File::GetTrackFloatProperty(MP4TrackId trackId, const char* name)
+{
+    return GetFloatProperty(MakeTrackName(trackId, name));
+}
+
+void MP4File::SetTrackFloatProperty(MP4TrackId trackId, const char* name,
+                                    float value)
+{
+    SetFloatProperty(MakeTrackName(trackId, name), value);
+}
+
+const char* MP4File::GetTrackStringProperty(MP4TrackId trackId, const char* name)
+{
+    return GetStringProperty(MakeTrackName(trackId, name));
+}
+
+void MP4File::SetTrackStringProperty(MP4TrackId trackId, const char* name,
+                                     const char* value)
+{
+    SetStringProperty(MakeTrackName(trackId, name), value);
+}
+
+void MP4File::GetTrackBytesProperty(MP4TrackId trackId, const char* name,
+                                    uint8_t** ppValue, uint32_t* pValueSize)
+{
+    GetBytesProperty(MakeTrackName(trackId, name), ppValue, pValueSize);
+}
+
+void MP4File::SetTrackBytesProperty(MP4TrackId trackId, const char* name,
+                                    const uint8_t* pValue, uint32_t valueSize)
+{
+    SetBytesProperty(MakeTrackName(trackId, name), pValue, valueSize);
+}
+
+bool MP4File::GetTrackLanguage( MP4TrackId trackId, char* code )
+{
+    ostringstream oss;
+    oss << "moov.trak[" << FindTrakAtomIndex(trackId) << "].mdia.mdhd.language";
+
+    MP4Property* prop;
+    if( !m_pRootAtom->FindProperty( oss.str().c_str(), &prop ))
+        return false;
+
+    if( prop->GetType() != LanguageCodeProperty )
+        return false;
+
+    MP4LanguageCodeProperty& lang = *static_cast<MP4LanguageCodeProperty*>(prop);
+    string slang;
+    bmff::enumLanguageCode.toString( lang.GetValue(), slang );
+    if( slang.length() != 3 ) {
+        memset( code, '\0', 4 );
+    }
+    else {
+        memcpy( code, slang.c_str(), 3 );
+        code[3] = '\0';
+    }
+
+    return true;
+}
+
+bool MP4File::SetTrackLanguage( MP4TrackId trackId, const char* code )
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    ostringstream oss;
+    oss << "moov.trak[" << FindTrakAtomIndex(trackId) << "].mdia.mdhd.language";
+
+    MP4Property* prop;
+    if( !m_pRootAtom->FindProperty( oss.str().c_str(), &prop ))
+        return false;
+
+    if( prop->GetType() != LanguageCodeProperty )
+        return false;
+
+    MP4LanguageCodeProperty& lang = *static_cast<MP4LanguageCodeProperty*>(prop);
+    lang.SetValue( bmff::enumLanguageCode.toType( code ));
+
+    return true;
+}
+
+    
+bool MP4File::GetTrackName( MP4TrackId trackId, char** name )
+{
+    unsigned char *val = NULL;
+    uint32_t valSize = 0;
+    MP4Atom *pMetaAtom;
+
+    pMetaAtom = m_pRootAtom->FindAtom(MakeTrackName(trackId,"udta.name"));
+
+    if (pMetaAtom)
+    {
+        GetBytesProperty(MakeTrackName(trackId,"udta.name.value"), (uint8_t**)&val, &valSize);
+    }
+    if (valSize > 0)
+    {
+        *name = (char*)malloc((valSize+1)*sizeof(char));
+        if (*name == NULL) {
+            free(val);
+            return false;
+        }
+        memcpy(*name, val, valSize*sizeof(unsigned char));
+        free(val);
+        (*name)[valSize] = '\0';
+        return true;
+    }
+
+    return false;
+}
+
+bool MP4File::SetTrackName( MP4TrackId trackId, const char* name )
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+    char atomstring[40];
+    MP4Atom *pMetaAtom;
+    MP4BytesProperty *pMetadataProperty = NULL;
+    snprintf(atomstring, 40, "%s", MakeTrackName(trackId,"udta.name"));
+
+    pMetaAtom = m_pRootAtom->FindAtom(atomstring);
+
+    if (!pMetaAtom)
+    {
+        if (!AddDescendantAtoms(MakeTrackName(trackId, NULL), "udta.name"))
+            return false;
+        
+        pMetaAtom = m_pRootAtom->FindAtom(atomstring);
+        if (pMetaAtom == NULL) return false;
+    }
+
+    ASSERT(pMetaAtom->FindProperty("name.value",
+                                   (MP4Property**)&pMetadataProperty));
+    ASSERT(pMetadataProperty);
+
+    pMetadataProperty->SetValue((uint8_t*)name, (uint32_t)strlen(name));
+
+    return true;
+}
+
+// file level convenience functions
+
+MP4Duration MP4File::GetDuration()
+{
+    return m_pDurationProperty->GetValue();
+}
+
+void MP4File::SetDuration(MP4Duration value)
+{
+    m_pDurationProperty->SetValue(value);
+}
+
+uint32_t MP4File::GetTimeScale()
+{
+    return m_pTimeScaleProperty->GetValue();
+}
+
+void MP4File::SetTimeScale(uint32_t value)
+{
+    if (value == 0) {
+        throw new Exception("invalid value", __FILE__, __LINE__, __FUNCTION__);
+    }
+    m_pTimeScaleProperty->SetValue(value);
+}
+
+uint8_t MP4File::GetODProfileLevel()
+{
+    return GetIntegerProperty("moov.iods.ODProfileLevelId");
+}
+
+void MP4File::SetODProfileLevel(uint8_t value)
+{
+    SetIntegerProperty("moov.iods.ODProfileLevelId", value);
+}
+
+uint8_t MP4File::GetSceneProfileLevel()
+{
+    return GetIntegerProperty("moov.iods.sceneProfileLevelId");
+}
+
+void MP4File::SetSceneProfileLevel(uint8_t value)
+{
+    SetIntegerProperty("moov.iods.sceneProfileLevelId", value);
+}
+
+uint8_t MP4File::GetVideoProfileLevel()
+{
+    return GetIntegerProperty("moov.iods.visualProfileLevelId");
+}
+
+void MP4File::SetVideoProfileLevel(uint8_t value)
+{
+    SetIntegerProperty("moov.iods.visualProfileLevelId", value);
+}
+
+uint8_t MP4File::GetAudioProfileLevel()
+{
+    return GetIntegerProperty("moov.iods.audioProfileLevelId");
+}
+
+void MP4File::SetAudioProfileLevel(uint8_t value)
+{
+    SetIntegerProperty("moov.iods.audioProfileLevelId", value);
+}
+
+uint8_t MP4File::GetGraphicsProfileLevel()
+{
+    return GetIntegerProperty("moov.iods.graphicsProfileLevelId");
+}
+
+void MP4File::SetGraphicsProfileLevel(uint8_t value)
+{
+    SetIntegerProperty("moov.iods.graphicsProfileLevelId", value);
+}
+
+const char* MP4File::GetSessionSdp()
+{
+    return GetStringProperty("moov.udta.hnti.rtp .sdpText");
+}
+
+void MP4File::SetSessionSdp(const char* sdpString)
+{
+    (void)AddDescendantAtoms("moov", "udta.hnti.rtp ");
+
+    SetStringProperty("moov.udta.hnti.rtp .sdpText", sdpString);
+}
+
+void MP4File::AppendSessionSdp(const char* sdpFragment)
+{
+    const char* oldSdpString = NULL;
+    try {
+        oldSdpString = GetSessionSdp();
+    }
+    catch (Exception* x) {
+        delete x;
+        SetSessionSdp(sdpFragment);
+        return;
+    }
+
+    char* newSdpString =
+        (char*)MP4Malloc(strlen(oldSdpString) + strlen(sdpFragment) + 1);
+    strcpy(newSdpString, oldSdpString);
+    strcat(newSdpString, sdpFragment);
+    SetSessionSdp(newSdpString);
+    MP4Free(newSdpString);
+}
+
+//
+// ismacrypt API - retrieve OriginalFormatBox
+//
+// parameters are assumed to have been sanity tested in mp4.cpp
+// don't call this unless media data name is 'encv',
+// results may otherwise be unpredictable.
+//
+// input:
+// trackID - valid encv track ID for this file
+// buflen  - length of oFormat, minimum is 5 (4cc plus null terminator)
+//
+// output:
+// oFormat - buffer to return null terminated string containing
+//           track original format
+// return:
+// 0       - original format returned OK
+// 1       - buffer length error or problem retrieving track property
+//
+//
+bool MP4File::GetTrackMediaDataOriginalFormat(MP4TrackId trackId,
+        char *originalFormat, uint32_t buflen)
+{
+    uint32_t format;
+
+    if (buflen < 5)
+        return false;
+
+    format = GetTrackIntegerProperty(trackId,
+                                     "mdia.minf.stbl.stsd.*.sinf.frma.data-format");
+
+    IDATOM(format, originalFormat);
+    return true;
+
+}
+
+
+// track level convenience functions
+
+MP4SampleId MP4File::GetTrackNumberOfSamples(MP4TrackId trackId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetNumberOfSamples();
+}
+
+const char* MP4File::GetTrackType(MP4TrackId trackId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetType();
+}
+
+const char *MP4File::GetTrackMediaDataName (MP4TrackId trackId)
+{
+    MP4Atom *pChild;
+    MP4Atom *pAtom =
+        FindAtom(MakeTrackName(trackId,
+                               "mdia.minf.stbl.stsd"));
+    if (pAtom->GetNumberOfChildAtoms() != 1) {
+        log.errorf("%s: \"%s\": track %d has more than 1 child atoms in stsd", 
+                   __FUNCTION__, GetFilename().c_str(), trackId);
+        return NULL;
+    }
+    pChild = pAtom->GetChildAtom(0);
+    return pChild->GetType();
+}
+
+
+uint32_t MP4File::GetTrackTimeScale(MP4TrackId trackId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetTimeScale();
+}
+
+void MP4File::SetTrackTimeScale(MP4TrackId trackId, uint32_t value)
+{
+    if (value == 0) {
+        throw new Exception("invalid value", __FILE__, __LINE__, __FUNCTION__);
+    }
+    SetTrackIntegerProperty(trackId, "mdia.mdhd.timeScale", value);
+}
+
+MP4Duration MP4File::GetTrackDuration(MP4TrackId trackId)
+{
+    return GetTrackIntegerProperty(trackId, "mdia.mdhd.duration");
+}
+
+uint8_t MP4File::GetTrackEsdsObjectTypeId(MP4TrackId trackId)
+{
+    // changed mp4a to * to handle enca case
+    try {
+        return GetTrackIntegerProperty(trackId,
+                                       "mdia.minf.stbl.stsd.*.esds.decConfigDescr.objectTypeId");
+    } catch (Exception *x) {
+        delete x;
+        return GetTrackIntegerProperty(trackId,
+                                       "mdia.minf.stbl.stsd.*.*.esds.decConfigDescr.objectTypeId");
+    }
+}
+
+uint8_t MP4File::GetTrackAudioMpeg4Type(MP4TrackId trackId)
+{
+    // verify that track is an MPEG-4 audio track
+    if (GetTrackEsdsObjectTypeId(trackId) != MP4_MPEG4_AUDIO_TYPE) {
+        return MP4_MPEG4_INVALID_AUDIO_TYPE;
+    }
+
+    uint8_t* pEsConfig = NULL;
+    uint32_t esConfigSize;
+
+    // The Mpeg4 audio type (AAC, CELP, HXVC, ...)
+    // is the first 5 bits of the ES configuration
+
+    GetTrackESConfiguration(trackId, &pEsConfig, &esConfigSize);
+
+    if (esConfigSize < 1) {
+        free(pEsConfig);
+        return MP4_MPEG4_INVALID_AUDIO_TYPE;
+    }
+
+    uint8_t mpeg4Type = ((pEsConfig[0] >> 3) & 0x1f);
+    // TTTT TXXX XXX  potentially 6 bits of extension.
+    if (mpeg4Type == 0x1f) {
+        if (esConfigSize < 2) {
+            free(pEsConfig);
+            return MP4_MPEG4_INVALID_AUDIO_TYPE;
+        }
+        mpeg4Type = 32 +
+                    (((pEsConfig[0] & 0x7) << 3) | ((pEsConfig[1] >> 5) & 0x7));
+    }
+
+    free(pEsConfig);
+
+    return mpeg4Type;
+}
+
+
+MP4Duration MP4File::GetTrackFixedSampleDuration(MP4TrackId trackId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetFixedSampleDuration();
+}
+
+double MP4File::GetTrackVideoFrameRate(MP4TrackId trackId)
+{
+    MP4SampleId numSamples =
+        GetTrackNumberOfSamples(trackId);
+    uint64_t
+    msDuration =
+        ConvertFromTrackDuration(trackId,
+                                 GetTrackDuration(trackId), MP4_MSECS_TIME_SCALE);
+
+    if (msDuration == 0) {
+        return 0.0;
+    }
+
+    return ((double)numSamples / double(msDuration)) * MP4_MSECS_TIME_SCALE;
+}
+
+int MP4File::GetTrackAudioChannels (MP4TrackId trackId)
+{
+    return GetTrackIntegerProperty(trackId,
+                                   "mdia.minf.stbl.stsd.*[0].channels");
+}
+
+// true if media track encrypted according to ismacryp
+bool MP4File::IsIsmaCrypMediaTrack(MP4TrackId trackId)
+{
+    if (GetTrackIntegerProperty(trackId,
+                                "mdia.minf.stbl.stsd.*.sinf.frma.data-format")
+            != (uint64_t)-1) {
+        return true;
+    }
+    return false;
+}
+
+bool MP4File::IsWriteMode()
+{
+    if( !m_file )
+        return false;
+
+    switch( m_file->mode ) {
+        case File::MODE_READ:
+            return false;
+
+        case File::MODE_MODIFY:
+        case File::MODE_CREATE:
+        default:
+            break;
+    }
+
+    return true;
+}
+
+void MP4File::GetTrackESConfiguration(MP4TrackId trackId,
+                                      uint8_t** ppConfig, uint32_t* pConfigSize)
+{
+    try {
+        GetTrackBytesProperty(trackId,
+                              "mdia.minf.stbl.stsd.*[0].esds.decConfigDescr.decSpecificInfo[0].info",
+                              ppConfig, pConfigSize);
+    } catch (Exception *x) {
+        delete x;
+        GetTrackBytesProperty(trackId,
+                              "mdia.minf.stbl.stsd.*[0].*.esds.decConfigDescr.decSpecificInfo[0].info",
+                              ppConfig, pConfigSize);
+    }
+}
+
+void MP4File::GetTrackVideoMetadata(MP4TrackId trackId,
+                                    uint8_t** ppConfig, uint32_t* pConfigSize)
+{
+    GetTrackBytesProperty(trackId,
+                          "mdia.minf.stbl.stsd.*[0].*.metadata",
+                          ppConfig, pConfigSize);
+}
+
+void MP4File::SetTrackESConfiguration(MP4TrackId trackId,
+                                      const uint8_t* pConfig, uint32_t configSize)
+{
+    // get a handle on the track decoder config descriptor
+    MP4DescriptorProperty* pConfigDescrProperty = NULL;
+    if (FindProperty(MakeTrackName(trackId,
+                                   "mdia.minf.stbl.stsd.*[0].esds.decConfigDescr.decSpecificInfo"),
+                     (MP4Property**)&pConfigDescrProperty) == false ||
+            pConfigDescrProperty == NULL) {
+        // probably trackId refers to a hint track
+        throw new Exception("no such property", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    // lookup the property to store the configuration
+    MP4BytesProperty* pInfoProperty = NULL;
+    (void)pConfigDescrProperty->FindProperty("decSpecificInfo[0].info",
+            (MP4Property**)&pInfoProperty);
+
+    // configuration being set for the first time
+    if (pInfoProperty == NULL) {
+        // need to create a new descriptor to hold it
+        MP4Descriptor* pConfigDescr =
+            pConfigDescrProperty->AddDescriptor(MP4DecSpecificDescrTag);
+        pConfigDescr->Generate();
+
+        (void)pConfigDescrProperty->FindProperty(
+            "decSpecificInfo[0].info",
+            (MP4Property**)&pInfoProperty);
+        ASSERT(pInfoProperty);
+    }
+
+    // set the value
+    pInfoProperty->SetValue(pConfig, configSize);
+}
+
+
+void MP4File::GetTrackH264SeqPictHeaders (MP4TrackId trackId,
+        uint8_t ***pppSeqHeader,
+        uint32_t **ppSeqHeaderSize,
+        uint8_t ***pppPictHeader,
+        uint32_t **ppPictHeaderSize)
+{
+    uint32_t count;
+    const char *format;
+    MP4Atom *avcCAtom;
+
+    *pppSeqHeader = NULL;
+    *pppPictHeader = NULL;
+    *ppSeqHeaderSize = NULL;
+    *ppPictHeaderSize = NULL;
+
+    // get 4cc media format - can be avc1 or encv for ismacrypted track
+    format = GetTrackMediaDataName (trackId);
+
+    if (!strcasecmp(format, "avc1"))
+        avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.avc1.avcC"));
+    else if (!strcasecmp(format, "encv"))
+        avcCAtom = FindAtom(MakeTrackName(trackId, "mdia.minf.stbl.stsd.encv.avcC"));
+    else
+        // huh?  unknown track format
+        return;
+
+    MP4BitfieldProperty *pSeqCount;
+    MP4IntegerProperty *pSeqLen, *pPictCount, *pPictLen;
+    MP4BytesProperty *pSeqVal, *pPictVal;
+
+    if ((avcCAtom->FindProperty("avcC.numOfSequenceParameterSets",
+                                (MP4Property **)&pSeqCount) == false) ||
+            (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetLength",
+                                    (MP4Property **)&pSeqLen) == false) ||
+            (avcCAtom->FindProperty("avcC.sequenceEntries.sequenceParameterSetNALUnit",
+                                    (MP4Property **)&pSeqVal) == false)) {
+        log.errorf("%s: \"%s\": Could not find avcC properties", __FUNCTION__, GetFilename().c_str());
+        return ;
+    }
+    uint8_t **ppSeqHeader =
+        (uint8_t **)malloc((pSeqCount->GetValue() + 1) * sizeof(uint8_t *));
+    if (ppSeqHeader == NULL) return;
+    *pppSeqHeader = ppSeqHeader;
+
+    uint32_t *pSeqHeaderSize =
+        (uint32_t *)malloc((pSeqCount->GetValue() + 1) * sizeof(uint32_t *));
+
+    if (pSeqHeaderSize == NULL) return;
+
+    *ppSeqHeaderSize = pSeqHeaderSize;
+    for (count = 0; count < pSeqCount->GetValue(); count++) {
+        pSeqVal->GetValue(&(ppSeqHeader[count]), &(pSeqHeaderSize[count]),
+                          count);
+    }
+    ppSeqHeader[count] = NULL;
+    pSeqHeaderSize[count] = 0;
+
+    if ((avcCAtom->FindProperty("avcC.numOfPictureParameterSets",
+                                (MP4Property **)&pPictCount) == false) ||
+            (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetLength",
+                                    (MP4Property **)&pPictLen) == false) ||
+            (avcCAtom->FindProperty("avcC.pictureEntries.pictureParameterSetNALUnit",
+                                    (MP4Property **)&pPictVal) == false)) {
+        log.errorf("%s: \"%s\": Could not find avcC picture table properties",
+                   __FUNCTION__, GetFilename().c_str());
+        return ;
+    }
+    uint8_t
+    **ppPictHeader =
+        (uint8_t **)malloc((pPictCount->GetValue() + 1) * sizeof(uint8_t *));
+    if (ppPictHeader == NULL) return;
+    uint32_t *pPictHeaderSize =
+        (uint32_t *)malloc((pPictCount->GetValue() + 1)* sizeof(uint32_t *));
+    if (pPictHeaderSize == NULL) {
+        free(ppPictHeader);
+        return;
+    }
+    *pppPictHeader = ppPictHeader;
+    *ppPictHeaderSize = pPictHeaderSize;
+
+    for (count = 0; count < pPictCount->GetValue(); count++) {
+        pPictVal->GetValue(&(ppPictHeader[count]), &(pPictHeaderSize[count]),
+                           count);
+    }
+    ppPictHeader[count] = NULL;
+    pPictHeaderSize[count] = 0;
+    return ;
+}
+
+
+
+const char* MP4File::GetHintTrackSdp(MP4TrackId hintTrackId)
+{
+    return GetTrackStringProperty(hintTrackId, "udta.hnti.sdp .sdpText");
+}
+
+void MP4File::SetHintTrackSdp(MP4TrackId hintTrackId, const char* sdpString)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    (void)AddDescendantAtoms(
+        MakeTrackName(hintTrackId, NULL), "udta.hnti.sdp ");
+
+    SetTrackStringProperty(hintTrackId, "udta.hnti.sdp .sdpText", sdpString);
+}
+
+void MP4File::AppendHintTrackSdp(MP4TrackId hintTrackId,
+                                 const char* sdpFragment)
+{
+    const char* oldSdpString = NULL;
+    try {
+        oldSdpString = GetHintTrackSdp(hintTrackId);
+    }
+    catch (Exception* x) {
+        delete x;
+        SetHintTrackSdp(hintTrackId, sdpFragment);
+        return;
+    }
+
+    char* newSdpString =
+        (char*)MP4Malloc(strlen(oldSdpString) + strlen(sdpFragment) + 1);
+    strcpy(newSdpString, oldSdpString);
+    strcat(newSdpString, sdpFragment);
+    SetHintTrackSdp(hintTrackId, newSdpString);
+    MP4Free(newSdpString);
+}
+
+void MP4File::GetHintTrackRtpPayload(
+    MP4TrackId hintTrackId,
+    char** ppPayloadName,
+    uint8_t* pPayloadNumber,
+    uint16_t* pMaxPayloadSize,
+    char **ppEncodingParams)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    ((MP4RtpHintTrack*)pTrack)->GetPayload(
+        ppPayloadName, pPayloadNumber, pMaxPayloadSize, ppEncodingParams);
+}
+
+void MP4File::SetHintTrackRtpPayload(MP4TrackId hintTrackId,
+                                     const char* payloadName, uint8_t* pPayloadNumber, uint16_t maxPayloadSize,
+                                     const char *encoding_params,
+                                     bool include_rtp_map,
+                                     bool include_mpeg4_esid)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    uint8_t payloadNumber;
+    if (pPayloadNumber && *pPayloadNumber != MP4_SET_DYNAMIC_PAYLOAD) {
+        payloadNumber = *pPayloadNumber;
+    } else {
+        payloadNumber = AllocRtpPayloadNumber();
+        if (pPayloadNumber) {
+            *pPayloadNumber = payloadNumber;
+        }
+    }
+
+    ((MP4RtpHintTrack*)pTrack)->SetPayload(
+        payloadName, payloadNumber, maxPayloadSize, encoding_params,
+        include_rtp_map, include_mpeg4_esid);
+}
+
+uint8_t MP4File::AllocRtpPayloadNumber()
+{
+    MP4Integer32Array usedPayloads;
+    uint32_t i;
+
+    // collect rtp payload numbers in use by existing tracks
+    for (i = 0; i < m_pTracks.Size(); i++) {
+        MP4Atom& trakAtom = m_pTracks[i]->GetTrakAtom();
+
+        MP4Integer32Property* pPayloadProperty = NULL;
+        if (trakAtom.FindProperty("trak.udta.hinf.payt.payloadNumber",
+                                    (MP4Property**)&pPayloadProperty) &&
+                pPayloadProperty) {
+            usedPayloads.Add(pPayloadProperty->GetValue());
+        }
+    }
+
+    // search dynamic payload range for an available slot
+    uint8_t payload;
+    for (payload = 96; payload < 128; payload++) {
+        for (i = 0; i < usedPayloads.Size(); i++) {
+            if (payload == usedPayloads[i]) {
+                break;
+            }
+        }
+        if (i == usedPayloads.Size()) {
+            break;
+        }
+    }
+
+    if (payload >= 128) {
+        throw new Exception("no more available rtp payload numbers",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    return payload;
+}
+
+MP4TrackId MP4File::GetHintTrackReferenceTrackId(
+    MP4TrackId hintTrackId)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    MP4Track* pRefTrack = ((MP4RtpHintTrack*)pTrack)->GetRefTrack();
+
+    if (pRefTrack == NULL) {
+        return MP4_INVALID_TRACK_ID;
+    }
+    return pRefTrack->GetId();
+}
+
+void MP4File::ReadRtpHint(
+    MP4TrackId hintTrackId,
+    MP4SampleId hintSampleId,
+    uint16_t* pNumPackets)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->
+    ReadHint(hintSampleId, pNumPackets);
+}
+
+uint16_t MP4File::GetRtpHintNumberOfPackets(
+    MP4TrackId hintTrackId)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    return ((MP4RtpHintTrack*)pTrack)->GetHintNumberOfPackets();
+}
+
+int8_t MP4File::GetRtpPacketBFrame(
+    MP4TrackId hintTrackId,
+    uint16_t packetIndex)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    return ((MP4RtpHintTrack*)pTrack)->GetPacketBFrame(packetIndex);
+}
+
+int32_t MP4File::GetRtpPacketTransmitOffset(
+    MP4TrackId hintTrackId,
+    uint16_t packetIndex)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    return ((MP4RtpHintTrack*)pTrack)->GetPacketTransmitOffset(packetIndex);
+}
+
+void MP4File::ReadRtpPacket(
+    MP4TrackId hintTrackId,
+    uint16_t packetIndex,
+    uint8_t** ppBytes,
+    uint32_t* pNumBytes,
+    uint32_t ssrc,
+    bool includeHeader,
+    bool includePayload)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->ReadPacket(
+        packetIndex, ppBytes, pNumBytes,
+        ssrc, includeHeader, includePayload);
+}
+
+MP4Timestamp MP4File::GetRtpTimestampStart(
+    MP4TrackId hintTrackId)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+    return ((MP4RtpHintTrack*)pTrack)->GetRtpTimestampStart();
+}
+
+void MP4File::SetRtpTimestampStart(
+    MP4TrackId hintTrackId,
+    MP4Timestamp rtpStart)
+{
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->SetRtpTimestampStart(rtpStart);
+}
+
+void MP4File::AddRtpHint(MP4TrackId hintTrackId,
+                         bool isBframe, uint32_t timestampOffset)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->AddHint(isBframe, timestampOffset);
+}
+
+void MP4File::AddRtpPacket(
+    MP4TrackId hintTrackId, bool setMbit, int32_t transmitOffset)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track", __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->AddPacket(setMbit, transmitOffset);
+}
+
+void MP4File::AddRtpImmediateData(MP4TrackId hintTrackId,
+                                  const uint8_t* pBytes, uint32_t numBytes)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->AddImmediateData(pBytes, numBytes);
+}
+
+void MP4File::AddRtpSampleData(MP4TrackId hintTrackId,
+                               MP4SampleId sampleId, uint32_t dataOffset, uint32_t dataLength)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->AddSampleData(
+        sampleId, dataOffset, dataLength);
+}
+
+void MP4File::AddRtpESConfigurationPacket(MP4TrackId hintTrackId)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->AddESConfigurationPacket();
+}
+
+void MP4File::WriteRtpHint(MP4TrackId hintTrackId,
+                           MP4Duration duration, bool isSyncSample)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+
+    MP4Track* pTrack = m_pTracks[FindTrackIndex(hintTrackId)];
+
+    if (strcmp(pTrack->GetType(), MP4_HINT_TRACK_TYPE)) {
+        throw new Exception("track is not a hint track",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    ((MP4RtpHintTrack*)pTrack)->WriteHint(duration, isSyncSample);
+}
+
+uint64_t MP4File::ConvertFromMovieDuration(
+    MP4Duration duration,
+    uint32_t timeScale)
+{
+    return MP4ConvertTime((uint64_t)duration,
+                          GetTimeScale(), timeScale);
+}
+
+uint64_t MP4File::ConvertFromTrackTimestamp(
+    MP4TrackId trackId,
+    MP4Timestamp timeStamp,
+    uint32_t timeScale)
+{
+    return MP4ConvertTime(timeStamp,
+                          GetTrackTimeScale(trackId), timeScale);
+}
+
+MP4Timestamp MP4File::ConvertToTrackTimestamp(
+    MP4TrackId trackId,
+    uint64_t timeStamp,
+    uint32_t timeScale)
+{
+    return (MP4Timestamp)MP4ConvertTime(timeStamp,
+                                        timeScale, GetTrackTimeScale(trackId));
+}
+
+uint64_t MP4File::ConvertFromTrackDuration(
+    MP4TrackId trackId,
+    MP4Duration duration,
+    uint32_t timeScale)
+{
+    return MP4ConvertTime((uint64_t)duration,
+                          GetTrackTimeScale(trackId), timeScale);
+}
+
+MP4Duration MP4File::ConvertToTrackDuration(
+    MP4TrackId trackId,
+    uint64_t duration,
+    uint32_t timeScale)
+{
+    return (MP4Duration)MP4ConvertTime(duration,
+                                       timeScale, GetTrackTimeScale(trackId));
+}
+
+uint8_t MP4File::ConvertTrackTypeToStreamType(const char* trackType)
+{
+    uint8_t streamType;
+
+    if (!strcmp(trackType, MP4_OD_TRACK_TYPE)) {
+        streamType = MP4ObjectDescriptionStreamType;
+    } else if (!strcmp(trackType, MP4_SCENE_TRACK_TYPE)) {
+        streamType = MP4SceneDescriptionStreamType;
+    } else if (!strcmp(trackType, MP4_CLOCK_TRACK_TYPE)) {
+        streamType = MP4ClockReferenceStreamType;
+    } else if (!strcmp(trackType, MP4_MPEG7_TRACK_TYPE)) {
+        streamType = MP4Mpeg7StreamType;
+    } else if (!strcmp(trackType, MP4_OCI_TRACK_TYPE)) {
+        streamType = MP4OCIStreamType;
+    } else if (!strcmp(trackType, MP4_IPMP_TRACK_TYPE)) {
+        streamType = MP4IPMPStreamType;
+    } else if (!strcmp(trackType, MP4_MPEGJ_TRACK_TYPE)) {
+        streamType = MP4MPEGJStreamType;
+    } else {
+        streamType = MP4UserPrivateStreamType;
+    }
+
+    return streamType;
+}
+
+// edit list
+
+char* MP4File::MakeTrackEditName(
+    MP4TrackId trackId,
+    MP4EditId editId,
+    const char* name)
+{
+    char* trakName = MakeTrackName(trackId, NULL);
+
+    if (m_editName == NULL) {
+        m_editName = (char *)malloc(1024);
+        if (m_editName == NULL) return NULL;
+    }
+    snprintf(m_editName, 1024,
+             "%s.edts.elst.entries[%u].%s",
+             trakName, editId - 1, name);
+    return m_editName;
+}
+
+MP4EditId MP4File::AddTrackEdit(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+    return m_pTracks[FindTrackIndex(trackId)]->AddEdit(editId);
+}
+
+void MP4File::DeleteTrackEdit(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    ProtectWriteOperation(__FILE__, __LINE__, __FUNCTION__);
+    m_pTracks[FindTrackIndex(trackId)]->DeleteEdit(editId);
+}
+
+uint32_t MP4File::GetTrackNumberOfEdits(
+    MP4TrackId trackId)
+{
+    return GetTrackIntegerProperty(trackId, "edts.elst.entryCount");
+}
+
+MP4Duration MP4File::GetTrackEditTotalDuration(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetEditTotalDuration(editId);
+}
+
+MP4Timestamp MP4File::GetTrackEditStart(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetEditStart(editId);
+}
+
+MP4Timestamp MP4File::GetTrackEditMediaStart(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    return GetIntegerProperty(
+               MakeTrackEditName(trackId, editId, "mediaTime"));
+}
+
+void MP4File::SetTrackEditMediaStart(
+    MP4TrackId trackId,
+    MP4EditId editId,
+    MP4Timestamp startTime)
+{
+    SetIntegerProperty(
+        MakeTrackEditName(trackId, editId, "mediaTime"),
+        startTime);
+}
+
+MP4Duration MP4File::GetTrackEditDuration(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    return GetIntegerProperty(
+               MakeTrackEditName(trackId, editId, "segmentDuration"));
+}
+
+void MP4File::SetTrackEditDuration(
+    MP4TrackId trackId,
+    MP4EditId editId,
+    MP4Duration duration)
+{
+    SetIntegerProperty(
+        MakeTrackEditName(trackId, editId, "segmentDuration"),
+        duration);
+}
+
+bool MP4File::GetTrackEditDwell(
+    MP4TrackId trackId,
+    MP4EditId editId)
+{
+    return (GetIntegerProperty(
+                MakeTrackEditName(trackId, editId, "mediaRate")) == 0);
+}
+
+void MP4File::SetTrackEditDwell(
+    MP4TrackId trackId,
+    MP4EditId editId,
+    bool dwell)
+{
+    SetIntegerProperty(
+        MakeTrackEditName(trackId, editId, "mediaRate"),
+        (dwell ? 0 : 1));
+}
+
+MP4SampleId MP4File::GetSampleIdFromEditTime(
+    MP4TrackId trackId,
+    MP4Timestamp when,
+    MP4Timestamp* pStartTime,
+    MP4Duration* pDuration)
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetSampleIdFromEditTime(
+               when, pStartTime, pDuration);
+}
+
+MP4Duration MP4File::GetTrackDurationPerChunk( MP4TrackId trackId )
+{
+    return m_pTracks[FindTrackIndex(trackId)]->GetDurationPerChunk();
+}
+
+void MP4File::SetTrackDurationPerChunk( MP4TrackId trackId, MP4Duration duration )
+{
+    m_pTracks[FindTrackIndex(trackId)]->SetDurationPerChunk( duration );
+}
+
+void MP4File::CopySample(
+    MP4File*    srcFile,
+    MP4TrackId  srcTrackId,
+    MP4SampleId srcSampleId,
+    MP4File*    dstFile,
+    MP4TrackId  dstTrackId,
+    MP4Duration dstSampleDuration )
+{
+    // Note: we leave it up to the caller to ensure that the
+    // source and destination tracks are compatible.
+    // i.e. copying audio samples into a video track
+    // is unlikely to do anything useful
+
+    uint8_t* pBytes = NULL;
+    uint32_t numBytes = 0;
+    MP4Duration sampleDuration;
+    MP4Duration renderingOffset;
+    bool isSyncSample;
+    bool hasDependencyFlags;
+    uint32_t dependencyFlags;
+
+    srcFile->ReadSample(
+         srcTrackId,
+         srcSampleId,
+         &pBytes,
+         &numBytes,
+         NULL,
+         &sampleDuration,
+         &renderingOffset,
+         &isSyncSample,
+         &hasDependencyFlags,
+         &dependencyFlags );
+
+    if( !dstFile )
+        dstFile = srcFile;
+
+    if( dstTrackId == MP4_INVALID_TRACK_ID )
+        dstTrackId = srcTrackId;
+
+    if( dstSampleDuration != MP4_INVALID_DURATION )
+        sampleDuration = dstSampleDuration;
+
+    if( hasDependencyFlags ) {
+        dstFile->WriteSampleDependency(
+            dstTrackId,
+            pBytes,
+            numBytes,
+            sampleDuration,
+            renderingOffset,
+            isSyncSample,
+            dependencyFlags );
+    }
+    else {
+        dstFile->WriteSample(
+            dstTrackId,
+            pBytes,
+            numBytes,
+            sampleDuration,
+            renderingOffset,
+            isSyncSample );
+    }
+
+    free( pBytes );
+}
+
+void MP4File::EncAndCopySample(
+    MP4File*      srcFile,
+    MP4TrackId    srcTrackId,
+    MP4SampleId   srcSampleId,
+    encryptFunc_t encfcnp,
+    uint32_t      encfcnparam1,
+    MP4File*      dstFile,
+    MP4TrackId    dstTrackId,
+    MP4Duration   dstSampleDuration )
+{
+    // Note: we leave it up to the caller to ensure that the
+    // source and destination tracks are compatible.
+    // i.e. copying audio samples into a video track
+    // is unlikely to do anything useful
+
+    uint8_t* pBytes = NULL;
+    uint32_t numBytes = 0;
+    uint8_t* encSampleData = NULL;
+    uint32_t encSampleLength = 0;
+    MP4Duration sampleDuration;
+    MP4Duration renderingOffset;
+    bool isSyncSample;
+    bool hasDependencyFlags;
+    uint32_t dependencyFlags;
+
+    ASSERT(srcFile);
+    srcFile->ReadSample(
+         srcTrackId,
+         srcSampleId,
+         &pBytes,
+         &numBytes,
+         NULL,
+         &sampleDuration,
+         &renderingOffset,
+         &isSyncSample,
+         &hasDependencyFlags,
+         &dependencyFlags );
+
+    if( !dstFile )
+        dstFile = srcFile;
+
+    ASSERT(dstFile);
+
+    if( dstTrackId == MP4_INVALID_TRACK_ID )
+        dstTrackId = srcTrackId;
+
+    if( dstSampleDuration != MP4_INVALID_DURATION )
+        sampleDuration = dstSampleDuration;
+
+    //if( ismacrypEncryptSampleAddHeader( ismaCryptSId, numBytes, pBytes, &encSampleLength, &encSampleData ) != 0)
+    if( encfcnp( encfcnparam1, numBytes, pBytes, &encSampleLength, &encSampleData ) != 0 )
+        log.errorf("%s(%s,%s) Can't encrypt the sample and add its header %u", 
+                   __FUNCTION__, srcFile->GetFilename().c_str(), dstFile->GetFilename().c_str(), srcSampleId );
+
+    if( hasDependencyFlags ) {
+        dstFile->WriteSampleDependency(
+            dstTrackId,
+            pBytes,
+            numBytes,
+            sampleDuration,
+            renderingOffset,
+            isSyncSample,
+            dependencyFlags );
+    }
+    else {
+        dstFile->WriteSample(
+            dstTrackId,
+            encSampleData,
+            encSampleLength,
+            sampleDuration,
+            renderingOffset,
+            isSyncSample );
+    }
+
+    free( pBytes );
+
+    if( encSampleData != NULL )
+        free( encSampleData );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/mp4file.h b/src/mp4file.h
new file mode 100644
index 0000000..960505e
--- /dev/null
+++ b/src/mp4file.h
@@ -0,0 +1,992 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 - 2005.  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.
+//
+//  Contributors:
+//      Dave Mackie, dmackie@cisco.com
+//      Alix Marchandise-Franquet, alix@cisco.com
+//      Ximpo Group Ltd., mp4v2@ximpo.com
+//      Kona Blend, kona8lend@@gmail.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_MP4FILE_H
+#define MP4V2_IMPL_MP4FILE_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4Atom;
+class MP4Property;
+class MP4Float32Property;
+class MP4StringProperty;
+class MP4BytesProperty;
+class MP4Descriptor;
+class MP4DescriptorProperty;
+
+class MP4File
+{
+public:
+    static void CopySample(
+        MP4File*    srcFile,
+        MP4TrackId  srcTrackId,
+        MP4SampleId srcSampleId,
+        MP4File*    dstFile,
+        MP4TrackId  dstTrackId,
+        MP4Duration dstSampleDuration );
+
+    static void EncAndCopySample(
+        MP4File*      srcFile,
+        MP4TrackId    srcTrackId,
+        MP4SampleId   srcSampleId,
+        encryptFunc_t encfcnp,
+        uint32_t      encfcnparam1,
+        MP4File*      dstFile,
+        MP4TrackId    dstTrackId,
+        MP4Duration   dstSampleDuration );
+
+public:
+    MP4File();
+    ~MP4File();
+
+    ///////////////////////////////////////////////////////////////////////////
+    // file ops
+    ///////////////////////////////////////////////////////////////////////////
+
+    void Create( const char*            fileName,
+                 uint32_t               flags,
+                 const MP4FileProvider* provider = NULL,
+                 int                    add_ftyp = 1,
+                 int                    add_iods = 1,
+                 char*                  majorBrand = NULL,
+                 uint32_t               minorVersion = 0,
+                 char**                 supportedBrands = NULL,
+                 uint32_t               supportedBrandsCount = 0 );
+
+    const std::string &GetFilename() const;
+    void Read( const char* name, const MP4FileProvider* provider );
+    bool Modify( const char* fileName );
+    void Optimize( const char* srcFileName, const char* dstFileName = NULL );
+    bool CopyClose( const string& copyFileName );
+    void Dump( bool dumpImplicits = false );
+    void Close(uint32_t flags = 0);
+
+    bool Use64Bits(const char *atomName);
+    void Check64BitStatus(const char *atomName);
+    /* file properties */
+
+    uint64_t GetIntegerProperty(const char* name);
+    float GetFloatProperty(const char* name);
+    const char* GetStringProperty(const char* name);
+    void GetBytesProperty(const char* name,
+                          uint8_t** ppValue, uint32_t* pValueSize);
+
+    void SetIntegerProperty(const char* name, uint64_t value);
+    void SetFloatProperty(const char* name, float value);
+    void SetStringProperty(const char* name, const char* value);
+    void SetBytesProperty(const char* name,
+                          const uint8_t* pValue, uint32_t valueSize);
+
+    // file level convenience functions
+
+    MP4Duration GetDuration();
+    void SetDuration(MP4Duration value);
+
+    uint32_t GetTimeScale();
+    void SetTimeScale(uint32_t value);
+
+    uint8_t GetODProfileLevel();
+    void SetODProfileLevel(uint8_t value);
+
+    uint8_t GetSceneProfileLevel();
+    void SetSceneProfileLevel(uint8_t value);
+
+    uint8_t GetVideoProfileLevel();
+    void SetVideoProfileLevel(uint8_t value);
+
+    uint8_t GetAudioProfileLevel();
+    void SetAudioProfileLevel(uint8_t value);
+
+    uint8_t GetGraphicsProfileLevel();
+    void SetGraphicsProfileLevel(uint8_t value);
+
+    const char* GetSessionSdp();
+    void SetSessionSdp(const char* sdpString);
+    void AppendSessionSdp(const char* sdpString);
+
+    /* track operations */
+
+    MP4TrackId AddTrack(const char* type, uint32_t timeScale = 1000);
+    void DeleteTrack(MP4TrackId trackId);
+
+    uint32_t GetNumberOfTracks(const char* type = NULL, uint8_t subType = 0);
+
+    MP4TrackId AllocTrackId();
+    MP4TrackId FindTrackId(uint16_t trackIndex,
+                           const char* type = NULL, uint8_t subType = 0);
+    uint16_t FindTrackIndex(MP4TrackId trackId);
+    uint16_t FindTrakAtomIndex(MP4TrackId trackId);
+
+    /* track properties */
+    MP4Atom *FindTrackAtom(MP4TrackId trackId, const char *name);
+    uint64_t GetTrackIntegerProperty(
+        MP4TrackId trackId, const char* name);
+    float GetTrackFloatProperty(
+        MP4TrackId trackId, const char* name);
+    const char* GetTrackStringProperty(
+        MP4TrackId trackId, const char* name);
+    void GetTrackBytesProperty(
+        MP4TrackId trackId, const char* name,
+        uint8_t** ppValue, uint32_t* pValueSize);
+
+    void SetTrackIntegerProperty(
+        MP4TrackId trackId, const char* name, int64_t value);
+    void SetTrackFloatProperty(
+        MP4TrackId trackId, const char* name, float value);
+    void SetTrackStringProperty(
+        MP4TrackId trackId, const char* name, const char* value);
+    void SetTrackBytesProperty(
+        MP4TrackId trackId, const char* name,
+        const uint8_t* pValue, uint32_t valueSize);
+
+    bool GetTrackLanguage( MP4TrackId, char* );
+    bool SetTrackLanguage( MP4TrackId, const char* );
+    bool GetTrackName( MP4TrackId trackId, char** name );
+    bool SetTrackName( MP4TrackId trackId, const char* name);
+
+    /* sample operations */
+
+    uint32_t GetSampleSize(MP4TrackId trackId, MP4SampleId sampleId);
+
+    uint32_t GetTrackMaxSampleSize(MP4TrackId trackId);
+
+    MP4SampleId GetSampleIdFromTime(MP4TrackId trackId,
+                                    MP4Timestamp when, bool wantSyncSample = false);
+
+    MP4Timestamp GetSampleTime(
+        MP4TrackId trackId, MP4SampleId sampleId);
+
+    MP4Duration GetSampleDuration(
+        MP4TrackId trackId, MP4SampleId sampleId);
+
+    MP4Duration GetSampleRenderingOffset(
+        MP4TrackId trackId, MP4SampleId sampleId);
+
+    bool GetSampleSync(
+        MP4TrackId trackId, MP4SampleId sampleId);
+
+    void ReadSample(
+        // input parameters
+        MP4TrackId trackId,
+        MP4SampleId sampleId,
+        // output parameters
+        uint8_t**     ppBytes,
+        uint32_t*     pNumBytes,
+        MP4Timestamp* pStartTime = NULL,
+        MP4Duration*  pDuration = NULL,
+        MP4Duration*  pRenderingOffset = NULL,
+        bool*         pIsSyncSample = NULL,
+        bool*         hasDependencyFlags = NULL,
+        uint32_t*     dependencyFlags = NULL );
+
+    void WriteSample(
+        MP4TrackId     trackId,
+        const uint8_t* pBytes,
+        uint32_t       numBytes,
+        MP4Duration    duration = 0,
+        MP4Duration    renderingOffset = 0,
+        bool           isSyncSample = true );
+
+    void WriteSampleDependency(
+        MP4TrackId     trackId,
+        const uint8_t* pBytes,
+        uint32_t       numBytes,
+        MP4Duration    duration,
+        MP4Duration    renderingOffset,
+        bool           isSyncSample,
+        uint32_t       dependencyFlags );
+
+    void SetSampleRenderingOffset(
+        MP4TrackId  trackId,
+        MP4SampleId sampleId,
+        MP4Duration renderingOffset );
+
+    MP4Duration GetTrackDurationPerChunk( MP4TrackId );
+    void        SetTrackDurationPerChunk( MP4TrackId, MP4Duration );
+
+    /* track level convenience functions */
+
+    MP4TrackId AddSystemsTrack(const char* type, uint32_t timeScale = 1000 );
+
+    MP4TrackId AddODTrack();
+
+    MP4TrackId AddSceneTrack();
+
+    MP4TrackId AddAudioTrack(
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint8_t audioType);
+
+    MP4TrackId AddULawAudioTrack(
+        uint32_t timeScale);
+
+    MP4TrackId AddALawAudioTrack(
+        uint32_t timeScale);
+
+    MP4TrackId AddAC3AudioTrack(
+        uint32_t samplingRate,
+        uint8_t fscod,
+        uint8_t bsid,
+        uint8_t bsmod,
+        uint8_t acmod,
+        uint8_t lfeon,
+        uint8_t bit_rate_code);
+
+    MP4TrackId AddEncAudioTrack( // ismacryp
+        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);
+
+    void SetAmrVendor(
+        MP4TrackId trackId,
+        uint32_t vendor);
+
+    void SetAmrDecoderVersion(
+        MP4TrackId trackId,
+        uint8_t decoderVersion);
+
+    void SetAmrModeSet(
+        MP4TrackId trackId,
+        uint16_t modeSet);
+    uint16_t GetAmrModeSet(MP4TrackId trackId);
+
+    MP4TrackId AddAmrAudioTrack(
+        uint32_t timeScale,
+        uint16_t modeSet,
+        uint8_t modeChangePeriod,
+        uint8_t framesPerSample,
+        bool isAmrWB);
+
+    MP4TrackId AddHrefTrack(uint32_t timeScale,
+                            MP4Duration sampleDuration,
+                            const char *base_url);
+
+    MP4TrackId AddMP4VideoTrack(
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        uint8_t videoType);
+
+    MP4TrackId AddEncVideoTrack( // ismacryp
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        uint8_t  videoType,
+        mp4v2_ismacrypParams *icPp,
+        const char *oFormat);
+
+    void SetH263Vendor(
+        MP4TrackId trackId,
+        uint32_t vendor);
+
+    void SetH263DecoderVersion(
+        MP4TrackId trackId,
+        uint8_t decoderVersion);
+
+    void SetH263Bitrates(
+        MP4TrackId,
+        uint32_t avgBitrate,
+        uint32_t maxBitrate);
+
+    MP4TrackId AddH263VideoTrack(
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        uint8_t h263Level,
+        uint8_t h263Profile,
+        uint32_t avgBitrate,
+        uint32_t maxBitrate);
+
+    MP4TrackId 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);
+
+    MP4TrackId AddEncH264VideoTrack(
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        MP4Atom *srcAtom,
+        mp4v2_ismacrypParams *icPp);
+
+    void AddH264SequenceParameterSet(MP4TrackId trackId,
+                                     const uint8_t *pSequence,
+                                     uint16_t sequenceLen);
+    void AddH264PictureParameterSet(MP4TrackId trackId,
+                                    const uint8_t *pPicture,
+                                    uint16_t pictureLen);
+    MP4TrackId AddHintTrack(MP4TrackId refTrackId);
+
+    MP4TrackId AddTextTrack(MP4TrackId refTrackId);
+
+    /** Add a QuickTime chapter track.
+     *
+     *  This function adds a chapter (text) track.
+     *  The optional parameter <b>timescale</b> may be supplied to give the new
+     *  chapter a specific timescale. Otherwise the chapter track will have
+     *  the same timescale as the reference track defined in parameter refTrackId.
+     *
+     *  @param refTrackId ID of the track that will reference the chapter track.
+     *  @param timescale the timescale of the chapter track or 0 to use the
+     *      timescale of track specified by <b>refTrackId</b>.
+     *
+     *  @return ID of the created chapter track.
+     */
+    MP4TrackId AddChapterTextTrack(
+        MP4TrackId refTrackId,
+        uint32_t   timescale = 0 );
+
+    MP4TrackId AddSubtitleTrack(uint32_t timescale,
+                                uint16_t width,
+                                uint16_t height);
+
+    MP4TrackId AddSubpicTrack(uint32_t timescale,
+                                uint16_t width,
+                                uint16_t height);
+
+    MP4TrackId AddPixelAspectRatio(MP4TrackId trackId, uint32_t hSpacing, uint32_t vSpacing);
+    MP4TrackId AddColr(MP4TrackId trackId, uint16_t pri, uint16_t tran, uint16_t mat);
+
+    /** Add a QuickTime chapter.
+     *
+     *  @param chapterTrackId ID of chapter track or #MP4_INVALID_TRACK_ID
+     *      if unknown.
+     *  @param chapterDuration duration (in the timescale of the chapter track).
+     *  @param chapterTitle title text for the chapter or NULL to use default
+     *      title format ("Chapter %03d", n) where n is the chapter number.
+     */
+    void AddChapter(
+        MP4TrackId  chapterTrackId,
+        MP4Duration chapterDuration,
+        const char* chapterTitle = 0 );
+
+    /** Add a Nero chapter.
+     *
+     *  @param chapterStart the start time of the chapter in 100 nanosecond units
+     *  @param chapterTitle title text for the chapter or NULL to use default
+     *      title format ("Chapter %03d", n) where n is the chapter number.
+     */
+    void AddNeroChapter(
+        MP4Timestamp chapterStart,
+        const char*  chapterTitle = 0 );
+
+    /*! Returns the ID of the track referencing the chapter track chapterTrackId.
+     *  This function searches for a track of type MP4_AUDIO_TRACK_TYPE that references
+     *  the track chapterTrackId through the atom "tref.chap".
+     *
+     * @param chapterTrackId the ID of the chapter track
+     * @param trackName receives the name of the referencing track if not null
+     * @param trackNameSize the size of the memory pointed to by trackName
+     * @return the ID if the track referencing the chapter track or MP4_INVALID_TRACK_ID
+     */
+    MP4TrackId FindChapterReferenceTrack(MP4TrackId chapterTrackId,
+                                         char *trackName = 0,
+                                         int trackNameSize = 0);
+
+    /*! Find the QuickTime chapter track in the current file.
+     * This function searches for a track of type text.
+     *
+     * @param trackName receives the name of the chapter track if not null
+     * @param trackNameSize the size of the memory pointed to by trackName
+     * @return the ID of the chapter track or MP4_INVALID_TRACK_ID
+     */
+    MP4TrackId FindChapterTrack(char *trackName = 0, int trackNameSize = 0);
+
+    /** Delete chapters.
+     *
+     *  @param chapterType the type of chapters to delete:
+     *      @li #MP4ChapterTypeAny (delete all known chapter types)
+     *      @li #MP4ChapterTypeQt
+     *      @li #MP4ChapterTypeNero
+     *  @param chapterTrackId ID of the chapter track if known,
+     *      or #MP4_INVALID_TRACK_ID.
+     *      Only applies when <b>chapterType</b>=#MP4ChapterTypeQt.
+     *
+     * @return the type of deleted chapters
+     */
+    MP4ChapterType DeleteChapters(
+        MP4ChapterType chapterType = MP4ChapterTypeQt,
+        MP4TrackId chapterTrackId = 0 );
+
+    /** Get list of chapters.
+     *
+     *  @param chapterList address receiving array of chapter items.
+     *      If a non-NULL is received the caller is responsible for freeing the
+     *      memory with MP4Free().
+     *  @param chapterCount address receiving count of items in array.
+     *  @param chapterType the type of chapters to read:
+     *      @li #MP4ChapterTypeAny (any chapters, searched in order of Qt, Nero)
+     *      @li #MP4ChapterTypeQt
+     *      @li #MP4ChapterTypeNero
+     *
+     *  @result the first type of chapters found.
+     */
+
+    MP4ChapterType GetChapters(
+        MP4Chapter_t** chapterList,
+        uint32_t*      chapterCount,
+        MP4ChapterType fromChapterType = MP4ChapterTypeQt );
+
+    /** Set list of chapters.
+     *
+     *  This functions sets the complete chapter list.
+     *  If any chapters of the same type already exist they will first
+     *  be deleted.
+     *
+     *  @param chapterList array of chapters items.
+     *  @param chapterCount count of items in array.
+     *  @param chapterType type of chapters to write:
+     *      @li #MP4ChapterTypeAny (chapters of all types are written)
+     *      @li #MP4ChapterTypeQt
+     *      @li #MP4ChapterTypeNero
+     *
+     *  @return the type of chapters written.
+     */
+    MP4ChapterType SetChapters(
+        MP4Chapter_t*  chapterList,
+        uint32_t       chapterCount,
+        MP4ChapterType toChapterType = MP4ChapterTypeQt );
+
+    /** Convert chapters to another type.
+     *
+     *  This function converts existing chapters
+     *  from one type to another type.
+     *  Conversion from Nero to QuickTime or QuickTime to Nero is supported.
+     *
+     *  @param toChapterType the chapter type to convert to:
+     *      @li #MP4ChapterTypeQt (convert from Nero to Qt)
+     *      @li #MP4ChapterTypeNero (convert from Qt to Nero)
+     *
+     *  @return the chapter type before conversion or #MP4ChapterTypeNone
+     *      if the source chapters do not exist
+     *      or invalid <b>toChapterType</b> was specified.
+     */
+    MP4ChapterType ConvertChapters(MP4ChapterType toChapterType = MP4ChapterTypeQt);
+
+    /** Change the general timescale.
+     *
+     *  This function changes the general timescale to the new timescale
+     *  <b>value</b> by recalculating all values that depend on the timescale
+     *  in "moov.mvhd".
+     *
+     *  If the timescale is already equal to value nothing is done.
+     *
+     *  @param value the new timescale.
+     */
+    void ChangeMovieTimeScale(uint32_t timescale);
+
+    MP4SampleId GetTrackNumberOfSamples(MP4TrackId trackId);
+
+    const char* GetTrackType(MP4TrackId trackId);
+
+    const char *GetTrackMediaDataName(MP4TrackId trackId);
+    bool GetTrackMediaDataOriginalFormat(MP4TrackId trackId,
+                                         char *originalFormat, uint32_t buflen);
+    MP4Duration GetTrackDuration(MP4TrackId trackId);
+
+    uint32_t GetTrackTimeScale(MP4TrackId trackId);
+    void SetTrackTimeScale(MP4TrackId trackId, uint32_t value);
+
+    // replacement to GetTrackAudioType and GetTrackVideoType
+    uint8_t GetTrackEsdsObjectTypeId(MP4TrackId trackId);
+
+    uint8_t GetTrackAudioMpeg4Type(MP4TrackId trackId);
+
+    MP4Duration GetTrackFixedSampleDuration(MP4TrackId trackId);
+
+    double GetTrackVideoFrameRate(MP4TrackId trackId);
+
+    int GetTrackAudioChannels(MP4TrackId trackId);
+    void GetTrackESConfiguration(MP4TrackId trackId,
+                                 uint8_t** ppConfig, uint32_t* pConfigSize);
+    void SetTrackESConfiguration(MP4TrackId trackId,
+                                 const uint8_t* pConfig, uint32_t configSize);
+
+    void GetTrackVideoMetadata(MP4TrackId trackId,
+                               uint8_t** ppConfig, uint32_t* pConfigSize);
+    void GetTrackH264SeqPictHeaders(MP4TrackId trackId,
+                                    uint8_t ***pSeqHeader,
+                                    uint32_t **pSeqHeaderSize,
+                                    uint8_t ***pPictHeader,
+                                    uint32_t **pPictHeaderSize);
+    const char* GetHintTrackSdp(MP4TrackId hintTrackId);
+    void SetHintTrackSdp(MP4TrackId hintTrackId, const char* sdpString);
+    void AppendHintTrackSdp(MP4TrackId hintTrackId, const char* sdpString);
+
+    void MakeFtypAtom(
+        char*    majorBrand,
+        uint32_t minorVersion,
+        char**   compatibleBrands,
+        uint32_t compatibleBrandsCount );
+
+    // 3GPP specific functions
+    void Make3GPCompliant(const char* fileName,
+                          char* majorBrand,
+                          uint32_t minorVersion,
+                          char** supportedBrands,
+                          uint32_t supportedBrandsCount,
+                          bool deleteIodsAtom);
+
+    // ISMA specific functions
+
+    // true if media track encrypted according to ismacryp
+    bool IsIsmaCrypMediaTrack(MP4TrackId trackId);
+
+    void MakeIsmaCompliant(bool addIsmaComplianceSdp = true);
+
+    void CreateIsmaIodFromParams(
+        uint8_t videoProfile,
+        uint32_t videoBitrate,
+        uint8_t* videoConfig,
+        uint32_t videoConfigLength,
+        uint8_t audioProfile,
+        uint32_t audioBitrate,
+        uint8_t* audioConfig,
+        uint32_t audioConfigLength,
+        uint8_t** ppBytes,
+        uint64_t* pNumBytes);
+
+    // time convenience functions
+
+    uint64_t ConvertFromMovieDuration(
+        MP4Duration duration,
+        uint32_t timeScale);
+
+    uint64_t ConvertFromTrackTimestamp(
+        MP4TrackId trackId,
+        MP4Timestamp timeStamp,
+        uint32_t timeScale);
+
+    MP4Timestamp ConvertToTrackTimestamp(
+        MP4TrackId trackId,
+        uint64_t timeStamp,
+        uint32_t timeScale);
+
+    uint64_t ConvertFromTrackDuration(
+        MP4TrackId trackId,
+        MP4Duration duration,
+        uint32_t timeScale);
+
+    MP4Duration ConvertToTrackDuration(
+        MP4TrackId trackId,
+        uint64_t duration,
+        uint32_t timeScale);
+
+    // specialized operations
+
+    void GetHintTrackRtpPayload(
+        MP4TrackId hintTrackId,
+        char** ppPayloadName = NULL,
+        uint8_t* pPayloadNumber = NULL,
+        uint16_t* pMaxPayloadSize = NULL,
+        char **ppEncodingParams = NULL);
+
+    void SetHintTrackRtpPayload(
+        MP4TrackId hintTrackId,
+        const char* payloadName,
+        uint8_t* pPayloadNumber,
+        uint16_t maxPayloadSize,
+        const char *encoding_params,
+        bool include_rtp_map,
+        bool include_mpeg4_esid);
+
+    MP4TrackId GetHintTrackReferenceTrackId(
+        MP4TrackId hintTrackId);
+
+    void ReadRtpHint(
+        MP4TrackId hintTrackId,
+        MP4SampleId hintSampleId,
+        uint16_t* pNumPackets = NULL);
+
+    uint16_t GetRtpHintNumberOfPackets(
+        MP4TrackId hintTrackId);
+
+    int8_t GetRtpPacketBFrame(
+        MP4TrackId hintTrackId,
+        uint16_t packetIndex);
+
+    int32_t GetRtpPacketTransmitOffset(
+        MP4TrackId hintTrackId,
+        uint16_t packetIndex);
+
+    void ReadRtpPacket(
+        MP4TrackId hintTrackId,
+        uint16_t packetIndex,
+        uint8_t** ppBytes,
+        uint32_t* pNumBytes,
+        uint32_t ssrc = 0,
+        bool includeHeader = true,
+        bool includePayload = true);
+
+    MP4Timestamp GetRtpTimestampStart(
+        MP4TrackId hintTrackId);
+
+    void SetRtpTimestampStart(
+        MP4TrackId hintTrackId,
+        MP4Timestamp rtpStart);
+
+    void AddRtpHint(
+        MP4TrackId hintTrackId,
+        bool isBframe,
+        uint32_t timestampOffset);
+
+    void AddRtpPacket(
+        MP4TrackId hintTrackId,
+        bool setMbit,
+        int32_t transmitOffset);
+
+    void AddRtpImmediateData(
+        MP4TrackId hintTrackId,
+        const uint8_t* pBytes,
+        uint32_t numBytes);
+
+    void AddRtpSampleData(
+        MP4TrackId hintTrackId,
+        MP4SampleId sampleId,
+        uint32_t dataOffset,
+        uint32_t dataLength);
+
+    void AddRtpESConfigurationPacket(
+        MP4TrackId hintTrackId);
+
+    void WriteRtpHint(
+        MP4TrackId hintTrackId,
+        MP4Duration duration,
+        bool isSyncSample);
+
+    uint8_t AllocRtpPayloadNumber();
+
+    // edit list related
+
+    char* MakeTrackEditName(
+        MP4TrackId trackId,
+        MP4EditId editId,
+        const char* name);
+
+    MP4EditId AddTrackEdit(
+        MP4TrackId trackId,
+        MP4EditId editId = MP4_INVALID_EDIT_ID);
+
+    void DeleteTrackEdit(
+        MP4TrackId trackId,
+        MP4EditId editId);
+
+    uint32_t GetTrackNumberOfEdits(
+        MP4TrackId trackId);
+
+    MP4Timestamp GetTrackEditStart(
+        MP4TrackId trackId,
+        MP4EditId editId);
+
+    MP4Duration GetTrackEditTotalDuration(
+        MP4TrackId trackId,
+        MP4EditId editId);
+
+    MP4Timestamp GetTrackEditMediaStart(
+        MP4TrackId trackId,
+        MP4EditId editId);
+
+    void SetTrackEditMediaStart(
+        MP4TrackId trackId,
+        MP4EditId editId,
+        MP4Timestamp startTime);
+
+    MP4Duration GetTrackEditDuration(
+        MP4TrackId trackId,
+        MP4EditId editId);
+
+    void SetTrackEditDuration(
+        MP4TrackId trackId,
+        MP4EditId editId,
+        MP4Duration duration);
+
+    bool GetTrackEditDwell(
+        MP4TrackId trackId,
+        MP4EditId editId);
+
+    void SetTrackEditDwell(
+        MP4TrackId trackId,
+        MP4EditId editId,
+        bool dwell);
+
+    MP4SampleId GetSampleIdFromEditTime(
+        MP4TrackId trackId,
+        MP4Timestamp when,
+        MP4Timestamp* pStartTime = NULL,
+        MP4Duration* pDuration = NULL);
+
+    /* "protected" interface to be used only by friends in library */
+
+    uint64_t GetPosition( File* file = NULL );
+    void SetPosition( uint64_t pos, File* file = NULL );
+    uint64_t GetSize( File* file = NULL );
+
+    void ReadBytes( uint8_t* buf, uint32_t bufsiz, File* file = NULL );
+    void PeekBytes( uint8_t* buf, uint32_t bufsiz, File* file = NULL );
+
+    uint64_t ReadUInt(uint8_t size);
+    uint8_t ReadUInt8();
+    uint16_t ReadUInt16();
+    uint32_t ReadUInt24();
+    uint32_t ReadUInt32();
+    uint64_t ReadUInt64();
+    float ReadFixed16();
+    float ReadFixed32();
+    float ReadFloat();
+    char* ReadString();
+    char* ReadCountedString(
+        uint8_t charSize = 1, bool allowExpandedCount = false, uint8_t fixedLength = 0);
+    uint64_t ReadBits(uint8_t numBits);
+    void FlushReadBits();
+    uint32_t ReadMpegLength();
+
+
+    void WriteBytes( uint8_t* buf, uint32_t bufsiz, File* file = NULL );
+    void WriteUInt8(uint8_t value);
+    void WriteUInt16(uint16_t value);
+    void WriteUInt24(uint32_t value);
+    void WriteUInt32(uint32_t value);
+    void WriteUInt64(uint64_t value);
+    void WriteFixed16(float value);
+    void WriteFixed32(float value);
+    void WriteFloat(float value);
+    void WriteString(char* string);
+    void WriteCountedString(char* string,
+                            uint8_t charSize = 1,
+                            bool allowExpandedCount = false,
+                            uint32_t fixedLength = 0);
+    void WriteBits(uint64_t bits, uint8_t numBits);
+    void PadWriteBits(uint8_t pad = 0);
+    void FlushWriteBits();
+    void WriteMpegLength(uint32_t value, bool compact = false);
+
+    void EnableMemoryBuffer(
+        uint8_t* pBytes = NULL, uint64_t numBytes = 0);
+    void DisableMemoryBuffer(
+        uint8_t** ppBytes = NULL, uint64_t* pNumBytes = NULL);
+
+    bool IsWriteMode();
+
+    MP4Track* GetTrack(MP4TrackId trackId);
+
+    void UpdateDuration(MP4Duration duration);
+
+    MP4Atom* FindAtom(const char* name);
+
+    MP4Atom* AddChildAtom(
+        const char* parentName,
+        const char* childName);
+
+    MP4Atom* AddChildAtom(
+        MP4Atom* pParentAtom,
+        const char* childName);
+
+    MP4Atom* InsertChildAtom(
+        const char* parentName,
+        const char* childName,
+        uint32_t index);
+
+    MP4Atom* InsertChildAtom(
+        MP4Atom* pParentAtom,
+        const char* childName,
+        uint32_t index);
+
+    MP4Atom* AddDescendantAtoms(
+        const char* ancestorName,
+        const char* childName);
+
+    MP4Atom* AddDescendantAtoms(
+        MP4Atom* pAncestorAtom,
+        const char* childName);
+
+protected:
+    void Init();
+    void Open( const char* name, File::Mode mode, const MP4FileProvider* provider );
+    void ReadFromFile();
+    void GenerateTracks();
+    void BeginWrite();
+    void FinishWrite(uint32_t options);
+    void CacheProperties();
+    void RewriteMdat( File& src, File& dst );
+    bool ShallHaveIods();
+
+    void Rename(const char* existingFileName, const char* newFileName);
+
+    void ProtectWriteOperation(const char* file, int line, const char *func);
+
+    void FindIntegerProperty(const char* name,
+                             MP4Property** ppProperty, uint32_t* pIndex = NULL);
+    void FindFloatProperty(const char* name,
+                           MP4Property** ppProperty, uint32_t* pIndex = NULL);
+    void FindStringProperty(const char* name,
+                            MP4Property** ppProperty, uint32_t* pIndex = NULL);
+    void FindBytesProperty(const char* name,
+                           MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    bool FindProperty(const char* name,
+                      MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+    MP4TrackId AddVideoTrackDefault(
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        uint16_t width,
+        uint16_t height,
+        const char *videoType);
+    MP4TrackId AddCntlTrackDefault(
+        uint32_t timeScale,
+        MP4Duration sampleDuration,
+        const char *videoType);
+    void AddTrackToIod(MP4TrackId trackId);
+
+    void RemoveTrackFromIod(MP4TrackId trackId, bool shallHaveIods = true);
+
+    void AddTrackToOd(MP4TrackId trackId);
+
+    void RemoveTrackFromOd(MP4TrackId trackId);
+
+    void GetTrackReferenceProperties(const char* trefName,
+                                     MP4Property** ppCountProperty, MP4Property** ppTrackIdProperty);
+
+    void AddTrackReference(const char* trefName, MP4TrackId refTrackId);
+
+    uint32_t FindTrackReference(const char* trefName, MP4TrackId refTrackId);
+
+    void RemoveTrackReference(const char* trefName, MP4TrackId refTrackId);
+
+    void AddDataReference(MP4TrackId trackId, const char* url);
+
+    char* MakeTrackName(MP4TrackId trackId, const char* name);
+
+    uint8_t ConvertTrackTypeToStreamType(const char* trackType);
+
+    void CreateIsmaIodFromFile(
+        MP4TrackId odTrackId,
+        MP4TrackId sceneTrackId,
+        MP4TrackId audioTrackId,
+        MP4TrackId videoTrackId,
+        uint8_t** ppBytes,
+        uint64_t* pNumBytes);
+
+    void CreateESD(
+        MP4DescriptorProperty* pEsProperty,
+        uint32_t esid,
+        uint8_t objectType,
+        uint8_t streamType,
+        uint32_t bufferSize,
+        uint32_t bitrate,
+        const uint8_t* pConfig,
+        uint32_t configLength,
+        char* url);
+
+    void CreateIsmaODUpdateCommandFromFileForFile(
+        MP4TrackId odTrackId,
+        MP4TrackId audioTrackId,
+        MP4TrackId videoTrackId,
+        uint8_t** ppBytes,
+        uint64_t* pNumBytes);
+
+    void CreateIsmaODUpdateCommandFromFileForStream(
+        MP4TrackId audioTrackId,
+        MP4TrackId videoTrackId,
+        uint8_t** ppBytes,
+        uint64_t* pNumBytes);
+
+    void CreateIsmaODUpdateCommandForStream(
+        MP4DescriptorProperty* pAudioEsdProperty,
+        MP4DescriptorProperty* pVideoEsdProperty,
+        uint8_t** ppBytes,
+        uint64_t* pNumBytes);
+
+    void CreateIsmaSceneCommand(
+        bool hasAudio,
+        bool hasVideo,
+        uint8_t** ppBytes,
+        uint64_t* pNumBytes);
+
+protected:
+    File*    m_file;
+    uint64_t m_fileOriginalSize;
+    uint32_t m_createFlags;
+
+    MP4Atom*          m_pRootAtom;
+    MP4Integer32Array m_trakIds;
+    MP4TrackArray     m_pTracks;
+    MP4TrackId        m_odTrackId;
+    bool              m_useIsma;
+
+    // cached properties
+    MP4IntegerProperty*     m_pModificationProperty;
+    MP4Integer32Property*   m_pTimeScaleProperty;
+    MP4IntegerProperty*     m_pDurationProperty;
+
+    // read/write in memory
+    uint8_t*    m_memoryBuffer;
+    uint64_t    m_memoryBufferPosition;
+    uint64_t    m_memoryBufferSize;
+
+    // bit read/write buffering
+    uint8_t m_numReadBits;
+    uint8_t m_bufReadBits;
+    uint8_t m_numWriteBits;
+    uint8_t m_bufWriteBits;
+
+    char m_trakName[1024];
+    char *m_editName;
+
+ private:
+    MP4File ( const MP4File &src );
+    MP4File &operator= ( const MP4File &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4FILE_H
diff --git a/src/mp4file_io.cpp b/src/mp4file_io.cpp
new file mode 100644
index 0000000..d16d87f
--- /dev/null
+++ b/src/mp4file_io.cpp
@@ -0,0 +1,581 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// MP4File low level IO support
+
+uint64_t MP4File::GetPosition( File* file )
+{
+    if( m_memoryBuffer )
+        return m_memoryBufferPosition;
+
+    if( !file )
+        file = m_file;
+
+    ASSERT( file );
+    return file->position;
+}
+
+void MP4File::SetPosition( uint64_t pos, File* file )
+{
+    if( m_memoryBuffer ) {
+        if( pos >= m_memoryBufferSize )
+            throw new Exception( "position out of range", __FILE__, __LINE__, __FUNCTION__ );
+        m_memoryBufferPosition = pos;
+        return;
+    }
+
+    if( !file )
+        file = m_file;
+
+    ASSERT( file );
+    if( file->seek( pos ))
+        throw new PlatformException( "seek failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ );
+}
+
+uint64_t MP4File::GetSize( File* file )
+{
+    if( m_memoryBuffer )
+        return m_memoryBufferSize;
+
+    if( !file )
+        file = m_file;
+
+    ASSERT( file );
+    return file->size;
+}
+
+void MP4File::ReadBytes( uint8_t* buf, uint32_t bufsiz, File* file )
+{
+    if( bufsiz == 0 )
+        return;
+
+    ASSERT( buf );
+    WARNING( m_numReadBits > 0 );
+
+    if( m_memoryBuffer ) {
+        if( m_memoryBufferPosition + bufsiz > m_memoryBufferSize )
+            throw new Exception( "not enough bytes, reached end-of-memory", __FILE__, __LINE__, __FUNCTION__ );
+        memcpy( buf, &m_memoryBuffer[m_memoryBufferPosition], bufsiz );
+        m_memoryBufferPosition += bufsiz;
+        return;
+    }
+
+    if( !file )
+        file = m_file;
+
+    ASSERT( file );
+    File::Size nin;
+    if( file->read( buf, bufsiz, nin ))
+        throw new PlatformException( "read failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ );
+    if( nin != bufsiz )
+        throw new Exception( "not enough bytes, reached end-of-file", __FILE__, __LINE__, __FUNCTION__ );
+}
+
+void MP4File::PeekBytes( uint8_t* buf, uint32_t bufsiz, File* file )
+{
+    const uint64_t pos = GetPosition( file );
+    ReadBytes( buf, bufsiz, file );
+    SetPosition( pos, file );
+}
+
+void MP4File::EnableMemoryBuffer( uint8_t* pBytes, uint64_t numBytes )
+{
+    ASSERT( !m_memoryBuffer );
+
+    if (pBytes) {
+        m_memoryBuffer = pBytes;
+        m_memoryBufferSize = numBytes;
+    } else {
+        if (numBytes) {
+            m_memoryBufferSize = numBytes;
+        } else {
+            m_memoryBufferSize = 4096;
+        }
+        m_memoryBuffer = (uint8_t*)MP4Malloc(m_memoryBufferSize);
+    }
+    m_memoryBufferPosition = 0;
+}
+
+void MP4File::DisableMemoryBuffer( uint8_t** ppBytes, uint64_t* pNumBytes )
+{
+    ASSERT(m_memoryBuffer != NULL);
+
+    if (ppBytes) {
+        *ppBytes = m_memoryBuffer;
+    }
+    if (pNumBytes) {
+        *pNumBytes = m_memoryBufferPosition;
+    }
+
+    m_memoryBuffer = NULL;
+    m_memoryBufferSize = 0;
+    m_memoryBufferPosition = 0;
+}
+
+void MP4File::WriteBytes( uint8_t* buf, uint32_t bufsiz, File* file )
+{
+    ASSERT( m_numWriteBits == 0 || m_numWriteBits >= 8 );
+
+    if( !buf || bufsiz == 0 )
+        return;
+
+    if( m_memoryBuffer ) {
+        if( m_memoryBufferPosition + bufsiz > m_memoryBufferSize ) {
+            m_memoryBufferSize = 2 * (m_memoryBufferSize + bufsiz);
+            m_memoryBuffer = (uint8_t*)MP4Realloc( m_memoryBuffer, m_memoryBufferSize );
+        }
+        memcpy( &m_memoryBuffer[m_memoryBufferPosition], buf, bufsiz );
+        m_memoryBufferPosition += bufsiz;
+        return;
+    }
+
+    if( !file )
+        file = m_file;
+
+    ASSERT( file );
+    File::Size nout;
+    if( file->write( buf, bufsiz, nout ))
+        throw new PlatformException( "write failed", sys::getLastError(), __FILE__, __LINE__, __FUNCTION__ );
+    if( nout != bufsiz )
+        throw new Exception( "not all bytes written", __FILE__, __LINE__, __FUNCTION__ );
+}
+
+uint64_t MP4File::ReadUInt(uint8_t size)
+{
+    switch (size) {
+    case 1:
+        return ReadUInt8();
+    case 2:
+        return ReadUInt16();
+    case 3:
+        return ReadUInt24();
+    case 4:
+        return ReadUInt32();
+    case 8:
+        return ReadUInt64();
+    default:
+        ASSERT(false);
+        return 0;
+    }
+}
+
+uint8_t MP4File::ReadUInt8()
+{
+    uint8_t data;
+    ReadBytes(&data, 1);
+    return data;
+}
+
+void MP4File::WriteUInt8(uint8_t value)
+{
+    WriteBytes(&value, 1);
+}
+
+uint16_t MP4File::ReadUInt16()
+{
+    uint8_t data[2];
+    ReadBytes(&data[0], 2);
+    return ((data[0] << 8) | data[1]);
+}
+
+void MP4File::WriteUInt16(uint16_t value)
+{
+    uint8_t data[2];
+    data[0] = (value >> 8) & 0xFF;
+    data[1] = value & 0xFF;
+    WriteBytes(data, 2);
+}
+
+uint32_t MP4File::ReadUInt24()
+{
+    uint8_t data[3];
+    ReadBytes(&data[0], 3);
+    return ((data[0] << 16) | (data[1] << 8) | data[2]);
+}
+
+void MP4File::WriteUInt24(uint32_t value)
+{
+    uint8_t data[3];
+    data[0] = (value >> 16) & 0xFF;
+    data[1] = (value >> 8) & 0xFF;
+    data[2] = value & 0xFF;
+    WriteBytes(data, 3);
+}
+
+uint32_t MP4File::ReadUInt32()
+{
+    uint8_t data[4];
+    ReadBytes(&data[0], 4);
+    return ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]);
+}
+
+void MP4File::WriteUInt32(uint32_t value)
+{
+    uint8_t data[4];
+    data[0] = (value >> 24) & 0xFF;
+    data[1] = (value >> 16) & 0xFF;
+    data[2] = (value >> 8) & 0xFF;
+    data[3] = value & 0xFF;
+    WriteBytes(data, 4);
+}
+
+uint64_t MP4File::ReadUInt64()
+{
+    uint8_t data[8];
+    uint64_t result = 0;
+    uint64_t temp;
+
+    ReadBytes(&data[0], 8);
+
+    for (int i = 0; i < 8; i++) {
+        temp = data[i];
+        result |= temp << ((7 - i) * 8);
+    }
+    return result;
+}
+
+void MP4File::WriteUInt64(uint64_t value)
+{
+    uint8_t data[8];
+
+    for (int i = 7; i >= 0; i--) {
+        data[i] = value & 0xFF;
+        value >>= 8;
+    }
+    WriteBytes(data, 8);
+}
+
+float MP4File::ReadFixed16()
+{
+    uint8_t iPart = ReadUInt8();
+    uint8_t fPart = ReadUInt8();
+
+    return iPart + (((float)fPart) / 0x100);
+}
+
+void MP4File::WriteFixed16(float value)
+{
+    if (value >= 0x100) {
+        ostringstream msg;
+        msg << value << " out of range";
+        throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    uint8_t iPart = (uint8_t)value;
+    uint8_t fPart = (uint8_t)((value - iPart) * 0x100);
+
+    WriteUInt8(iPart);
+    WriteUInt8(fPart);
+}
+
+float MP4File::ReadFixed32()
+{
+    uint16_t iPart = ReadUInt16();
+    uint16_t fPart = ReadUInt16();
+
+    return iPart + (((float)fPart) / 0x10000);
+}
+
+void MP4File::WriteFixed32(float value)
+{
+    if (value >= 0x10000) {
+        ostringstream msg;
+        msg << value << " out of range";
+        throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    uint16_t iPart = (uint16_t)value;
+    uint16_t fPart = (uint16_t)((value - iPart) * 0x10000);
+
+    WriteUInt16(iPart);
+    WriteUInt16(fPart);
+}
+
+float MP4File::ReadFloat()
+{
+    union {
+        float f;
+        uint32_t i;
+    } u;
+
+    u.i = ReadUInt32();
+    return u.f;
+}
+
+void MP4File::WriteFloat(float value)
+{
+    union {
+        float f;
+        uint32_t i;
+    } u;
+
+    u.f = value;
+    WriteUInt32(u.i);
+}
+
+char* MP4File::ReadString()
+{
+    uint32_t length = 0;
+    uint32_t alloced = 64;
+    char* data = (char*)MP4Malloc(alloced);
+
+    do {
+        if (length == alloced) {
+            data = (char*)MP4Realloc(data, alloced * 2);
+            if (data == NULL) return NULL;
+            alloced *= 2;
+        }
+        ReadBytes((uint8_t*)&data[length], 1);
+        length++;
+    } while (data[length - 1] != 0);
+
+    data = (char*)MP4Realloc(data, length);
+    return data;
+}
+
+void MP4File::WriteString(char* string)
+{
+    if (string == NULL) {
+        uint8_t zero = 0;
+        WriteBytes(&zero, 1);
+    } else {
+        WriteBytes((uint8_t*)string, (uint32_t)strlen(string) + 1);
+    }
+}
+
+char* MP4File::ReadCountedString(uint8_t charSize, bool allowExpandedCount, uint8_t fixedLength)
+{
+    uint32_t charLength;
+    if (allowExpandedCount) {
+        uint8_t b;
+        uint32_t ix = 0;
+        charLength = 0;
+        do {
+            b = ReadUInt8();
+            charLength += b;
+            ix++;
+            if (ix > 25)
+                throw new PlatformException("Counted string too long 25 * 255",ERANGE,
+                                            __FILE__, __LINE__, __FUNCTION__);
+        } while (b == 255);
+    } else {
+        charLength = ReadUInt8();
+    }
+    
+    if (fixedLength && (charLength > fixedLength - 1)) {
+        /*
+         * The counted length of this string is greater than the
+         * maxiumum fixed length, so truncate the string to the
+         * maximum fixed length amount (take 1 byte away from the
+         * fixedlength since we've already sacrificed one byte for
+         * reading the counted length, and there has been a bug where
+         * a non counted string has been used in the place of a
+         * counted string).
+         */  
+        WARNING(charLength > fixedLength - 1);
+        charLength = fixedLength - 1U;
+    }
+
+    uint32_t byteLength = charLength * charSize;
+    char* data = (char*)MP4Malloc(byteLength + 1);
+    if (byteLength > 0) {
+        ReadBytes((uint8_t*)data, byteLength);
+    }
+    data[byteLength] = '\0';
+
+    // read padding
+    if (fixedLength) {
+        const uint8_t padsize = fixedLength - byteLength -1U;
+        if( padsize ) {
+            uint8_t* padbuf = (uint8_t*)malloc( padsize );
+            ReadBytes( padbuf, padsize );
+            free( padbuf );
+        }
+    }
+
+    return data;
+}
+
+void MP4File::WriteCountedString(char* string,
+                                 uint8_t charSize, bool allowExpandedCount,
+                                 uint32_t fixedLength)
+{
+    uint32_t byteLength;
+    uint8_t zero[1];
+
+    if (string) {
+        byteLength = (uint32_t)strlen(string);
+        if (fixedLength && (byteLength >= fixedLength)) {
+            byteLength = fixedLength-1;
+        }
+    }
+    else {
+        byteLength = 0;
+    }
+    uint32_t charLength = byteLength / charSize;
+
+    if (allowExpandedCount) {
+        while (charLength >= 0xFF) {
+            WriteUInt8(0xFF);
+            charLength -= 0xFF;
+        }
+        // Write the count
+        WriteUInt8(charLength);
+    } else {
+        if (charLength > 255) {
+            ostringstream msg;
+            msg << "Length is " << charLength;
+            throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__);
+        }
+        // Write the count
+        WriteUInt8(charLength);
+    }
+
+    if (byteLength > 0) {
+        // Write the string (or the portion that we want to write)
+        WriteBytes((uint8_t*)string, byteLength);
+    }
+
+    // Write any padding if this is a fixed length counted string
+    if (fixedLength) {
+        zero[0] = 0;
+        while (byteLength < fixedLength-1U) {
+            WriteBytes(zero, 1);
+            byteLength++;
+        }
+    }
+}
+
+uint64_t MP4File::ReadBits(uint8_t numBits)
+{
+    ASSERT(numBits > 0);
+    ASSERT(numBits <= 64);
+
+    uint64_t bits = 0;
+
+    for (uint8_t i = numBits; i > 0; i--) {
+        if (m_numReadBits == 0) {
+            ReadBytes(&m_bufReadBits, 1);
+            m_numReadBits = 8;
+        }
+        bits = (bits << 1) | ((m_bufReadBits >> (--m_numReadBits)) & 1);
+    }
+
+    return bits;
+}
+
+void MP4File::FlushReadBits()
+{
+    // eat any remaining bits in the read buffer
+    m_numReadBits = 0;
+}
+
+void MP4File::WriteBits(uint64_t bits, uint8_t numBits)
+{
+    ASSERT(numBits <= 64);
+
+    for (uint8_t i = numBits; i > 0; i--) {
+        m_bufWriteBits |=
+            (((bits >> (i - 1)) & 1) << (8 - ++m_numWriteBits));
+
+        if (m_numWriteBits == 8) {
+            FlushWriteBits();
+        }
+    }
+}
+
+void MP4File::PadWriteBits(uint8_t pad)
+{
+    if (m_numWriteBits) {
+        WriteBits(pad ? 0xFF : 0x00, 8 - m_numWriteBits);
+    }
+}
+
+void MP4File::FlushWriteBits()
+{
+    if (m_numWriteBits > 0) {
+        WriteBytes(&m_bufWriteBits, 1);
+        m_numWriteBits = 0;
+        m_bufWriteBits = 0;
+    }
+}
+
+uint32_t MP4File::ReadMpegLength()
+{
+    uint32_t length = 0;
+    uint8_t numBytes = 0;
+    uint8_t b;
+
+    do {
+        b = ReadUInt8();
+        length = (length << 7) | (b & 0x7F);
+        numBytes++;
+    } while ((b & 0x80) && numBytes < 4);
+
+    return length;
+}
+
+void MP4File::WriteMpegLength(uint32_t value, bool compact)
+{
+    if (value > 0x0FFFFFFF) {
+        ostringstream msg;
+        msg << "out of range: " << value;
+        throw new PlatformException(msg.str().c_str(), ERANGE, __FILE__, __LINE__, __FUNCTION__ ); 
+    }
+
+    int8_t numBytes;
+
+    if (compact) {
+        if (value <= 0x7F) {
+            numBytes = 1;
+        } else if (value <= 0x3FFF) {
+            numBytes = 2;
+        } else if (value <= 0x1FFFFF) {
+            numBytes = 3;
+        } else {
+            numBytes = 4;
+        }
+    } else {
+        numBytes = 4;
+    }
+
+    int8_t i = numBytes;
+    do {
+        i--;
+        uint8_t b = (value >> (i * 7)) & 0x7F;
+        if (i > 0) {
+            b |= 0x80;
+        }
+        WriteUInt8(b);
+    } while (i > 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/mp4info.cpp b/src/mp4info.cpp
new file mode 100644
index 0000000..676a5be
--- /dev/null
+++ b/src/mp4info.cpp
@@ -0,0 +1,625 @@
+/*
+ * 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-2002.  All Rights Reserved.
+ *
+ * Portions created by Ximpo Group Ltd. are
+ * Copyright (C) Ximpo Group Ltd. 2003, 2004.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie               dmackie@cisco.com
+ *              Bill May                  wmay@cisco.com
+ *      Alix Marchandise-Franquet alix@cisco.com
+ *      Ximpo Group Ltd.          mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+static char* PrintAudioInfo(
+    MP4FileHandle mp4File,
+    MP4TrackId trackId)
+{
+    static const char* mpeg4AudioNames[] = {
+        "MPEG-4 AAC main",
+        "MPEG-4 AAC LC",
+        "MPEG-4 AAC SSR",
+        "MPEG-4 AAC LTP",
+        "MPEG-4 AAC HE",
+        "MPEG-4 AAC Scalable",
+        "MPEG-4 TwinVQ",
+        "MPEG-4 CELP",
+        "MPEG-4 HVXC",
+        NULL, NULL,
+        "MPEG-4 TTSI",
+        "MPEG-4 Main Synthetic",
+        "MPEG-4 Wavetable Syn",
+        "MPEG-4 General MIDI",
+        "MPEG-4 Algo Syn and Audio FX",
+        "MPEG-4 ER AAC LC",
+        NULL,
+        "MPEG-4 ER AAC LTP",
+        "MPEG-4 ER AAC Scalable",
+        "MPEG-4 ER TwinVQ",
+        "MPEG-4 ER BSAC",
+        "MPEG-4 ER ACC LD",
+        "MPEG-4 ER CELP",
+        "MPEG-4 ER HVXC",
+        "MPEG-4 ER HILN",
+        "MPEG-4 ER Parametric",
+        "MPEG-4 SSC",
+        "MPEG-4 PS",
+        "MPEG-4 MPEG Surround",
+        NULL,
+        "MPEG-4 Layer-1",
+        "MPEG-4 Layer-2",
+        "MPEG-4 Layer-3",
+        "MPEG-4 DST",
+        "MPEG-4 Audio Lossless",
+        "MPEG-4 SLS",
+        "MPEG-4 SLS non-core",
+    };
+
+    static const uint8_t mpegAudioTypes[] = {
+        MP4_MPEG2_AAC_MAIN_AUDIO_TYPE,  // 0x66
+        MP4_MPEG2_AAC_LC_AUDIO_TYPE,    // 0x67
+        MP4_MPEG2_AAC_SSR_AUDIO_TYPE,   // 0x68
+        MP4_MPEG2_AUDIO_TYPE,           // 0x69
+        MP4_MPEG1_AUDIO_TYPE,           // 0x6B
+        // private types
+        MP4_PCM16_LITTLE_ENDIAN_AUDIO_TYPE,
+        MP4_VORBIS_AUDIO_TYPE,
+        MP4_ALAW_AUDIO_TYPE,
+        MP4_ULAW_AUDIO_TYPE,
+        MP4_G723_AUDIO_TYPE,
+        MP4_PCM16_BIG_ENDIAN_AUDIO_TYPE,
+    };
+    static const char* mpegAudioNames[] = {
+        "MPEG-2 AAC Main",
+        "MPEG-2 AAC LC",
+        "MPEG-2 AAC SSR",
+        "MPEG-2 Audio (13818-3)",
+        "MPEG-1 Audio (11172-3)",
+        // private types
+        "PCM16 (little endian)",
+        "Vorbis",
+        "G.711 aLaw",
+        "G.711 uLaw",
+        "G.723.1",
+        "PCM16 (big endian)",
+    };
+    uint8_t numMpegAudioTypes =
+        sizeof(mpegAudioTypes) / sizeof(uint8_t);
+
+    const char* typeName = "Unknown";
+    bool foundType = false;
+    uint8_t type = 0;
+    const char *media_data_name = NULL;
+
+    media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
+
+    if (media_data_name == NULL) {
+        typeName = "Unknown - no media data name";
+    } else if (strcasecmp(media_data_name, "samr") == 0) {
+        typeName = "AMR";
+        foundType = true;
+    } else if (strcasecmp(media_data_name, "sawb") == 0) {
+        typeName = "AMR-WB";
+        foundType = true;
+    } else if (strcasecmp(media_data_name, "mp4a") == 0) {
+
+        type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId);
+        switch (type) {
+        case MP4_INVALID_AUDIO_TYPE:
+            typeName = "AAC from .mov";
+            foundType = true;
+            break;
+        case MP4_MPEG4_AUDIO_TYPE:  {
+
+            type = MP4GetTrackAudioMpeg4Type(mp4File, trackId);
+            if (type == MP4_MPEG4_INVALID_AUDIO_TYPE ||
+                    type > NUM_ELEMENTS_IN_ARRAY(mpeg4AudioNames) ||
+                    mpeg4AudioNames[type - 1] == NULL) {
+                typeName = "MPEG-4 Unknown Profile";
+            } else {
+                typeName = mpeg4AudioNames[type - 1];
+                foundType = true;
+            }
+            break;
+        }
+        // fall through
+        default:
+            for (uint8_t i = 0; i < numMpegAudioTypes; i++) {
+                if (type == mpegAudioTypes[i]) {
+                    typeName = mpegAudioNames[i];
+                    foundType = true;
+                    break;
+                }
+            }
+        }
+    } else {
+        typeName = media_data_name;
+        foundType = true;
+    }
+
+    uint32_t timeScale =
+        MP4GetTrackTimeScale(mp4File, trackId);
+
+    MP4Duration trackDuration =
+        MP4GetTrackDuration(mp4File, trackId);
+
+    double msDuration =
+        double(MP4ConvertFromTrackDuration(mp4File, trackId,
+                                           trackDuration, MP4_MSECS_TIME_SCALE));
+
+    uint32_t avgBitRate =
+        MP4GetTrackBitRate(mp4File, trackId);
+
+    char *sInfo = (char*)MP4Malloc(256);
+
+    // type duration avgBitrate samplingFrequency
+    if (foundType)
+        snprintf(sInfo, 256,
+                 "%u\taudio\t%s%s, %.3f secs, %u kbps, %u Hz\n",
+                 trackId,
+                 MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "enca - " : "",
+                 typeName,
+                 msDuration / 1000.0,
+                 (avgBitRate + 500) / 1000,
+                 timeScale);
+    else
+        snprintf(sInfo, 256,
+                 "%u\taudio\t%s%s(%u), %.3f secs, %u kbps, %u Hz\n",
+                 trackId,
+                 MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "enca - " : "",
+                 typeName,
+                 type,
+                 msDuration / 1000.0,
+                 (avgBitRate + 500) / 1000,
+                 timeScale);
+
+    return sInfo;
+}
+static const struct {
+    uint8_t profile;
+    const char *name;
+} VisualProfileToName[] = {
+    { MPEG4_SP_L1, "MPEG-4 Simple @ L1"},
+    { MPEG4_SP_L2, "MPEG-4 Simple @ L2" },
+    { MPEG4_SP_L3, "MPEG-4 Simple @ L3" },
+    { MPEG4_SP_L0, "MPEG-4 Simple @ L0" },
+    { MPEG4_SSP_L1, "MPEG-4 Simple Scalable @ L1"},
+    { MPEG4_SSP_L2, "MPEG-4 Simple Scalable @ L2" },
+    { MPEG4_CP_L1, "MPEG-4 Core @ L1"},
+    { MPEG4_CP_L2, "MPEG-4 Core @ L2"},
+    { MPEG4_MP_L2, "MPEG-4 Main @ L2"},
+    { MPEG4_MP_L3, "MPEG-4 Main @ L3"},
+    { MPEG4_MP_L4, "MPEG-4 Main @ L4"},
+    { MPEG4_NBP_L2, "MPEG-4 N-bit @ L2"},
+    { MPEG4_STP_L1, "MPEG-4  Scalable Texture @ L1"},
+    { MPEG4_SFAP_L1, "MPEG-4 Simple Face Anim @ L1"},
+    { MPEG4_SFAP_L2, "MPEG-4  Simple Face Anim @ L2"},
+    { MPEG4_SFBAP_L1, "MPEG-4  Simple FBA @ L1"},
+    { MPEG4_SFBAP_L2, "MPEG-4 Simple FBA @ L2"},
+    { MPEG4_BATP_L1, "MPEG-4 Basic Anim Text @ L1"},
+    { MPEG4_BATP_L2, "MPEG-4 Basic Anim Text @ L2"},
+    { MPEG4_HP_L1, "MPEG-4 Hybrid @ L1"},
+    { MPEG4_HP_L2, "MPEG-4 Hybrid @ L2"},
+    { MPEG4_ARTSP_L1, "MPEG-4 Adv RT Simple @ L1"},
+    { MPEG4_ARTSP_L2, "MPEG-4 Adv RT Simple @ L2"},
+    { MPEG4_ARTSP_L3, "MPEG-4 Adv RT Simple @ L3"},
+    { MPEG4_ARTSP_L4, "MPEG-4 Adv RT Simple @ L4"},
+    { MPEG4_CSP_L1, "MPEG-4 Core Scalable @ L1"},
+    { MPEG4_CSP_L2, "MPEG-4 Core Scalable @ L2"},
+    { MPEG4_CSP_L3, "MPEG-4 Core Scalable @ L3"},
+    { MPEG4_ACEP_L1, "MPEG-4 Adv Coding Efficieny @ L1"},
+    { MPEG4_ACEP_L2, "MPEG-4 Adv Coding Efficieny @ L2"},
+    { MPEG4_ACEP_L3, "MPEG-4 Adv Coding Efficieny @ L3"},
+    { MPEG4_ACEP_L4, "MPEG-4 Adv Coding Efficieny @ L4"},
+    { MPEG4_ACP_L1, "MPEG-4 Adv Core Profile @ L1"},
+    { MPEG4_ACP_L2, "MPEG-4 Adv Core Profile @ L2"},
+    { MPEG4_AST_L1, "MPEG-4 Adv Scalable Texture @ L1"},
+    { MPEG4_AST_L2, "MPEG-4 Adv Scalable Texture @ L2"},
+    { MPEG4_AST_L3, "MPEG-4 Adv Scalable Texture @ L3"},
+    { MPEG4_S_STUDIO_P_L1, "MPEG-4 Simple Studio @ L1"},
+    { MPEG4_S_STUDIO_P_L2, "MPEG-4 Simple Studio @ L2"},
+    { MPEG4_S_STUDIO_P_L3, "MPEG-4 Simple Studio @ L3"},
+    { MPEG4_S_STUDIO_P_L4, "MPEG-4 Simple Studio @ L4"},
+    { MPEG4_C_STUDIO_P_L1, "MPEG-4 Core Studio @ L1"},
+    { MPEG4_C_STUDIO_P_L2, "MPEG-4 Core Studio @ L2"},
+    { MPEG4_C_STUDIO_P_L3, "MPEG-4 Core Studio @ L3"},
+    { MPEG4_C_STUDIO_P_L4, "MPEG-4 Core Studio @ L4"},
+    { MPEG4_ASP_L0, "MPEG-4 Adv Simple@L0"},
+    { MPEG4_ASP_L1, "MPEG-4 Adv Simple@L1"},
+    { MPEG4_ASP_L2, "MPEG-4 Adv Simple@L2"},
+    { MPEG4_ASP_L3, "MPEG-4 Adv Simple@L3"},
+    { MPEG4_ASP_L4, "MPEG-4 Adv Simple@L4"},
+    { MPEG4_ASP_L5, "MPEG-4 Adv Simple@L5"},
+    { MPEG4_ASP_L3B, "MPEG-4 Adv Simple@L3b"},
+    { MPEG4_FGSP_L0, "MPEG-4 FGS @ L0" },
+    { MPEG4_FGSP_L1, "MPEG-4 FGS @ L1" },
+    { MPEG4_FGSP_L2, "MPEG-4 FGS @ L2" },
+    { MPEG4_FGSP_L3, "MPEG-4 FGS @ L3" },
+    { MPEG4_FGSP_L4, "MPEG-4 FGS @ L4" },
+    { MPEG4_FGSP_L5, "MPEG-4 FGS @ L5" }
+};
+
+static const char *Mpeg4VisualProfileName (uint8_t visual_profile)
+{
+    size_t size = sizeof(VisualProfileToName) / sizeof(*VisualProfileToName);
+
+    for (size_t ix = 0; ix < size; ix++) {
+        if (visual_profile == VisualProfileToName[ix].profile) {
+            return (VisualProfileToName[ix].name);
+        }
+    }
+    return (NULL);
+}
+static char* PrintVideoInfo(
+    MP4FileHandle mp4File,
+    MP4TrackId trackId)
+{
+
+    static const uint8_t mpegVideoTypes[] = {
+        MP4_MPEG2_SIMPLE_VIDEO_TYPE,    // 0x60
+        MP4_MPEG2_MAIN_VIDEO_TYPE,      // 0x61
+        MP4_MPEG2_SNR_VIDEO_TYPE,       // 0x62
+        MP4_MPEG2_SPATIAL_VIDEO_TYPE,   // 0x63
+        MP4_MPEG2_HIGH_VIDEO_TYPE,      // 0x64
+        MP4_MPEG2_442_VIDEO_TYPE,       // 0x65
+        MP4_MPEG1_VIDEO_TYPE,           // 0x6A
+        MP4_JPEG_VIDEO_TYPE,            // 0x6C
+        MP4_YUV12_VIDEO_TYPE,
+        MP4_H263_VIDEO_TYPE,
+        MP4_H261_VIDEO_TYPE,
+    };
+    static const char* mpegVideoNames[] = {
+        "MPEG-2 Simple",
+        "MPEG-2 Main",
+        "MPEG-2 SNR",
+        "MPEG-2 Spatial",
+        "MPEG-2 High",
+        "MPEG-2 4:2:2",
+        "MPEG-1",
+        "JPEG",
+        "YUV12",
+        "H.263",
+        "H.261",
+    };
+    uint8_t numMpegVideoTypes =
+        sizeof(mpegVideoTypes) / sizeof(uint8_t);
+    bool foundTypeName = false;
+    const char* typeName = "Unknown";
+
+    const char *media_data_name = NULL;
+    char originalFormat[8];
+    char  oformatbuffer[32];
+    originalFormat[0] = 0;
+    *oformatbuffer = 0;
+    uint8_t type = 0;
+
+    media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
+    // encv 264b
+    if (media_data_name != NULL && strcasecmp(media_data_name, "encv") == 0) {
+        if (MP4GetTrackMediaDataOriginalFormat(mp4File,
+                                               trackId,
+                                               originalFormat,
+                                               sizeof(originalFormat)) == false)
+            media_data_name = NULL;
+
+    }
+
+    char  typebuffer[80];
+    if (media_data_name == NULL) {
+        typeName = "Unknown - no media data name";
+        foundTypeName = true;
+    } else if ((strcasecmp(media_data_name, "avc1") == 0) ||
+               (strcasecmp(originalFormat, "264b") == 0)) {
+        // avc
+        uint8_t profile, level;
+        char profileb[20], levelb[20];
+        if (MP4GetTrackH264ProfileLevel(mp4File, trackId,
+                                        &profile, &level)) {
+            if (profile == 66) {
+                strcpy(profileb, "Baseline");
+            } else if (profile == 77) {
+                strcpy(profileb, "Main");
+            } else if (profile == 88) {
+                strcpy(profileb, "Extended");
+            } else if (profile == 100) {
+                strcpy(profileb, "High");
+            } else if (profile == 110) {
+                strcpy(profileb, "High 10");
+            } else if (profile == 122) {
+                strcpy(profileb, "High 4:2:2");
+            } else if (profile == 144) {
+                strcpy(profileb, "High 4:4:4");
+            } else {
+                snprintf(profileb, 20, "Unknown Profile %x", profile);
+            }
+            switch (level) {
+            case 10:
+            case 20:
+            case 30:
+            case 40:
+            case 50:
+                snprintf(levelb, 20, "%u", level / 10);
+                break;
+            case 11:
+            case 12:
+            case 13:
+            case 21:
+            case 22:
+            case 31:
+            case 32:
+            case 41:
+            case 42:
+            case 51:
+                snprintf(levelb, 20, "%u.%u", level / 10, level % 10);
+                break;
+            default:
+                snprintf(levelb, 20, "unknown level %x", level);
+                break;
+            }
+            if (originalFormat != NULL && originalFormat[0] != '\0')
+                snprintf(oformatbuffer, 32, "(%s) ", originalFormat);
+            snprintf(typebuffer, sizeof(typebuffer), "H264 %s%s@%s",
+                     oformatbuffer, profileb, levelb);
+            typeName = typebuffer;
+        } else {
+            typeName = "H.264 - profile/level error";
+        }
+        foundTypeName = true;
+    } else if (strcasecmp(media_data_name, "s263") == 0) {
+        // 3gp h.263
+        typeName = "H.263";
+        foundTypeName = true;
+    } else if ((strcasecmp(media_data_name, "mp4v") == 0) ||
+               (strcasecmp(media_data_name, "encv") == 0)) {
+        // note encv might needs it's own field eventually.
+        type = MP4GetTrackEsdsObjectTypeId(mp4File, trackId);
+        if (type == MP4_MPEG4_VIDEO_TYPE) {
+            type = MP4GetVideoProfileLevel(mp4File, trackId);
+            typeName = Mpeg4VisualProfileName(type);
+            if (typeName == NULL) {
+                typeName = "MPEG-4 Unknown Profile";
+            } else {
+                foundTypeName = true;
+            }
+        } else {
+            for (uint8_t i = 0; i < numMpegVideoTypes; i++) {
+                if (type == mpegVideoTypes[i]) {
+                    typeName = mpegVideoNames[i];
+                    foundTypeName = true;
+                    break;
+                }
+            }
+        }
+    } else {
+        typeName = media_data_name;
+        foundTypeName = true; // we don't have a type value to display
+    }
+
+    MP4Duration trackDuration =
+        MP4GetTrackDuration(mp4File, trackId);
+
+    double msDuration =
+        double(MP4ConvertFromTrackDuration(mp4File, trackId,
+                                           trackDuration, MP4_MSECS_TIME_SCALE));
+
+    uint32_t avgBitRate =
+        MP4GetTrackBitRate(mp4File, trackId);
+
+    // Note not all mp4 implementations set width and height correctly
+    // The real answer can be buried inside the ES configuration info
+    uint16_t width = MP4GetTrackVideoWidth(mp4File, trackId);
+
+    uint16_t height = MP4GetTrackVideoHeight(mp4File, trackId);
+
+    double fps = MP4GetTrackVideoFrameRate(mp4File, trackId);
+
+    char *sInfo = (char*)MP4Malloc(256);
+
+    // type duration avgBitrate frameSize frameRate
+    if (foundTypeName) {
+        sprintf(sInfo,
+                "%u\tvideo\t%s%s, %.3f secs, %u kbps, %ux%u @ %f fps\n",
+                trackId,
+                MP4IsIsmaCrypMediaTrack(mp4File, trackId) ? "encv - " : "",
+                typeName,
+                msDuration / 1000.0,
+                (avgBitRate + 500) / 1000,
+                width,
+                height,
+                fps
+               );
+    } else {
+        sprintf(sInfo,
+                "%u\tvideo\t%s(%u), %.3f secs, %u kbps, %ux%u @ %f fps\n",
+                trackId,
+                typeName,
+                type,
+                msDuration / 1000.0,
+                (avgBitRate + 500) / 1000,
+                width,
+                height,
+                fps
+               );
+    }
+
+    return sInfo;
+}
+static char* PrintCntlInfo(
+    MP4FileHandle mp4File,
+    MP4TrackId trackId)
+{
+    const char *media_data_name = MP4GetTrackMediaDataName(mp4File, trackId);
+    const char *typeName = "Unknown";
+
+    if (media_data_name == NULL) {
+        typeName = "Unknown - no media data name";
+    } else if (strcasecmp(media_data_name, "href") == 0) {
+        typeName = "ISMA Href";
+    } else {
+        typeName = media_data_name;
+    }
+
+    MP4Duration trackDuration =
+        MP4GetTrackDuration(mp4File, trackId);
+
+    double msDuration =
+        double(MP4ConvertFromTrackDuration(mp4File, trackId,
+                                           trackDuration, MP4_MSECS_TIME_SCALE));
+    char *sInfo = (char *)MP4Malloc(256);
+
+    snprintf(sInfo, 256,
+             "%u\tcontrol\t%s, %.3f secs\n",
+             trackId,
+             typeName,
+             msDuration / 1000.0);
+    return sInfo;
+}
+
+
+static char* PrintHintInfo(
+    MP4FileHandle mp4File,
+    MP4TrackId trackId)
+{
+    MP4TrackId referenceTrackId =
+        MP4GetHintTrackReferenceTrackId(mp4File, trackId);
+
+    char* payloadName = NULL;
+    if (!MP4GetHintTrackRtpPayload(mp4File, trackId, &payloadName))
+        return NULL;
+
+    char *sInfo = (char*)MP4Malloc(256);
+
+    snprintf(sInfo, 256,
+             "%u\thint\tPayload %s for track %u\n",
+             trackId,
+             payloadName,
+             referenceTrackId);
+
+    free(payloadName);
+
+    return sInfo;
+}
+
+static char* PrintTrackInfo(
+    MP4FileHandle mp4File,
+    MP4TrackId trackId)
+{
+    char* trackInfo = NULL;
+
+    const char* trackType =
+        MP4GetTrackType(mp4File, trackId);
+    if (trackType == NULL) return NULL;
+
+    if (!strcmp(trackType, MP4_AUDIO_TRACK_TYPE)) {
+        trackInfo = PrintAudioInfo(mp4File, trackId);
+    } else if (!strcmp(trackType, MP4_VIDEO_TRACK_TYPE)) {
+        trackInfo = PrintVideoInfo(mp4File, trackId);
+    } else if (!strcmp(trackType, MP4_HINT_TRACK_TYPE)) {
+        trackInfo = PrintHintInfo(mp4File, trackId);
+    } else if (strcmp(trackType, MP4_CNTL_TRACK_TYPE) == 0) {
+        trackInfo = PrintCntlInfo(mp4File, trackId);
+    } else {
+        trackInfo = (char*)MP4Malloc(256);
+        if (!strcmp(trackType, MP4_OD_TRACK_TYPE)) {
+            snprintf(trackInfo, 256,
+                     "%u\tod\tObject Descriptors\n",
+                     trackId);
+        } else if (!strcmp(trackType, MP4_SCENE_TRACK_TYPE)) {
+            snprintf(trackInfo, 256,
+                     "%u\tscene\tBIFS\n",
+                     trackId);
+        } else {
+            snprintf(trackInfo, 256,
+                     "%u\t%s\n",
+                     trackId, trackType);
+        }
+    }
+
+    return trackInfo;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+///////////////////////////////////////////////////////////////////////////////
+
+using namespace mp4v2::impl;
+
+extern "C"
+char* MP4Info(
+        MP4FileHandle mp4File,
+        MP4TrackId    trackId )
+{
+    char* info = NULL;
+
+    if (MP4_IS_VALID_FILE_HANDLE(mp4File)) {
+        try {
+            if (trackId == MP4_INVALID_TRACK_ID) {
+                uint32_t buflen = 4 * 1024;
+                info = (char*)MP4Calloc(buflen);
+
+                buflen -= snprintf(info, buflen,
+                                   "Track\tType\tInfo\n");
+
+                uint32_t numTracks = MP4GetNumberOfTracks(mp4File);
+
+                for (uint32_t i = 0; i < numTracks; i++) {
+                    trackId = MP4FindTrackId(mp4File, i);
+                    char* trackInfo = PrintTrackInfo(mp4File, trackId);
+                    strncat(info, trackInfo, buflen);
+                    uint32_t newlen = (uint32_t)strlen(trackInfo);
+                    if (newlen > buflen) buflen = 0;
+                    else buflen -= newlen;
+                    MP4Free(trackInfo);
+                }
+            } else {
+                info = PrintTrackInfo(mp4File, trackId);
+            }
+        }
+        catch (Exception* x) {
+            mp4v2::impl::log.errorf(*x);
+            delete x;
+        }
+    }
+
+    return info;
+}
+
+extern "C"
+char* MP4FileInfo(
+    const char* fileName,
+    MP4TrackId  trackId )
+{
+    MP4FileHandle mp4File = MP4Read(fileName);
+
+    if (!mp4File) {
+        return NULL;
+    }
+
+    char* info = MP4Info(mp4File, trackId);
+
+    MP4Close(mp4File);
+
+    return info;    // caller should free this
+}
diff --git a/src/mp4property.cpp b/src/mp4property.cpp
new file mode 100644
index 0000000..9a5b1e3
--- /dev/null
+++ b/src/mp4property.cpp
@@ -0,0 +1,1190 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ *      Kona Blend      kona8lend@@gmail.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4Property::MP4Property(MP4Atom& parentAtom, const char* name)
+    : m_parentAtom(parentAtom)
+{
+    m_name = name;
+    m_readOnly = false;
+    m_implicit = false;
+}
+
+bool MP4Property::FindProperty(const char* name,
+                               MP4Property** ppProperty, uint32_t* pIndex)
+{
+    if (name == NULL) {
+        return false;
+    }
+
+    if (!strcasecmp(m_name, name)) {
+        log.verbose1f("\"%s\": FindProperty: matched %s", 
+                      m_parentAtom.GetFile().GetFilename().c_str(), name);
+        *ppProperty = this;
+        return true;
+    }
+    return false;
+}
+
+// Integer Property
+
+uint64_t MP4IntegerProperty::GetValue(uint32_t index)
+{
+    switch (this->GetType()) {
+    case Integer8Property:
+        return ((MP4Integer8Property*)this)->GetValue(index);
+    case Integer16Property:
+        return ((MP4Integer16Property*)this)->GetValue(index);
+    case Integer24Property:
+        return ((MP4Integer24Property*)this)->GetValue(index);
+    case Integer32Property:
+        return ((MP4Integer32Property*)this)->GetValue(index);
+    case Integer64Property:
+        return ((MP4Integer64Property*)this)->GetValue(index);
+    default:
+        ASSERT(false);
+    }
+    return (0);
+}
+
+void MP4IntegerProperty::SetValue(uint64_t value, uint32_t index)
+{
+    switch (this->GetType()) {
+    case Integer8Property:
+        ((MP4Integer8Property*)this)->SetValue(value, index);
+        break;
+    case Integer16Property:
+        ((MP4Integer16Property*)this)->SetValue(value, index);
+        break;
+    case Integer24Property:
+        ((MP4Integer24Property*)this)->SetValue(value, index);
+        break;
+    case Integer32Property:
+        ((MP4Integer32Property*)this)->SetValue(value, index);
+        break;
+    case Integer64Property:
+        ((MP4Integer64Property*)this)->SetValue(value, index);
+        break;
+    default:
+        ASSERT(false);
+    }
+}
+
+void MP4IntegerProperty::InsertValue(uint64_t value, uint32_t index)
+{
+    switch (this->GetType()) {
+    case Integer8Property:
+        ((MP4Integer8Property*)this)->InsertValue(value, index);
+        break;
+    case Integer16Property:
+        ((MP4Integer16Property*)this)->InsertValue(value, index);
+        break;
+    case Integer24Property:
+        ((MP4Integer24Property*)this)->InsertValue(value, index);
+        break;
+    case Integer32Property:
+        ((MP4Integer32Property*)this)->InsertValue(value, index);
+        break;
+    case Integer64Property:
+        ((MP4Integer64Property*)this)->InsertValue(value, index);
+        break;
+    default:
+        ASSERT(false);
+    }
+}
+
+void MP4IntegerProperty::DeleteValue(uint32_t index)
+{
+    switch (this->GetType()) {
+    case Integer8Property:
+        ((MP4Integer8Property*)this)->DeleteValue(index);
+        break;
+    case Integer16Property:
+        ((MP4Integer16Property*)this)->DeleteValue(index);
+        break;
+    case Integer24Property:
+        ((MP4Integer24Property*)this)->DeleteValue(index);
+        break;
+    case Integer32Property:
+        ((MP4Integer32Property*)this)->DeleteValue(index);
+        break;
+    case Integer64Property:
+        ((MP4Integer64Property*)this)->DeleteValue(index);
+        break;
+    default:
+        ASSERT(false);
+    }
+}
+
+void MP4IntegerProperty::IncrementValue(int32_t increment, uint32_t index)
+{
+    SetValue(GetValue() + increment);
+}
+
+void MP4Integer8Property::Dump(uint8_t indent,
+                               bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%02x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index], m_values[index]);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%02x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index], m_values[index]);
+}
+
+void MP4Integer16Property::Dump(uint8_t indent,
+                                bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%04x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index], m_values[index]);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%04x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index], m_values[index]);
+}
+
+void MP4Integer24Property::Dump(uint8_t indent,
+                                bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%06x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index], m_values[index]);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%06x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index], m_values[index]);
+}
+
+void MP4Integer32Property::Dump(uint8_t indent,
+                                bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %u (0x%08x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index], m_values[index]);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %u (0x%08x)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index], m_values[index]);
+}
+
+void MP4Integer64Property::Dump(uint8_t indent,
+                                bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %" PRIu64 " (0x%016" PRIx64 ")",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index], m_values[index]);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %" PRIu64 " (0x%016" PRIx64 ")",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index], m_values[index]);
+}
+
+// MP4BitfieldProperty
+
+void MP4BitfieldProperty::Read(MP4File& file, uint32_t index)
+{
+    if (m_implicit) {
+        return;
+    }
+    m_values[index] = file.ReadBits(m_numBits);
+}
+
+void MP4BitfieldProperty::Write(MP4File& file, uint32_t index)
+{
+    if (m_implicit) {
+        return;
+    }
+    file.WriteBits(m_values[index], m_numBits);
+}
+
+void MP4BitfieldProperty::Dump(uint8_t indent,
+                               bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    uint8_t hexWidth = m_numBits / 4;
+    if (hexWidth == 0 || (m_numBits % 4)) {
+        hexWidth++;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1,
+                 "\"%s\": %s[%u] = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits>",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index], (int)hexWidth, m_values[index], m_numBits);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1,
+                 "\"%s\": %s = %" PRIu64 " (0x%0*" PRIx64 ") <%u bits>",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index], (int)hexWidth, m_values[index], m_numBits);
+}
+
+// MP4Float32Property
+
+void MP4Float32Property::Read(MP4File& file, uint32_t index)
+{
+    if (m_implicit) {
+        return;
+    }
+    if (m_useFixed16Format) {
+        m_values[index] = file.ReadFixed16();
+    } else if (m_useFixed32Format) {
+        m_values[index] = file.ReadFixed32();
+    } else {
+        m_values[index] = file.ReadFloat();
+    }
+}
+
+void MP4Float32Property::Write(MP4File& file, uint32_t index)
+{
+    if (m_implicit) {
+        return;
+    }
+    if (m_useFixed16Format) {
+        file.WriteFixed16(m_values[index]);
+    } else if (m_useFixed32Format) {
+        file.WriteFixed32(m_values[index]);
+    } else {
+        file.WriteFloat(m_values[index]);
+    }
+}
+
+void MP4Float32Property::Dump(uint8_t indent,
+                              bool dumpImplicits, uint32_t index)
+{
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+    if (index != 0)
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u] = %f",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, index, m_values[index]);
+    else
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %f",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, m_values[index]);
+}
+
+// MP4StringProperty
+
+MP4StringProperty::MP4StringProperty(
+    MP4Atom& parentAtom,
+    const char* name,
+    bool        useCountedFormat,
+    bool        useUnicode,
+    bool        arrayMode )
+
+    : MP4Property( parentAtom, name )
+    , m_arrayMode        ( arrayMode )
+    , m_useCountedFormat ( useCountedFormat )
+    , m_useExpandedCount ( false )
+    , m_useUnicode       ( useUnicode )
+    , m_fixedLength      ( 0 )
+{
+    SetCount( 1 );
+    m_values[0] = NULL;
+}
+
+MP4StringProperty::~MP4StringProperty()
+{
+    uint32_t count = GetCount();
+    for (uint32_t i = 0; i < count; i++) {
+        MP4Free(m_values[i]);
+    }
+}
+
+void MP4StringProperty::SetCount(uint32_t count)
+{
+    uint32_t oldCount = m_values.Size();
+
+    m_values.Resize(count);
+
+    for (uint32_t i = oldCount; i < count; i++) {
+        m_values[i] = NULL;
+    }
+}
+
+void MP4StringProperty::SetValue(const char* value, uint32_t index)
+{
+    if (m_readOnly) {
+        ostringstream msg;
+        msg << "property " << m_name << "is read-only";
+        throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    MP4Free(m_values[index]);
+
+    if (m_fixedLength) {
+        m_values[index] = (char*)MP4Calloc(m_fixedLength + 1);
+        if (value) {
+            strncpy(m_values[index], value, m_fixedLength);
+        }
+    } else {
+        if (value) {
+            m_values[index] = MP4Stralloc(value);
+        } else {
+            m_values[index] = NULL;
+        }
+    }
+}
+
+void MP4StringProperty::Read( MP4File& file, uint32_t index )
+{
+    if( m_implicit )
+        return;
+
+    uint32_t begin = index;
+    uint32_t max   = index + 1;
+
+    if( m_arrayMode ) {
+        begin = 0;
+        max   = GetCount();
+    }
+
+    for( uint32_t i = begin; i < max; i++ ) {
+        char*& value = m_values[i];
+
+        // Generally a default atom setting, e.g. see atom_avc1.cpp, "JVT/AVC Coding"; we'll leak this string if
+        // we don't free.  Note that MP4Free checks for null.
+        MP4Free(value); 
+
+        if( m_useCountedFormat ) {
+            value = file.ReadCountedString( (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
+        }
+        else if( m_fixedLength ) {
+            value = (char*)MP4Calloc( m_fixedLength + 1 );
+            file.ReadBytes( (uint8_t*)value, m_fixedLength );
+        }
+        else {
+            value = file.ReadString();
+        }
+    }
+}
+
+void MP4StringProperty::Write( MP4File& file, uint32_t index )
+{
+    if( m_implicit )
+        return;
+
+    uint32_t begin = index;
+    uint32_t max   = index + 1;
+
+    if( m_arrayMode ) {
+        begin = 0;
+        max   = GetCount();
+    }
+
+    for( uint32_t i = begin; i < max; i++ ) {
+        char*& value = m_values[i];
+
+        if( m_useCountedFormat ) {
+            file.WriteCountedString( value, (m_useUnicode ? 2 : 1), m_useExpandedCount, m_fixedLength );
+        }
+        else if( m_fixedLength ) {
+            file.WriteBytes( (uint8_t*)value, m_fixedLength );
+        }
+        else {
+            file.WriteString( value );
+        }
+    }
+}
+
+void MP4StringProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
+{
+    if( m_implicit && !dumpImplicits )
+        return;
+
+    if( !m_arrayMode ) {
+        char indexd[32];
+        if( index != 0 )
+            snprintf( indexd, 32, "[%u]", index );
+        else
+            indexd[0] = '\0';
+
+        if( m_useUnicode )
+            log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s%s = %ls",
+                     m_parentAtom.GetFile().GetFilename().c_str(),
+                     m_name, indexd, (wchar_t*)m_values[index] );
+        else
+            log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s%s = %s",
+                     m_parentAtom.GetFile().GetFilename().c_str(),
+                     m_name, indexd, m_values[index] );
+    }
+    else if( log.verbosity >= MP4_LOG_VERBOSE2 )
+    {
+        const uint32_t max = GetCount();
+
+        log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s (size=%u)",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, max );
+
+        for( uint32_t i = 0; i < max; i++ ) {
+            char*& value = m_values[i];
+
+            if( m_useUnicode )
+                log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s[%u] = %ls",
+                         m_parentAtom.GetFile().GetFilename().c_str(),
+                         m_name, i, (wchar_t*)value );
+            else
+                log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s[%u] = %s",
+                         m_parentAtom.GetFile().GetFilename().c_str(),
+                         m_name, i, value );
+        }
+    }
+    else {
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": <table entries suppressed>",
+                 m_parentAtom.GetFile().GetFilename().c_str() );
+    }
+}
+
+// MP4BytesProperty
+
+MP4BytesProperty::MP4BytesProperty(MP4Atom& parentAtom, const char* name, uint32_t valueSize,
+                                   uint32_t defaultValueSize)
+        : MP4Property(parentAtom, name)
+        , m_fixedValueSize(0)
+        , m_defaultValueSize(defaultValueSize)
+{
+    SetCount(1);
+    m_values[0] = (uint8_t*)MP4Calloc(valueSize);
+    m_valueSizes[0] = valueSize;
+}
+
+MP4BytesProperty::~MP4BytesProperty()
+{
+    uint32_t count = GetCount();
+    for (uint32_t i = 0; i < count; i++) {
+        MP4Free(m_values[i]);
+    }
+}
+
+void MP4BytesProperty::SetCount(uint32_t count)
+{
+    uint32_t oldCount = m_values.Size();
+
+    m_values.Resize(count);
+    m_valueSizes.Resize(count);
+
+    for (uint32_t i = oldCount; i < count; i++) {
+        m_values[i] = NULL;
+        m_valueSizes[i] = m_defaultValueSize;
+    }
+}
+
+void MP4BytesProperty::SetValue(const uint8_t* pValue, uint32_t valueSize,
+                                uint32_t index)
+{
+    if (m_readOnly) {
+        ostringstream msg;
+        msg << "property " << m_name << "is read-only";
+        throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__ );
+    }
+    if (m_fixedValueSize) {
+        if (valueSize > m_fixedValueSize) {
+            ostringstream msg;
+            msg << GetParentAtom().GetType() << "." << GetName() << " value size " << valueSize << " exceeds fixed value size " << m_fixedValueSize;
+            throw new Exception(msg.str().c_str(), __FILE__, __LINE__, __FUNCTION__ );
+        }
+        if (m_values[index] == NULL) {
+            m_values[index] = (uint8_t*)MP4Calloc(m_fixedValueSize);
+            m_valueSizes[index] = m_fixedValueSize;
+        }
+        if (pValue) {
+            memcpy(m_values[index], pValue, valueSize);
+        }
+    } else {
+        MP4Free(m_values[index]);
+        if (pValue) {
+            m_values[index] = (uint8_t*)MP4Malloc(valueSize);
+            memcpy(m_values[index], pValue, valueSize);
+            m_valueSizes[index] = valueSize;
+        } else {
+            m_values[index] = NULL;
+            m_valueSizes[index] = 0;
+        }
+    }
+}
+
+void MP4BytesProperty::SetValueSize(uint32_t valueSize, uint32_t index)
+{
+    if (m_fixedValueSize) {
+        throw new Exception("can't change size of fixed sized property",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+    if (m_values[index] != NULL) {
+        m_values[index] = (uint8_t*)MP4Realloc(m_values[index], valueSize);
+    }
+    m_valueSizes[index] = valueSize;
+}
+
+void MP4BytesProperty::SetFixedSize(uint32_t fixedSize)
+{
+    m_fixedValueSize = 0;
+    for (uint32_t i = 0; i < GetCount(); i++) {
+        SetValueSize(fixedSize, i);
+    }
+    m_fixedValueSize = fixedSize;
+}
+
+void MP4BytesProperty::Read(MP4File& file, uint32_t index)
+{
+    if (m_implicit) {
+        return;
+    }
+    MP4Free(m_values[index]);
+    m_values[index] = (uint8_t*)MP4Malloc(m_valueSizes[index]);
+    file.ReadBytes(m_values[index], m_valueSizes[index]);
+}
+
+void MP4BytesProperty::Write(MP4File& file, uint32_t index)
+{
+    if (m_implicit) {
+        return;
+    }
+    file.WriteBytes(m_values[index], m_valueSizes[index]);
+}
+
+void MP4BytesProperty::Dump(uint8_t indent,
+                            bool dumpImplicits, uint32_t index)
+{
+    if( m_implicit && !dumpImplicits )
+        return;
+
+    const uint32_t size  = m_valueSizes[index];
+    const uint8_t* const value = m_values[index];
+
+    if( size == 0 ) {
+        log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, size );
+        return;
+    }
+
+    if( size <= 16 ) {
+        ostringstream oss;
+        ostringstream text;
+
+        oss << "  ";
+        for( uint32_t i = 0; i < size; i++ ) {
+            if( i )
+                oss << ' ';
+            oss << hex << setw(2) << setfill('0') << right << static_cast<uint32_t>(value[i]);
+            text << (isprint( static_cast<int>(value[i]) ) ? static_cast<char>(value[i]) : '.');
+        }
+
+        oss << "  |" << text.str() << "|";
+
+        log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>%s",
+                 m_parentAtom.GetFile().GetFilename().c_str(),
+                 m_name, size, oss.str().c_str() );
+        return;
+    }
+
+    // specialization for ilst item data always show all bytes except for covr
+    bool showall = false;
+    MP4Atom* const datac = m_parentAtom.GetParentAtom(); // data container
+    MP4Atom* const datacc = datac->GetParentAtom();
+    if( datacc &&
+        ATOMID( datacc->GetType() ) == ATOMID( "ilst" ) &&
+        ATOMID( datac->GetType() ) != ATOMID( "covr" ) )
+    {
+        showall = true;
+    }
+
+    uint32_t adjsize;
+    bool supressed;
+
+    if( showall ||
+        size < 128 || log.verbosity >= MP4_LOG_VERBOSE2 )
+    {
+        adjsize = size;
+        supressed = false;
+    }
+    else {
+        adjsize = 128;
+        supressed = true;
+    }
+
+    ostringstream oss;
+    ostringstream text;
+
+    log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = <%u bytes>",
+             m_parentAtom.GetFile().GetFilename().c_str(),
+             m_name, size );
+    log.hexDump(indent, MP4_LOG_VERBOSE2, value, adjsize, "\"%s\": %s",
+                m_parentAtom.GetFile().GetFilename().c_str(),
+                m_name);
+
+    if( supressed ) {
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": <remaining bytes supressed>",
+                 m_parentAtom.GetFile().GetFilename().c_str() );
+    }
+}
+
+// MP4TableProperty
+
+MP4TableProperty::MP4TableProperty(MP4Atom& parentAtom, const char* name, MP4IntegerProperty* pCountProperty)
+        : MP4Property(parentAtom, name)
+{
+    m_pCountProperty = pCountProperty;
+    m_pCountProperty->SetReadOnly();
+}
+
+MP4TableProperty::~MP4TableProperty()
+{
+    for (uint32_t i = 0; i < m_pProperties.Size(); i++) {
+        delete m_pProperties[i];
+    }
+}
+
+void MP4TableProperty::AddProperty(MP4Property* pProperty)
+{
+    ASSERT(pProperty);
+    ASSERT(pProperty->GetType() != TableProperty);
+    ASSERT(pProperty->GetType() != DescriptorProperty);
+    m_pProperties.Add(pProperty);
+    pProperty->SetCount(0);
+}
+
+bool MP4TableProperty::FindProperty(const char *name,
+                                    MP4Property** ppProperty, uint32_t* pIndex)
+{
+    ASSERT(m_name);
+
+    // check if first component of name matches ourselves
+    if (!MP4NameFirstMatches(m_name, name)) {
+        return false;
+    }
+
+    // check if the specified table entry exists
+    uint32_t index;
+    bool haveIndex = MP4NameFirstIndex(name, &index);
+    if (haveIndex) {
+        if (index >= GetCount()) {
+            return false;
+        }
+        if (pIndex) {
+            *pIndex = index;
+        }
+    }
+    
+    log.verbose1f("\"%s\": FindProperty: matched %s", 
+                  m_parentAtom.GetFile().GetFilename().c_str(), name);
+
+    // get name of table property
+    const char *tablePropName = MP4NameAfterFirst(name);
+    if (tablePropName == NULL) {
+        if (!haveIndex) {
+            *ppProperty = this;
+            return true;
+        }
+        return false;
+    }
+
+    // check if this table property exists
+    return FindContainedProperty(tablePropName, ppProperty, pIndex);
+}
+
+bool MP4TableProperty::FindContainedProperty(const char *name,
+        MP4Property** ppProperty, uint32_t* pIndex)
+{
+    uint32_t numProperties = m_pProperties.Size();
+
+    for (uint32_t i = 0; i < numProperties; i++) {
+        if (m_pProperties[i]->FindProperty(name, ppProperty, pIndex)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void MP4TableProperty::Read(MP4File& file, uint32_t index)
+{
+    ASSERT(index == 0);
+
+    if (m_implicit) {
+        return;
+    }
+
+    uint32_t numProperties = m_pProperties.Size();
+
+    if (numProperties == 0) {
+        WARNING(numProperties == 0);
+        return;
+    }
+
+    uint32_t numEntries = GetCount();
+
+    /* for each property set size */
+    for (uint32_t j = 0; j < numProperties; j++) {
+        m_pProperties[j]->SetCount(numEntries);
+    }
+
+    for (uint32_t i = 0; i < numEntries; i++) {
+        ReadEntry(file, i);
+    }
+}
+
+void MP4TableProperty::ReadEntry(MP4File& file, uint32_t index)
+{
+    for (uint32_t j = 0; j < m_pProperties.Size(); j++) {
+        m_pProperties[j]->Read(file, index);
+    }
+}
+
+void MP4TableProperty::Write(MP4File& file, uint32_t index)
+{
+    ASSERT(index == 0);
+
+    if (m_implicit) {
+        return;
+    }
+
+    uint32_t numProperties = m_pProperties.Size();
+
+    if (numProperties == 0) {
+        WARNING(numProperties == 0);
+        return;
+    }
+
+    uint32_t numEntries = GetCount();
+
+    if (m_pProperties[0]->GetCount() != numEntries) {
+        log.errorf("%s: \"%s\": %s %s \"%s\"table entries %u doesn't match count %u",
+                   __FUNCTION__, m_parentAtom.GetFile().GetFilename().c_str(),
+                   GetParentAtom().GetType(),
+                   GetName(), m_pProperties[0]->GetName(),
+                   m_pProperties[0]->GetCount(), numEntries);
+
+        ASSERT(m_pProperties[0]->GetCount() == numEntries);
+    }
+
+    for (uint32_t i = 0; i < numEntries; i++) {
+        WriteEntry(file, i);
+    }
+}
+
+void MP4TableProperty::WriteEntry(MP4File& file, uint32_t index)
+{
+    for (uint32_t j = 0; j < m_pProperties.Size(); j++) {
+        m_pProperties[j]->Write(file, index);
+    }
+}
+
+void MP4TableProperty::Dump(uint8_t indent,
+                            bool dumpImplicits, uint32_t index)
+{
+    ASSERT(index == 0);
+
+    // implicit tables just can't be dumped
+    if (m_implicit) {
+        return;
+    }
+
+    uint32_t numProperties = m_pProperties.Size();
+
+    if (numProperties == 0) {
+        WARNING(numProperties == 0);
+        return;
+    }
+
+    uint32_t numEntries = GetCount();
+
+    for (uint32_t i = 0; i < numEntries; i++) {
+        for (uint32_t j = 0; j < numProperties; j++) {
+            m_pProperties[j]->Dump(indent + 1, dumpImplicits, i);
+        }
+    }
+}
+
+// MP4DescriptorProperty
+
+MP4DescriptorProperty::MP4DescriptorProperty(MP4Atom& parentAtom, const char* name,
+        uint8_t tagsStart, uint8_t tagsEnd, bool mandatory, bool onlyOne)
+        : MP4Property(parentAtom, name)
+{
+    SetTags(tagsStart, tagsEnd);
+    m_sizeLimit = 0;
+    m_mandatory = mandatory;
+    m_onlyOne = onlyOne;
+}
+
+MP4DescriptorProperty::~MP4DescriptorProperty()
+{
+    for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
+        delete m_pDescriptors[i];
+    }
+}
+
+MP4Descriptor* MP4DescriptorProperty::AddDescriptor(uint8_t tag)
+{
+    // check that tag is in expected range
+    ASSERT(tag >= m_tagsStart && tag <= m_tagsEnd);
+
+    MP4Descriptor* pDescriptor = CreateDescriptor(m_parentAtom, tag);
+    ASSERT(pDescriptor);
+
+    m_pDescriptors.Add(pDescriptor);
+
+    return pDescriptor;
+}
+
+void MP4DescriptorProperty::DeleteDescriptor(uint32_t index)
+{
+    delete m_pDescriptors[index];
+    m_pDescriptors.Delete(index);
+}
+
+void MP4DescriptorProperty::Generate()
+{
+    // generate a default descriptor
+    // if it is mandatory, and single
+    if (m_mandatory && m_onlyOne) {
+        MP4Descriptor* pDescriptor =
+            AddDescriptor(m_tagsStart);
+        pDescriptor->Generate();
+    }
+}
+
+bool MP4DescriptorProperty::FindProperty(const char *name,
+        MP4Property** ppProperty, uint32_t* pIndex)
+{
+    // we're unnamed, so just check contained properties
+    if (m_name == NULL || !strcmp(m_name, "")) {
+        return FindContainedProperty(name, ppProperty, pIndex);
+    }
+
+    // check if first component of name matches ourselves
+    if (!MP4NameFirstMatches(m_name, name)) {
+        return false;
+    }
+
+    // check if the specific descriptor entry exists
+    uint32_t descrIndex;
+    bool haveDescrIndex = MP4NameFirstIndex(name, &descrIndex);
+
+    if (haveDescrIndex && descrIndex >= GetCount()) {
+        return false;
+    }
+
+    log.verbose1f("\"%s\": matched %s",
+                  m_parentAtom.GetFile().GetFilename().c_str(),
+                  name);
+
+    // get name of descriptor property
+    name = MP4NameAfterFirst(name);
+    if (name == NULL) {
+        if (!haveDescrIndex) {
+            *ppProperty = this;
+            return true;
+        }
+        return false;
+    }
+
+    /* check rest of name */
+    if (haveDescrIndex) {
+        return m_pDescriptors[descrIndex]->FindProperty(name,
+                ppProperty, pIndex);
+    } else {
+        return FindContainedProperty(name, ppProperty, pIndex);
+    }
+}
+
+bool MP4DescriptorProperty::FindContainedProperty(const char *name,
+        MP4Property** ppProperty, uint32_t* pIndex)
+{
+    for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
+        if (m_pDescriptors[i]->FindProperty(name, ppProperty, pIndex)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void MP4DescriptorProperty::Read(MP4File& file, uint32_t index)
+{
+    ASSERT(index == 0);
+
+    if (m_implicit) {
+        return;
+    }
+
+    uint64_t start = file.GetPosition();
+
+    while (true) {
+        // enforce size limitation
+        if (m_sizeLimit && file.GetPosition() >= start + m_sizeLimit) {
+            break;
+        }
+
+        uint8_t tag;
+        try {
+            file.PeekBytes(&tag, 1);
+        }
+        catch (Exception* x) {
+            if (file.GetPosition() >= file.GetSize()) {
+                // EOF
+                delete x;
+                break;
+            }
+            throw x;
+        }
+
+        // check if tag is in desired range
+        if (tag < m_tagsStart || tag > m_tagsEnd) {
+            break;
+        }
+
+        MP4Descriptor* pDescriptor =
+            AddDescriptor(tag);
+
+        pDescriptor->Read(file);
+    }
+
+    // warnings
+    if (m_mandatory && m_pDescriptors.Size() == 0) {
+        log.warningf("%s: \"%s\": Mandatory descriptor 0x%02x missing",
+                     __FUNCTION__, GetParentAtom().GetFile().GetFilename().c_str(), m_tagsStart);
+    } else if (m_onlyOne && m_pDescriptors.Size() > 1) {
+        log.warningf("%s: \"%s\": Descriptor 0x%02x has more than one instance",
+                     __FUNCTION__, GetParentAtom().GetFile().GetFilename().c_str(), m_tagsStart);
+    }
+}
+
+void MP4DescriptorProperty::Write(MP4File& file, uint32_t index)
+{
+    ASSERT(index == 0);
+
+    if (m_implicit) {
+        return;
+    }
+
+    for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
+        m_pDescriptors[i]->Write(file);
+    }
+}
+
+void MP4DescriptorProperty::Dump(uint8_t indent,
+                                 bool dumpImplicits, uint32_t index)
+{
+    ASSERT(index == 0);
+
+    if (m_implicit && !dumpImplicits) {
+        return;
+    }
+
+    if (m_name) {
+        if (index != 0)
+            log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s[%u]",
+                     m_parentAtom.GetFile().GetFilename().c_str(),
+                     m_name, index);
+        else
+            log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s",
+                     m_parentAtom.GetFile().GetFilename().c_str(),
+                     m_name);
+        indent++;
+    }
+
+    for (uint32_t i = 0; i < m_pDescriptors.Size(); i++) {
+        m_pDescriptors[i]->Dump(indent, dumpImplicits);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4LanguageCodeProperty::MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* name, bmff::LanguageCode value )
+    : MP4Property( parentAtom, name )
+{
+    SetValue( value );
+}
+
+MP4LanguageCodeProperty::MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* name, const string& code )
+    : MP4Property( parentAtom, name )
+{
+    SetValue( bmff::enumLanguageCode.toType( code ));
+}
+
+void
+MP4LanguageCodeProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
+{
+    uint16_t data = 0;
+
+    string svalue;
+    bmff::enumLanguageCode.toString( _value, svalue );
+    if( svalue.length() == 3 ) {
+        data = (((svalue[0] - 0x60) & 0x001f) << 10)
+             | (((svalue[1] - 0x60) & 0x001f) <<  5)
+             | (((svalue[2] - 0x60) & 0x001f)      );
+    }
+
+    log.dump(indent, MP4_LOG_VERBOSE2, "\"%s\": %s = %s (0x%04x)",
+             m_parentAtom.GetFile().GetFilename().c_str(),
+             m_name, bmff::enumLanguageCode.toString( _value, true ).c_str(), data );
+}
+
+uint32_t
+MP4LanguageCodeProperty::GetCount()
+{
+    return 1;
+}
+
+MP4PropertyType
+MP4LanguageCodeProperty::GetType()
+{
+    return LanguageCodeProperty;
+}
+
+bmff::LanguageCode
+MP4LanguageCodeProperty::GetValue()
+{
+    return _value;
+}
+
+void
+MP4LanguageCodeProperty::Read( MP4File& file, uint32_t index )
+{
+    uint16_t data = file.ReadBits( 16 );
+
+    char code[3];
+    code[0] = ((data & 0x7c00) >> 10) + 0x60;
+    code[1] = ((data & 0x03e0) >>  5) + 0x60;
+    code[2] = ((data & 0x001f)      ) + 0x60;
+
+    SetValue( bmff::enumLanguageCode.toType( string( code, sizeof(code) )));
+}
+
+void
+MP4LanguageCodeProperty::SetCount( uint32_t count )
+{
+    // do nothing; count is always 1
+}
+
+void
+MP4LanguageCodeProperty::SetValue( bmff::LanguageCode value )
+{
+    _value = value;
+}
+
+void
+MP4LanguageCodeProperty::Write( MP4File& file, uint32_t index )
+{
+    uint16_t data = 0;
+
+    string svalue;
+    bmff::enumLanguageCode.toString( _value, svalue );
+    if( svalue.length() == 3 ) {
+        data = (((svalue[0] - 0x60) & 0x001f) << 10)
+             | (((svalue[1] - 0x60) & 0x001f) <<  5)
+             | (((svalue[2] - 0x60) & 0x001f)      );
+    }
+
+    file.WriteBits( data, 16 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4BasicTypeProperty::MP4BasicTypeProperty( MP4Atom& parentAtom, const char* name, itmf::BasicType type )
+    : MP4Property( parentAtom, name )
+{
+    SetValue( type );
+}
+
+void
+MP4BasicTypeProperty::Dump( uint8_t indent, bool dumpImplicits, uint32_t index )
+{
+    log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": %s = %s (0x%02x)",
+             m_parentAtom.GetFile().GetFilename().c_str(), m_name,
+             itmf::enumBasicType.toString( _value, true ).c_str(), _value );
+}
+
+uint32_t
+MP4BasicTypeProperty::GetCount()
+{
+    return 1;
+}
+
+MP4PropertyType
+MP4BasicTypeProperty::GetType()
+{
+    return BasicTypeProperty;
+}
+
+itmf::BasicType
+MP4BasicTypeProperty::GetValue()
+{
+    return _value;
+}
+
+void
+MP4BasicTypeProperty::Read( MP4File& file, uint32_t index )
+{
+    SetValue( static_cast<itmf::BasicType>( file.ReadBits( 8 )));
+}
+
+void
+MP4BasicTypeProperty::SetCount( uint32_t count )
+{
+    // do nothing; count is always 1
+}
+
+void
+MP4BasicTypeProperty::SetValue( itmf::BasicType value )
+{
+    _value = value;
+}
+
+void
+MP4BasicTypeProperty::Write( MP4File& file, uint32_t index )
+{
+    file.WriteBits( _value, 8 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/mp4property.h b/src/mp4property.h
new file mode 100644
index 0000000..4c7ccad
--- /dev/null
+++ b/src/mp4property.h
@@ -0,0 +1,663 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_MP4PROPERTY_H
+#define MP4V2_IMPL_MP4PROPERTY_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// forward declarations
+class MP4Atom;
+
+class MP4Descriptor;
+MP4ARRAY_DECL(MP4Descriptor, MP4Descriptor*);
+
+enum MP4PropertyType {
+    Integer8Property,
+    Integer16Property,
+    Integer24Property,
+    Integer32Property,
+    Integer64Property,
+    Float32Property,
+    StringProperty,
+    BytesProperty,
+    TableProperty,
+    DescriptorProperty,
+    LanguageCodeProperty,
+    BasicTypeProperty,
+};
+
+class MP4Property {
+public:
+    MP4Property(MP4Atom& parentAtom, const char *name = NULL);
+
+    virtual ~MP4Property() { }
+
+    MP4Atom& GetParentAtom() {
+        return m_parentAtom;
+    }
+
+    const char *GetName() {
+        return m_name;
+    }
+
+    virtual MP4PropertyType GetType() = 0;
+
+    bool IsReadOnly() {
+        return m_readOnly;
+    }
+    void SetReadOnly(bool value = true) {
+        m_readOnly = value;
+    }
+
+    bool IsImplicit() {
+        return m_implicit;
+    }
+    void SetImplicit(bool value = true) {
+        m_implicit = value;
+    }
+
+    virtual uint32_t GetCount() = 0;
+    virtual void SetCount(uint32_t count) = 0;
+
+    virtual void Generate() { /* default is a no-op */ };
+
+    virtual void Read(MP4File& file, uint32_t index = 0) = 0;
+
+    virtual void Write(MP4File& file, uint32_t index = 0) = 0;
+
+    virtual void Dump(uint8_t indent,
+                      bool dumpImplicits, uint32_t index = 0) = 0;
+
+    virtual bool FindProperty(const char* name,
+                              MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+protected:
+    MP4Atom& m_parentAtom;
+    const char* m_name;
+    bool m_readOnly;
+    bool m_implicit;
+
+private:
+    MP4Property();
+    MP4Property ( const MP4Property &src );
+    MP4Property &operator= ( const MP4Property &src );
+};
+
+MP4ARRAY_DECL(MP4Property, MP4Property*);
+
+class MP4IntegerProperty : public MP4Property {
+protected:
+    MP4IntegerProperty(MP4Atom& parentAtom, const char* name)
+            : MP4Property(parentAtom, name) { };
+
+public:
+    uint64_t GetValue(uint32_t index = 0);
+
+    void SetValue(uint64_t value, uint32_t index = 0);
+
+    void InsertValue(uint64_t value, uint32_t index = 0);
+
+    void DeleteValue(uint32_t index = 0);
+
+    void IncrementValue(int32_t increment = 1, uint32_t index = 0);
+
+private:
+    MP4IntegerProperty();
+    MP4IntegerProperty ( const MP4IntegerProperty &src );
+    MP4IntegerProperty &operator= ( const MP4IntegerProperty &src );
+};
+
+#define MP4INTEGER_PROPERTY_DECL2(isize, xsize) \
+    class MP4Integer##xsize##Property : public MP4IntegerProperty { \
+    public: \
+        MP4Integer##xsize##Property(MP4Atom& parentAtom, const char* name) \
+            : MP4IntegerProperty(parentAtom, name) { \
+            SetCount(1); \
+            m_values[0] = 0; \
+        } \
+        \
+        MP4PropertyType GetType() { \
+            return Integer##xsize##Property; \
+        } \
+        \
+        uint32_t GetCount() { \
+            return m_values.Size(); \
+        } \
+        void SetCount(uint32_t count) { \
+            m_values.Resize(count); \
+        } \
+        \
+        uint##isize##_t GetValue(uint32_t index = 0) { \
+            return m_values[index]; \
+        } \
+        \
+        void SetValue(uint##isize##_t value, uint32_t index = 0) { \
+            if (m_readOnly) { \
+                ostringstream msg; \
+                msg << "property is read-only: " << m_name; \
+                throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__); \
+            } \
+            m_values[index] = value; \
+        } \
+        void AddValue(uint##isize##_t value) { \
+            m_values.Add(value); \
+        } \
+        void InsertValue(uint##isize##_t value, uint32_t index) { \
+            m_values.Insert(value, index); \
+        } \
+        void DeleteValue(uint32_t index) { \
+            m_values.Delete(index); \
+        } \
+        void IncrementValue(int32_t increment = 1, uint32_t index = 0) { \
+            m_values[index] += increment; \
+        } \
+        void Read(MP4File& file, uint32_t index = 0) { \
+            if (m_implicit) { \
+                return; \
+            } \
+            m_values[index] = file.ReadUInt##xsize(); \
+        } \
+        \
+        void Write(MP4File& file, uint32_t index = 0) { \
+            if (m_implicit) { \
+                return; \
+            } \
+            file.WriteUInt##xsize(m_values[index]); \
+        } \
+        void Dump(uint8_t indent, \
+            bool dumpImplicits, uint32_t index = 0); \
+    \
+    protected: \
+        MP4Integer##isize##Array m_values; \
+    private: \
+        MP4Integer##xsize##Property(); \
+        MP4Integer##xsize##Property ( const MP4Integer##xsize##Property &src ); \
+        MP4Integer##xsize##Property &operator= ( const MP4Integer##xsize##Property &src ); \
+    };
+
+#define MP4INTEGER_PROPERTY_DECL(size) \
+    MP4INTEGER_PROPERTY_DECL2(size, size)
+
+MP4INTEGER_PROPERTY_DECL(8);
+MP4INTEGER_PROPERTY_DECL(16);
+MP4INTEGER_PROPERTY_DECL2(32, 24);
+MP4INTEGER_PROPERTY_DECL(32);
+MP4INTEGER_PROPERTY_DECL(64);
+
+class MP4BitfieldProperty : public MP4Integer64Property {
+public:
+    MP4BitfieldProperty(MP4Atom& parentAtom, const char* name, uint8_t numBits)
+            : MP4Integer64Property(parentAtom, name) {
+        ASSERT(numBits != 0);
+        ASSERT(numBits <= 64);
+        m_numBits = numBits;
+    }
+
+    uint8_t GetNumBits() {
+        return m_numBits;
+    }
+    void SetNumBits(uint8_t numBits) {
+        m_numBits = numBits;
+    }
+
+    void Read(MP4File& file, uint32_t index = 0);
+    void Write(MP4File& file, uint32_t index = 0);
+    void Dump(uint8_t indent,
+              bool dumpImplicits, uint32_t index = 0);
+
+protected:
+    uint8_t m_numBits;
+
+private:
+    MP4BitfieldProperty();
+    MP4BitfieldProperty ( const MP4BitfieldProperty &src );
+    MP4BitfieldProperty &operator= ( const MP4BitfieldProperty &src );
+};
+
+class MP4Float32Property : public MP4Property {
+public:
+    MP4Float32Property(MP4Atom& parentAtom, const char* name)
+            : MP4Property(parentAtom, name) {
+        m_useFixed16Format = false;
+        m_useFixed32Format = false;
+        SetCount(1);
+        m_values[0] = 0.0;
+    }
+
+    MP4PropertyType GetType() {
+        return Float32Property;
+    }
+
+    uint32_t GetCount() {
+        return m_values.Size();
+    }
+    void SetCount(uint32_t count) {
+        m_values.Resize(count);
+    }
+
+    float GetValue(uint32_t index = 0) {
+        return m_values[index];
+    }
+
+    void SetValue(float value, uint32_t index = 0) {
+        if (m_readOnly) {
+            ostringstream msg;
+            msg << "property is read-only: " << m_name;
+            throw new PlatformException(msg.str().c_str(), EACCES, __FILE__, __LINE__, __FUNCTION__);
+        }
+        m_values[index] = value;
+    }
+
+    void AddValue(float value) {
+        m_values.Add(value);
+    }
+
+    void InsertValue(float value, uint32_t index) {
+        m_values.Insert(value, index);
+    }
+
+    bool IsFixed16Format() {
+        return m_useFixed16Format;
+    }
+
+    void SetFixed16Format(bool useFixed16Format = true) {
+        m_useFixed16Format = useFixed16Format;
+    }
+
+    bool IsFixed32Format() {
+        return m_useFixed32Format;
+    }
+
+    void SetFixed32Format(bool useFixed32Format = true) {
+        m_useFixed32Format = useFixed32Format;
+    }
+
+    void Read(MP4File& file, uint32_t index = 0);
+    void Write(MP4File& file, uint32_t index = 0);
+    void Dump(uint8_t indent,
+              bool dumpImplicits, uint32_t index = 0);
+
+protected:
+    bool m_useFixed16Format;
+    bool m_useFixed32Format;
+    MP4Float32Array m_values;
+
+private:
+    MP4Float32Property();
+    MP4Float32Property ( const MP4Float32Property &src );
+    MP4Float32Property &operator= ( const MP4Float32Property &src );
+};
+
+class MP4StringProperty : public MP4Property {
+public:
+    MP4StringProperty(MP4Atom& parentAtom, const char* name,
+                      bool useCountedFormat = false, bool useUnicode = false, bool arrayMode = false);
+
+    ~MP4StringProperty();
+
+    MP4PropertyType GetType() {
+        return StringProperty;
+    }
+
+    uint32_t GetCount() {
+        return m_values.Size();
+    }
+
+    void SetCount(uint32_t count);
+
+    const char* GetValue(uint32_t index = 0) {
+        return m_values[index];
+    }
+
+    void SetValue(const char* value, uint32_t index = 0);
+
+    void AddValue(const char* value) {
+        uint32_t count = GetCount();
+        SetCount(count + 1);
+        SetValue(value, count);
+    }
+
+    bool IsCountedFormat() {
+        return m_useCountedFormat;
+    }
+
+    void SetCountedFormat(bool useCountedFormat) {
+        m_useCountedFormat = useCountedFormat;
+    }
+
+    bool IsExpandedCountedFormat() {
+        return m_useExpandedCount;
+    }
+
+    void SetExpandedCountedFormat(bool useExpandedCount) {
+        m_useExpandedCount = useExpandedCount;
+    }
+
+    bool IsUnicode() {
+        return m_useUnicode;
+    }
+
+    void SetUnicode(bool useUnicode) {
+        m_useUnicode = useUnicode;
+    }
+
+    uint32_t GetFixedLength() {
+        return m_fixedLength;
+    }
+
+    void SetFixedLength(uint32_t fixedLength) {
+        m_fixedLength = fixedLength;
+    }
+
+    void Read(MP4File& file, uint32_t index = 0);
+    void Write(MP4File& file, uint32_t index = 0);
+    void Dump(uint8_t indent,
+              bool dumpImplicits, uint32_t index = 0);
+
+protected:
+    bool m_arrayMode; // during read/write ignore index and read/write full array
+    bool m_useCountedFormat;
+    bool m_useExpandedCount;
+    bool m_useUnicode;
+    uint32_t m_fixedLength;
+
+    MP4StringArray m_values;
+
+private:
+    MP4StringProperty();
+    MP4StringProperty ( const MP4StringProperty &src );
+    MP4StringProperty &operator= ( const MP4StringProperty &src );
+};
+
+class MP4BytesProperty : public MP4Property {
+public:
+    MP4BytesProperty(MP4Atom& parentAtom, const char* name, uint32_t valueSize = 0,
+                     uint32_t defaultValueSize = 0);
+
+    ~MP4BytesProperty();
+
+    MP4PropertyType GetType() {
+        return BytesProperty;
+    }
+
+    uint32_t GetCount() {
+        return m_values.Size();
+    }
+
+    void SetCount(uint32_t count);
+
+    void GetValue(uint8_t** ppValue, uint32_t* pValueSize,
+                  uint32_t index = 0) {
+        // N.B. caller must free memory
+        *ppValue = (uint8_t*)MP4Malloc(m_valueSizes[index]);
+        memcpy(*ppValue, m_values[index], m_valueSizes[index]);
+        *pValueSize = m_valueSizes[index];
+    }
+
+    char* GetValueStringAlloc( uint32_t index = 0 ) {
+        char* buf = (char*)MP4Malloc( m_valueSizes[index] + 1 );
+        memcpy( buf, m_values[index], m_valueSizes[index] );
+        buf[m_valueSizes[index]] = '\0';
+        return buf;
+    }
+
+    bool CompareToString( const string& s, uint32_t index = 0 ) {
+        return string( (const char*)m_values[index], m_valueSizes[index] ) != s;
+    }
+
+    void CopyValue(uint8_t* pValue, uint32_t index = 0) {
+        // N.B. caller takes responsbility for valid pointer
+        // and sufficient memory at the destination
+        memcpy(pValue, m_values[index], m_valueSizes[index]);
+    }
+
+    void SetValue(const uint8_t* pValue, uint32_t valueSize,
+                  uint32_t index = 0);
+
+    void AddValue(const uint8_t* pValue, uint32_t valueSize) {
+        uint32_t count = GetCount();
+        SetCount(count + 1);
+        SetValue(pValue, valueSize, count);
+    }
+
+    uint32_t GetValueSize( uint32_t index = 0 ) {
+        return m_valueSizes[index];
+    }
+
+    void SetValueSize(uint32_t valueSize, uint32_t index = 0);
+
+    uint32_t GetFixedSize() {
+        return m_fixedValueSize;
+    }
+
+    void SetFixedSize(uint32_t fixedSize);
+
+    void Read(MP4File& file, uint32_t index = 0);
+    void Write(MP4File& file, uint32_t index = 0);
+    void Dump(uint8_t indent,
+              bool dumpImplicits, uint32_t index = 0);
+
+protected:
+    uint32_t        m_fixedValueSize;
+    uint32_t        m_defaultValueSize;
+    MP4Integer32Array   m_valueSizes;
+    MP4BytesArray       m_values;
+
+private:
+    MP4BytesProperty();
+    MP4BytesProperty ( const MP4BytesProperty &src );
+    MP4BytesProperty &operator= ( const MP4BytesProperty &src );
+};
+
+class MP4TableProperty : public MP4Property {
+public:
+    MP4TableProperty(MP4Atom& parentAtom, const char* name, MP4IntegerProperty* pCountProperty);
+
+    ~MP4TableProperty();
+
+    MP4PropertyType GetType() {
+        return TableProperty;
+    }
+
+    void AddProperty(MP4Property* pProperty);
+
+    MP4Property* GetProperty(uint32_t index) {
+        return m_pProperties[index];
+    }
+
+    virtual uint32_t GetCount() {
+        return m_pCountProperty->GetValue();
+    }
+    virtual void SetCount(uint32_t count) {
+        m_pCountProperty->SetValue(count);
+    }
+
+    void Read(MP4File& file, uint32_t index = 0);
+    void Write(MP4File& file, uint32_t index = 0);
+    void Dump(uint8_t indent,
+              bool dumpImplicits, uint32_t index = 0);
+
+    bool FindProperty(const char* name,
+                      MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+protected:
+    virtual void ReadEntry(MP4File& file, uint32_t index);
+    virtual void WriteEntry(MP4File& file, uint32_t index);
+
+    bool FindContainedProperty(const char* name,
+                               MP4Property** ppProperty, uint32_t* pIndex);
+
+protected:
+    MP4IntegerProperty* m_pCountProperty;
+    MP4PropertyArray    m_pProperties;
+
+private:
+    MP4TableProperty();
+    MP4TableProperty ( const MP4TableProperty &src );
+    MP4TableProperty &operator= ( const MP4TableProperty &src );
+};
+
+class MP4DescriptorProperty : public MP4Property {
+public:
+    MP4DescriptorProperty(MP4Atom& parentAtom, const char* name = NULL,
+                          uint8_t tagsStart = 0, uint8_t tagsEnd = 0,
+                          bool mandatory = false, bool onlyOne = false);
+
+    ~MP4DescriptorProperty();
+
+    MP4PropertyType GetType() {
+        return DescriptorProperty;
+    }
+
+    void SetParentAtom(MP4Atom* pParentAtom);
+
+    void SetSizeLimit(uint64_t sizeLimit) {
+        m_sizeLimit = sizeLimit;
+    }
+
+    uint32_t GetCount() {
+        return m_pDescriptors.Size();
+    }
+    void SetCount(uint32_t count) {
+        m_pDescriptors.Resize(count);
+    }
+
+    void SetTags(uint8_t tagsStart, uint8_t tagsEnd = 0) {
+        m_tagsStart = tagsStart;
+        m_tagsEnd = tagsEnd ? tagsEnd : tagsStart;
+    }
+
+    MP4Descriptor* AddDescriptor(uint8_t tag);
+
+    void AppendDescriptor(MP4Descriptor* pDescriptor) {
+        m_pDescriptors.Add(pDescriptor);
+    }
+
+    void DeleteDescriptor(uint32_t index);
+
+    void Generate();
+    void Read(MP4File& file, uint32_t index = 0);
+    void Write(MP4File& file, uint32_t index = 0);
+    void Dump(uint8_t indent,
+              bool dumpImplicits, uint32_t index = 0);
+
+    bool FindProperty(const char* name,
+                      MP4Property** ppProperty, uint32_t* pIndex = NULL);
+
+protected:
+    virtual MP4Descriptor* CreateDescriptor(MP4Atom& parentAtom, uint8_t tag);
+
+    bool FindContainedProperty(const char* name,
+                               MP4Property** ppProperty, uint32_t* pIndex);
+
+protected:
+    uint8_t         m_tagsStart;
+    uint8_t         m_tagsEnd;
+    uint64_t            m_sizeLimit;
+    bool                m_mandatory;
+    bool                m_onlyOne;
+    MP4DescriptorArray  m_pDescriptors;
+
+private:
+    MP4DescriptorProperty();
+    MP4DescriptorProperty ( const MP4DescriptorProperty &src );
+    MP4DescriptorProperty &operator= ( const MP4DescriptorProperty &src );
+};
+
+class MP4QosQualifierProperty : public MP4DescriptorProperty {
+public:
+    MP4QosQualifierProperty(MP4Atom& parentAtom, const char* name = NULL,
+                            uint8_t tagsStart = 0, uint8_t tagsEnd = 0,
+                            bool mandatory = false, bool onlyOne = false) :
+            MP4DescriptorProperty(parentAtom, name, tagsStart, tagsEnd, mandatory, onlyOne) { }
+
+protected:
+    MP4Descriptor* CreateDescriptor(MP4Atom& parentAtom, uint8_t tag);
+
+private:
+    MP4QosQualifierProperty();
+    MP4QosQualifierProperty ( const MP4QosQualifierProperty &src );
+    MP4QosQualifierProperty &operator= ( const MP4QosQualifierProperty &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// ISO-639-2/T language code.
+/// Language codes are 3-alpha (always lowercase) codes which are then
+/// offset using 0x60 and packed as 5-bit values into 16-bits, most
+/// significant bit is zero-padding.
+
+class MP4LanguageCodeProperty : public MP4Property {
+private:
+    bmff::LanguageCode _value;
+
+public:
+    explicit MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* , bmff::LanguageCode = bmff::ILC_UND );
+    MP4LanguageCodeProperty( MP4Atom& parentAtom, const char* , const string& );
+
+    MP4PropertyType GetType();
+    uint32_t        GetCount();
+    void            SetCount( uint32_t );
+    void            Read( MP4File&, uint32_t = 0 );
+    void            Write( MP4File&, uint32_t = 0 );
+    void            Dump( uint8_t, bool, uint32_t = 0 );
+
+    bmff::LanguageCode GetValue();
+    void               SetValue( bmff::LanguageCode );
+
+private:
+    MP4LanguageCodeProperty();
+    MP4LanguageCodeProperty ( const MP4LanguageCodeProperty &src );
+    MP4LanguageCodeProperty &operator= ( const MP4LanguageCodeProperty &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class MP4BasicTypeProperty : public MP4Property {
+private:
+    itmf::BasicType _value;
+
+public:
+    explicit MP4BasicTypeProperty( MP4Atom& parentAtom, const char* , itmf::BasicType = itmf::BT_UNDEFINED );
+
+    MP4PropertyType GetType();
+    uint32_t        GetCount();
+    void            SetCount( uint32_t );
+    void            Read( MP4File&, uint32_t = 0 );
+    void            Write( MP4File&, uint32_t = 0 );
+    void            Dump( uint8_t, bool, uint32_t = 0 );
+    itmf::BasicType GetValue();
+    void            SetValue( itmf::BasicType );
+
+private:
+    MP4BasicTypeProperty();
+    MP4BasicTypeProperty ( const MP4BasicTypeProperty &src );
+    MP4BasicTypeProperty &operator= ( const MP4BasicTypeProperty &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4PROPERTY_H
diff --git a/src/mp4track.cpp b/src/mp4track.cpp
new file mode 100644
index 0000000..cfa1a1f
--- /dev/null
+++ b/src/mp4track.cpp
@@ -0,0 +1,1910 @@
+/*
+ * 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.
+ *
+ * Contributor(s):
+ *      Dave Mackie         dmackie@cisco.com
+ *      Alix Marchandise-Franquet   alix@cisco.com
+ *              Ximpo Group Ltd.                mp4v2@ximpo.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define AMR_UNINITIALIZED -1
+#define AMR_TRUE 0
+#define AMR_FALSE 1
+
+MP4Track::MP4Track(MP4File& file, MP4Atom& trakAtom)
+    : m_File(file)
+    , m_trakAtom(trakAtom)
+{
+    m_lastStsdIndex = 0;
+    m_lastSampleFile = NULL;
+
+    m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
+    m_pCachedReadSample = NULL;
+    m_cachedReadSampleSize = 0;
+
+    m_writeSampleId = 1;
+    m_fixedSampleDuration = 0;
+    m_pChunkBuffer = NULL;
+    m_chunkBufferSize = 0;
+    m_sizeOfDataInChunkBuffer = 0;
+    m_chunkSamples = 0;
+    m_chunkDuration = 0;
+
+    // m_bytesPerSample should be set to 1, except for the
+    // quicktime audio constant bit rate samples, which have non-1 values
+    m_bytesPerSample = 1;
+    m_samplesPerChunk = 0;
+    m_durationPerChunk = 0;
+    m_isAmr = AMR_UNINITIALIZED;
+    m_curMode = 0;
+
+    m_cachedSttsSid = MP4_INVALID_SAMPLE_ID;
+    m_cachedCttsSid = MP4_INVALID_SAMPLE_ID;
+
+    bool success = true;
+
+    MP4Integer32Property* pTrackIdProperty;
+    success &= m_trakAtom.FindProperty(
+                   "trak.tkhd.trackId",
+                   (MP4Property**)&pTrackIdProperty);
+    if (success) {
+        m_trackId = pTrackIdProperty->GetValue();
+    }
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.mdhd.timeScale",
+                   (MP4Property**)&m_pTimeScaleProperty);
+    if (success) {
+        // default chunking is 1 second of samples
+        m_durationPerChunk = m_pTimeScaleProperty->GetValue();
+    }
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.tkhd.duration",
+                   (MP4Property**)&m_pTrackDurationProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.mdhd.duration",
+                   (MP4Property**)&m_pMediaDurationProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.tkhd.modificationTime",
+                   (MP4Property**)&m_pTrackModificationProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.mdhd.modificationTime",
+                   (MP4Property**)&m_pMediaModificationProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.hdlr.handlerType",
+                   (MP4Property**)&m_pTypeProperty);
+
+    // get handles on sample size information
+
+
+    m_pStszFixedSampleSizeProperty = NULL;
+    bool have_stsz =
+        m_trakAtom.FindProperty("trak.mdia.minf.stbl.stsz.sampleSize",
+                                  (MP4Property**)&m_pStszFixedSampleSizeProperty);
+
+    if (have_stsz) {
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.stsz.sampleCount",
+                       (MP4Property**)&m_pStszSampleCountProperty);
+
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.stsz.entries.entrySize",
+                       (MP4Property**)&m_pStszSampleSizeProperty);
+        m_stsz_sample_bits = 32;
+    } else {
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.stz2.sampleCount",
+                       (MP4Property**)&m_pStszSampleCountProperty);
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.stz2.entries.entrySize",
+                       (MP4Property**)&m_pStszSampleSizeProperty);
+        MP4Integer8Property *stz2_field_size;
+        if (m_trakAtom.FindProperty(
+                    "trak.mdia.minf.stbl.stz2.fieldSize",
+                    (MP4Property **)&stz2_field_size)) {
+            m_stsz_sample_bits = stz2_field_size->GetValue();
+            m_have_stz2_4bit_sample = false;
+        } else success = false;
+    }
+
+    // get handles on information needed to map sample id's to file offsets
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stsc.entryCount",
+                   (MP4Property**)&m_pStscCountProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stsc.entries.firstChunk",
+                   (MP4Property**)&m_pStscFirstChunkProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stsc.entries.samplesPerChunk",
+                   (MP4Property**)&m_pStscSamplesPerChunkProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stsc.entries.sampleDescriptionIndex",
+                   (MP4Property**)&m_pStscSampleDescrIndexProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stsc.entries.firstSample",
+                   (MP4Property**)&m_pStscFirstSampleProperty);
+
+    bool haveStco = m_trakAtom.FindProperty(
+                        "trak.mdia.minf.stbl.stco.entryCount",
+                        (MP4Property**)&m_pChunkCountProperty);
+
+    if (haveStco) {
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.stco.entries.chunkOffset",
+                       (MP4Property**)&m_pChunkOffsetProperty);
+    } else {
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.co64.entryCount",
+                       (MP4Property**)&m_pChunkCountProperty);
+
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.co64.entries.chunkOffset",
+                       (MP4Property**)&m_pChunkOffsetProperty);
+    }
+
+    // get handles on sample timing info
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stts.entryCount",
+                   (MP4Property**)&m_pSttsCountProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stts.entries.sampleCount",
+                   (MP4Property**)&m_pSttsSampleCountProperty);
+
+    success &= m_trakAtom.FindProperty(
+                   "trak.mdia.minf.stbl.stts.entries.sampleDelta",
+                   (MP4Property**)&m_pSttsSampleDeltaProperty);
+
+    // get handles on rendering offset info if it exists
+
+    m_pCttsCountProperty = NULL;
+    m_pCttsSampleCountProperty = NULL;
+    m_pCttsSampleOffsetProperty = NULL;
+
+    bool haveCtts = m_trakAtom.FindProperty(
+                        "trak.mdia.minf.stbl.ctts.entryCount",
+                        (MP4Property**)&m_pCttsCountProperty);
+
+    if (haveCtts) {
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.ctts.entries.sampleCount",
+                       (MP4Property**)&m_pCttsSampleCountProperty);
+
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.ctts.entries.sampleOffset",
+                       (MP4Property**)&m_pCttsSampleOffsetProperty);
+    }
+
+    // get handles on sync sample info if it exists
+
+    m_pStssCountProperty = NULL;
+    m_pStssSampleProperty = NULL;
+
+    bool haveStss = m_trakAtom.FindProperty(
+                        "trak.mdia.minf.stbl.stss.entryCount",
+                        (MP4Property**)&m_pStssCountProperty);
+
+    if (haveStss) {
+        success &= m_trakAtom.FindProperty(
+                       "trak.mdia.minf.stbl.stss.entries.sampleNumber",
+                       (MP4Property**)&m_pStssSampleProperty);
+    }
+
+    // edit list
+    (void)InitEditListProperties();
+
+    // was everything found?
+    if (!success) {
+        throw new Exception("invalid track", __FILE__, __LINE__, __FUNCTION__ );
+    }
+    CalculateBytesPerSample();
+
+    // update sdtp log from sdtp atom
+    MP4SdtpAtom* sdtp = (MP4SdtpAtom*)m_trakAtom.FindAtom( "trak.mdia.minf.stbl.sdtp" );
+    if( sdtp ) {
+        uint8_t* buffer;
+        uint32_t bufsize;
+        sdtp->data.GetValue( &buffer, &bufsize );
+        m_sdtpLog.assign( (char*)buffer, bufsize );
+        free( buffer );
+    }
+}
+
+MP4Track::~MP4Track()
+{
+    MP4Free(m_pCachedReadSample);
+    m_pCachedReadSample = NULL;
+    MP4Free(m_pChunkBuffer);
+    m_pChunkBuffer = NULL;
+}
+
+const char* MP4Track::GetType()
+{
+    return m_pTypeProperty->GetValue();
+}
+
+void MP4Track::SetType(const char* type)
+{
+    m_pTypeProperty->SetValue(MP4NormalizeTrackType(type));
+}
+
+void MP4Track::ReadSample(
+    MP4SampleId   sampleId,
+    uint8_t**     ppBytes,
+    uint32_t*     pNumBytes,
+    MP4Timestamp* pStartTime,
+    MP4Duration*  pDuration,
+    MP4Duration*  pRenderingOffset,
+    bool*         pIsSyncSample,
+    bool*         hasDependencyFlags, 
+    uint32_t*     dependencyFlags )
+{
+    if( sampleId == MP4_INVALID_SAMPLE_ID )
+        throw new Exception( "sample id can't be zero", __FILE__, __LINE__, __FUNCTION__ );
+
+    if( hasDependencyFlags )
+        *hasDependencyFlags = !m_sdtpLog.empty();
+
+    if( dependencyFlags ) {
+        if( m_sdtpLog.empty() ) {
+            *dependencyFlags = 0;
+        }
+        else {
+            if( sampleId > m_sdtpLog.size() )
+                throw new Exception( "sample id > sdtp logsize", __FILE__, __LINE__, __FUNCTION__ );
+            *dependencyFlags = m_sdtpLog[sampleId-1]; // sampleId is 1-based
+        }
+    }
+
+    // handle unusual case of wanting to read a sample
+    // that is still sitting in the write chunk buffer
+    if (m_pChunkBuffer && sampleId >= m_writeSampleId - m_chunkSamples) {
+        WriteChunkBuffer();
+    }
+
+    File* fin = GetSampleFile( sampleId );
+    if( fin == (File*)-1 )
+        throw new Exception( "sample is located in an inaccessible file", __FILE__, __LINE__, __FUNCTION__ );
+
+    uint64_t fileOffset = GetSampleFileOffset(sampleId);
+
+    uint32_t sampleSize = GetSampleSize(sampleId);
+    if (*ppBytes != NULL && *pNumBytes < sampleSize) {
+        throw new Exception("sample buffer is too small",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+    *pNumBytes = sampleSize;
+
+    log.verbose3f("\"%s\": ReadSample: track %u id %u offset 0x%" PRIx64 " size %u (0x%x)",
+                  GetFile().GetFilename().c_str(), m_trackId, sampleId, fileOffset, *pNumBytes, *pNumBytes);
+
+    bool bufferMalloc = false;
+    if (*ppBytes == NULL) {
+        *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes);
+        bufferMalloc = true;
+    }
+
+    uint64_t oldPos = m_File.GetPosition( fin ); // only used in mode == 'w'
+    try {
+        m_File.SetPosition( fileOffset, fin );
+        m_File.ReadBytes( *ppBytes, *pNumBytes, fin );
+
+        if (pStartTime || pDuration) {
+            GetSampleTimes(sampleId, pStartTime, pDuration);
+
+            log.verbose3f("\"%s\": ReadSample:  start %" PRIu64 " duration %" PRId64,
+                          GetFile().GetFilename().c_str(), (pStartTime ? *pStartTime : 0),
+                          (pDuration ? *pDuration : 0));
+        }
+        if (pRenderingOffset) {
+            *pRenderingOffset = GetSampleRenderingOffset(sampleId);
+
+            log.verbose3f("\"%s\": ReadSample:  renderingOffset %" PRId64,
+                          GetFile().GetFilename().c_str(), *pRenderingOffset);
+        }
+        if (pIsSyncSample) {
+            *pIsSyncSample = IsSyncSample(sampleId);
+
+            log.verbose3f("\"%s\": ReadSample:  isSyncSample %u",
+                          GetFile().GetFilename().c_str(), *pIsSyncSample);
+        }
+    }
+
+    catch (Exception* x) {
+        if( bufferMalloc ) {
+            MP4Free( *ppBytes );
+            *ppBytes = NULL;
+        }
+
+        if( m_File.IsWriteMode() )
+            m_File.SetPosition( oldPos, fin );
+
+        throw x;
+    }
+
+    if( m_File.IsWriteMode() )
+        m_File.SetPosition( oldPos, fin );
+}
+
+void MP4Track::ReadSampleFragment(
+    MP4SampleId sampleId,
+    uint32_t sampleOffset,
+    uint16_t sampleLength,
+    uint8_t* pDest)
+{
+    if (sampleId == MP4_INVALID_SAMPLE_ID) {
+        throw new Exception("invalid sample id",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    if (sampleId != m_cachedReadSampleId) {
+        MP4Free(m_pCachedReadSample);
+        m_pCachedReadSample = NULL;
+        m_cachedReadSampleSize = 0;
+        m_cachedReadSampleId = MP4_INVALID_SAMPLE_ID;
+
+        ReadSample(
+            sampleId,
+            &m_pCachedReadSample,
+            &m_cachedReadSampleSize);
+
+        m_cachedReadSampleId = sampleId;
+    }
+
+    if (sampleOffset + sampleLength > m_cachedReadSampleSize) {
+        throw new Exception("offset and/or length are too large",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    memcpy(pDest, &m_pCachedReadSample[sampleOffset], sampleLength);
+}
+
+void MP4Track::WriteSample(
+    const uint8_t* pBytes,
+    uint32_t       numBytes,
+    MP4Duration    duration,
+    MP4Duration    renderingOffset,
+    bool           isSyncSample )
+{
+    uint8_t curMode = 0;
+
+    log.verbose3f("\"%s\": WriteSample: track %u id %u size %u (0x%x) ",
+                  GetFile().GetFilename().c_str(),
+                  m_trackId, m_writeSampleId, numBytes, numBytes);
+
+    if (pBytes == NULL && numBytes > 0) {
+        throw new Exception("no sample data", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    if (m_isAmr == AMR_UNINITIALIZED ) {
+        // figure out if this is an AMR audio track
+        if (m_trakAtom.FindAtom("trak.mdia.minf.stbl.stsd.samr") ||
+                m_trakAtom.FindAtom("trak.mdia.minf.stbl.stsd.sawb")) {
+            m_isAmr = AMR_TRUE;
+            m_curMode = (pBytes[0] >> 3) & 0x000F;
+        } else {
+            m_isAmr = AMR_FALSE;
+        }
+    }
+
+    if (m_isAmr == AMR_TRUE) {
+        curMode = (pBytes[0] >> 3) &0x000F; // The mode is in the first byte
+    }
+
+    if (duration == MP4_INVALID_DURATION) {
+        duration = GetFixedSampleDuration();
+    }
+
+    log.verbose3f("\"%s\": duration %" PRIu64, GetFile().GetFilename().c_str(), 
+                  duration);
+
+    if ((m_isAmr == AMR_TRUE) &&
+            (m_curMode != curMode)) {
+        WriteChunkBuffer();
+        m_curMode = curMode;
+    }
+
+    // append sample bytes to chunk buffer
+    if( m_sizeOfDataInChunkBuffer + numBytes > m_chunkBufferSize ) {
+        m_pChunkBuffer = (uint8_t*)MP4Realloc(m_pChunkBuffer, m_chunkBufferSize + numBytes);
+        if (m_pChunkBuffer == NULL) 
+            return;	
+        
+        m_chunkBufferSize += numBytes;
+    }
+
+    memcpy(&m_pChunkBuffer[m_sizeOfDataInChunkBuffer], pBytes, numBytes);
+    m_sizeOfDataInChunkBuffer += numBytes;
+    m_chunkSamples++;
+    m_chunkDuration += duration;
+
+    UpdateSampleSizes(m_writeSampleId, numBytes);
+
+    UpdateSampleTimes(duration);
+
+    UpdateRenderingOffsets(m_writeSampleId, renderingOffset);
+
+    UpdateSyncSamples(m_writeSampleId, isSyncSample);
+
+    if (IsChunkFull(m_writeSampleId)) {
+        WriteChunkBuffer();
+        m_curMode = curMode;
+    }
+
+    UpdateDurations(duration);
+
+    UpdateModificationTimes();
+
+    m_writeSampleId++;
+}
+
+void MP4Track::WriteSampleDependency(
+    const uint8_t* pBytes,
+    uint32_t       numBytes,
+    MP4Duration    duration,
+    MP4Duration    renderingOffset,
+    bool           isSyncSample,
+    uint32_t       dependencyFlags )
+{
+    m_sdtpLog.push_back( dependencyFlags ); // record dependency flags for processing at finish
+    WriteSample( pBytes, numBytes, duration, renderingOffset, isSyncSample );
+}
+
+void MP4Track::WriteChunkBuffer()
+{
+    if (m_sizeOfDataInChunkBuffer == 0) {
+        return;
+    }
+
+    uint64_t chunkOffset = m_File.GetPosition();
+
+    // write chunk buffer
+    m_File.WriteBytes(m_pChunkBuffer, m_sizeOfDataInChunkBuffer);
+
+    log.verbose3f("\"%s\": WriteChunk: track %u offset 0x%" PRIx64 " size %u (0x%x) numSamples %u",
+                  GetFile().GetFilename().c_str(), 
+                  m_trackId, chunkOffset, m_sizeOfDataInChunkBuffer,
+                  m_sizeOfDataInChunkBuffer, m_chunkSamples);
+
+    UpdateSampleToChunk(m_writeSampleId,
+                        m_pChunkCountProperty->GetValue() + 1,
+                        m_chunkSamples);
+
+    UpdateChunkOffsets(chunkOffset);
+
+    // note: we do not free our chunk buffer; we reuse it, expanding as needed.
+    // It gets zapped when this class goes out of scope
+    m_sizeOfDataInChunkBuffer = 0;
+    m_chunkSamples = 0;
+    m_chunkDuration = 0;
+}
+
+void MP4Track::FinishWrite(uint32_t options)
+{
+    FinishSdtp();
+
+    // write out any remaining samples in chunk buffer
+    WriteChunkBuffer();
+
+    if (m_pStszFixedSampleSizeProperty == NULL &&
+            m_stsz_sample_bits == 4) {
+        if (m_have_stz2_4bit_sample) {
+            ((MP4Integer8Property *)m_pStszSampleSizeProperty)->AddValue(m_stz2_4bit_sample_value);
+            m_pStszSampleSizeProperty->IncrementValue();
+        }
+    }
+
+    // record buffer size and bitrates
+    MP4BitfieldProperty* pBufferSizeProperty;
+
+    if (m_trakAtom.FindProperty(
+                "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.bufferSizeDB",
+                (MP4Property**)&pBufferSizeProperty)) {
+        pBufferSizeProperty->SetValue(GetMaxSampleSize());
+    }
+
+	// don't overwrite bitrate if it was requested in the Close call
+    if( !(options & MP4_CLOSE_DO_NOT_COMPUTE_BITRATE)) {
+        MP4Integer32Property* pBitrateProperty;
+
+        if (m_trakAtom.FindProperty(
+                    "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.maxBitrate",
+                    (MP4Property**)&pBitrateProperty)) {
+            pBitrateProperty->SetValue(GetMaxBitrate());
+        }
+
+        if (m_trakAtom.FindProperty(
+                    "trak.mdia.minf.stbl.stsd.*.esds.decConfigDescr.avgBitrate",
+                    (MP4Property**)&pBitrateProperty)) {
+            pBitrateProperty->SetValue(GetAvgBitrate());
+        }
+    }
+
+    // cleaup trak.udta
+    MP4BytesProperty* nameProperty = NULL;
+    m_trakAtom.FindProperty("trak.udta.name.value", (MP4Property**) &nameProperty);
+    if( nameProperty != NULL && nameProperty->GetValueSize() == 0 ){
+        // Zero length name value--delete name, and then udta if no child atoms
+        MP4Atom* name = m_trakAtom.FindChildAtom("udta.name");
+        if( name ) {
+            MP4Atom* udta = name->GetParentAtom();
+            udta->DeleteChildAtom( name );
+            delete name;
+
+            if( udta->GetNumberOfChildAtoms() == 0 ) {
+                udta->GetParentAtom()->DeleteChildAtom( udta );
+                delete udta;
+            }
+        }
+    }
+}
+
+// Process sdtp log and add sdtp atom.
+//
+// Testing (subjective) showed a marked improvement with QuickTime
+// player on Mac OS X when scrubbing. Best results were obtained
+// from encodings using low number of bframes. It's expected sdtp may help
+// other QT-based players.
+//
+void MP4Track::FinishSdtp()
+{
+    // bail if log is empty -- indicates dependency information was not written
+    if( m_sdtpLog.empty() )
+        return;
+
+    MP4SdtpAtom* sdtp = (MP4SdtpAtom*)m_trakAtom.FindAtom( "trak.mdia.minf.stbl.sdtp" );
+    if( !sdtp )
+        sdtp = (MP4SdtpAtom*)AddAtom( "trak.mdia.minf.stbl", "sdtp" );
+    sdtp->data.SetValue( (const uint8_t*)m_sdtpLog.data(), (uint32_t)m_sdtpLog.size() );
+
+    // add avc1 compatibility indicator if not present
+    MP4FtypAtom* ftyp = (MP4FtypAtom*)m_File.FindAtom( "ftyp" );
+    if( ftyp ) {
+        bool found = false;
+        const uint32_t max = ftyp->compatibleBrands.GetCount();
+        for( uint32_t i = 0; i < max; i++ ) {
+            if( !strcmp( ftyp->compatibleBrands.GetValue( i ), "avc1" )) {
+                found = true;
+                break;
+            }
+        }
+
+        if( !found )
+            ftyp->compatibleBrands.AddValue( "avc1" );
+    }
+}
+
+bool MP4Track::IsChunkFull(MP4SampleId sampleId)
+{
+    if (m_samplesPerChunk) {
+        return m_chunkSamples >= m_samplesPerChunk;
+    }
+
+    ASSERT(m_durationPerChunk);
+    return m_chunkDuration >= m_durationPerChunk;
+}
+
+uint32_t MP4Track::GetNumberOfSamples()
+{
+    return m_pStszSampleCountProperty->GetValue();
+}
+
+uint32_t MP4Track::GetSampleSize(MP4SampleId sampleId)
+{
+    if (m_pStszFixedSampleSizeProperty != NULL) {
+        uint32_t fixedSampleSize =
+            m_pStszFixedSampleSizeProperty->GetValue();
+
+        if (fixedSampleSize != 0) {
+            return fixedSampleSize * m_bytesPerSample;
+        }
+    }
+    // will have to check for 4 bit sample size here
+    if (m_stsz_sample_bits == 4) {
+        uint8_t value = m_pStszSampleSizeProperty->GetValue((sampleId - 1) / 2);
+        if ((sampleId - 1) / 2 == 0) {
+            value >>= 4;
+        } else value &= 0xf;
+        return m_bytesPerSample * value;
+    }
+    return m_bytesPerSample *
+           m_pStszSampleSizeProperty->GetValue(sampleId - 1);
+}
+
+uint32_t MP4Track::GetMaxSampleSize()
+{
+    if (m_pStszFixedSampleSizeProperty != NULL) {
+        uint32_t fixedSampleSize =
+            m_pStszFixedSampleSizeProperty->GetValue();
+
+        if (fixedSampleSize != 0) {
+            return fixedSampleSize * m_bytesPerSample;
+        }
+    }
+
+    uint32_t maxSampleSize = 0;
+    uint32_t numSamples = m_pStszSampleSizeProperty->GetCount();
+    for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
+        uint32_t sampleSize =
+            m_pStszSampleSizeProperty->GetValue(sid - 1);
+        if (sampleSize > maxSampleSize) {
+            maxSampleSize = sampleSize;
+        }
+    }
+    return maxSampleSize * m_bytesPerSample;
+}
+
+uint64_t MP4Track::GetTotalOfSampleSizes()
+{
+    uint64_t retval;
+    if (m_pStszFixedSampleSizeProperty != NULL) {
+        uint32_t fixedSampleSize =
+            m_pStszFixedSampleSizeProperty->GetValue();
+
+        // if fixed sample size, just need to multiply by number of samples
+        if (fixedSampleSize != 0) {
+            retval = m_bytesPerSample;
+            retval *= fixedSampleSize;
+            retval *= GetNumberOfSamples();
+            return retval;
+        }
+    }
+
+    // else non-fixed sample size, sum them
+    uint64_t totalSampleSizes = 0;
+    uint32_t numSamples = m_pStszSampleSizeProperty->GetCount();
+    for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
+        uint32_t sampleSize =
+            m_pStszSampleSizeProperty->GetValue(sid - 1);
+        totalSampleSizes += sampleSize;
+    }
+    return totalSampleSizes * m_bytesPerSample;
+}
+
+void MP4Track::SampleSizePropertyAddValue (uint32_t size)
+{
+    // this has to deal with different sample size values
+    switch (m_pStszSampleSizeProperty->GetType()) {
+    case Integer32Property:
+        ((MP4Integer32Property *)m_pStszSampleSizeProperty)->AddValue(size);
+        break;
+    case Integer16Property:
+        ((MP4Integer16Property *)m_pStszSampleSizeProperty)->AddValue(size);
+        break;
+    case Integer8Property:
+        if (m_stsz_sample_bits == 4) {
+            if (m_have_stz2_4bit_sample == false) {
+                m_have_stz2_4bit_sample = true;
+                m_stz2_4bit_sample_value = size << 4;
+                return;
+            } else {
+                m_have_stz2_4bit_sample = false;
+                size &= 0xf;
+                size |= m_stz2_4bit_sample_value;
+            }
+        }
+        ((MP4Integer8Property *)m_pStszSampleSizeProperty)->AddValue(size);
+        break;
+    default:
+        break;
+    }
+
+
+    //  m_pStszSampleSizeProperty->IncrementValue();
+}
+
+void MP4Track::UpdateSampleSizes(MP4SampleId sampleId, uint32_t numBytes)
+{
+    if (m_bytesPerSample > 1) {
+        if ((numBytes % m_bytesPerSample) != 0) {
+            // error
+            log.errorf("%s: \"%s\": numBytes %u not divisible by bytesPerSample %u sampleId %u",
+                       __FUNCTION__, GetFile().GetFilename().c_str(),
+                       numBytes, m_bytesPerSample, sampleId);
+        }
+        numBytes /= m_bytesPerSample;
+    }
+    // for first sample
+    // wmay - if we are adding, we want to make sure that
+    // we don't inadvertently set up the fixed size again.
+    // so, we check the number of samples
+    if (sampleId == 1 && GetNumberOfSamples() == 0) {
+        if (m_pStszFixedSampleSizeProperty == NULL ||
+                numBytes == 0) {
+            // special case of first sample is zero bytes in length
+            // leave m_pStszFixedSampleSizeProperty at 0
+            // start recording variable sample sizes
+            if (m_pStszFixedSampleSizeProperty != NULL)
+                m_pStszFixedSampleSizeProperty->SetValue(0);
+            SampleSizePropertyAddValue(0);
+        } else {
+            // presume sample size is fixed
+            m_pStszFixedSampleSizeProperty->SetValue(numBytes);
+        }
+    } else { // sampleId > 1
+
+        uint32_t fixedSampleSize = 0;
+        if (m_pStszFixedSampleSizeProperty != NULL) {
+            fixedSampleSize = m_pStszFixedSampleSizeProperty->GetValue();
+        }
+
+        // if we don't have a fixed size, or the current sample size
+        // doesn't match our sample size, we need to write the current
+        // sample size into the table
+        if (fixedSampleSize == 0 || numBytes != fixedSampleSize) {
+            if (fixedSampleSize != 0) {
+                // fixed size was set; we need to clear fixed sample size
+                m_pStszFixedSampleSizeProperty->SetValue(0);
+
+                // and create sizes for all previous samples
+                // use GetNumberOfSamples due to needing the total number
+                // not just the appended part of the file
+                uint32_t samples = GetNumberOfSamples();
+                for (MP4SampleId sid = 1; sid <= samples; sid++) {
+                    SampleSizePropertyAddValue(fixedSampleSize);
+                }
+            }
+            // add size value for this sample
+            SampleSizePropertyAddValue(numBytes);
+        }
+    }
+    // either way, we increment the number of samples.
+    m_pStszSampleCountProperty->IncrementValue();
+}
+
+uint32_t MP4Track::GetAvgBitrate()
+{
+    if (GetDuration() == 0) {
+        return 0;
+    }
+
+    double calc = double(GetTotalOfSampleSizes());
+    // this is a bit better - we use the whole duration
+    calc *= 8.0;
+    calc *= GetTimeScale();
+    calc /= double(GetDuration());
+    // we might want to think about rounding to the next 100 or 1000
+    return (uint32_t) ceil(calc);
+}
+
+uint32_t MP4Track::GetMaxBitrate()
+{
+    uint32_t timeScale = GetTimeScale();
+    MP4SampleId numSamples = GetNumberOfSamples();
+    uint32_t maxBytesPerSec = 0;
+    uint32_t bytesThisSec = 0;
+    MP4Timestamp thisSecStart = 0;
+    MP4Timestamp lastSampleTime = 0;
+    uint32_t lastSampleSize = 0;
+
+    MP4SampleId thisSecStartSid = 1;
+    for (MP4SampleId sid = 1; sid <= numSamples; sid++) {
+        uint32_t sampleSize;
+        MP4Timestamp sampleTime;
+
+        sampleSize = GetSampleSize(sid);
+        GetSampleTimes(sid, &sampleTime, NULL);
+
+        if (sampleTime < thisSecStart + timeScale) {
+            bytesThisSec += sampleSize;
+            lastSampleSize = sampleSize;
+            lastSampleTime = sampleTime;
+        } else {
+            // we've already written the last sample and sampleSize.
+            // this means that we've probably overflowed the last second
+            // calculate the time we've overflowed
+            MP4Duration overflow_dur =
+                (thisSecStart + timeScale) - lastSampleTime;
+            // calculate the duration of the last sample
+            MP4Duration lastSampleDur = sampleTime - lastSampleTime;
+            // now, calculate the number of bytes we overflowed.  Round up.
+            if( lastSampleDur > 0 ) {
+                uint32_t overflow_bytes = 0;
+                overflow_bytes = ((lastSampleSize * overflow_dur) + (lastSampleDur - 1)) / lastSampleDur;
+
+                if (bytesThisSec - overflow_bytes > maxBytesPerSec) {
+                    maxBytesPerSec = bytesThisSec - overflow_bytes;
+                }
+            }
+
+            // now adjust the values for this sample.  Remove the bytes
+            // from the first sample in this time frame
+            lastSampleTime = sampleTime;
+            lastSampleSize = sampleSize;
+            bytesThisSec += sampleSize;
+            bytesThisSec -= GetSampleSize(thisSecStartSid);
+            thisSecStartSid++;
+            GetSampleTimes(thisSecStartSid, &thisSecStart, NULL);
+        }
+    }
+
+    return maxBytesPerSec * 8;
+}
+
+uint32_t MP4Track::GetSampleStscIndex(MP4SampleId sampleId)
+{
+    uint32_t stscIndex;
+    uint32_t numStscs = m_pStscCountProperty->GetValue();
+
+    if (numStscs == 0) {
+        throw new Exception("No data chunks exist", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
+        if (sampleId < m_pStscFirstSampleProperty->GetValue(stscIndex)) {
+            ASSERT(stscIndex != 0);
+            stscIndex -= 1;
+            break;
+        }
+    }
+    if (stscIndex == numStscs) {
+        ASSERT(stscIndex != 0);
+        stscIndex -= 1;
+    }
+
+    return stscIndex;
+}
+
+File* MP4Track::GetSampleFile( MP4SampleId sampleId )
+{
+    uint32_t stscIndex = GetSampleStscIndex( sampleId );
+    uint32_t stsdIndex = m_pStscSampleDescrIndexProperty->GetValue( stscIndex );
+
+    // check if the answer will be the same as last time
+    if( m_lastStsdIndex && stsdIndex == m_lastStsdIndex )
+        return m_lastSampleFile;
+
+    MP4Atom* pStsdAtom = m_trakAtom.FindAtom( "trak.mdia.minf.stbl.stsd" );
+    ASSERT( pStsdAtom );
+
+    MP4Atom* pStsdEntryAtom = pStsdAtom->GetChildAtom( stsdIndex - 1 );
+    ASSERT( pStsdEntryAtom );
+
+    MP4Integer16Property* pDrefIndexProperty = NULL;
+    if( !pStsdEntryAtom->FindProperty( "*.dataReferenceIndex", (MP4Property**)&pDrefIndexProperty ) ||
+        pDrefIndexProperty == NULL )
+    {
+        throw new Exception( "invalid stsd entry", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    uint32_t drefIndex = pDrefIndexProperty->GetValue();
+
+    MP4Atom* pDrefAtom = m_trakAtom.FindAtom( "trak.mdia.minf.dinf.dref" );
+    ASSERT(pDrefAtom);
+
+    MP4Atom* pUrlAtom = pDrefAtom->GetChildAtom( drefIndex - 1 );
+    ASSERT( pUrlAtom );
+
+    File* file;
+
+    // make sure this is actually a url atom (somtimes it's "cios", like in iTunes videos)
+    if( strcmp(pUrlAtom->GetType(), "url ") ||
+        pUrlAtom->GetFlags() & 1 ) {
+        file = NULL; // self-contained
+    }
+    else {
+        MP4StringProperty* pLocationProperty = NULL;
+        ASSERT( pUrlAtom->FindProperty( "*.location", (MP4Property**)&pLocationProperty) );
+        ASSERT( pLocationProperty );
+
+        const char* url = pLocationProperty->GetValue();
+
+        log.verbose3f("\"%s\": dref url = %s", GetFile().GetFilename().c_str(), 
+                      url);
+
+        file = (File*)-1;
+
+        // attempt to open url if it's a file url
+        // currently this is the only thing we understand
+        if( !strncmp( url, "file:", 5 )) {
+            const char* fileName = url + 5;
+
+            if( !strncmp(fileName, "//", 2 ))
+                fileName = strchr( fileName + 2, '/' );
+
+            if( fileName ) {
+                file = new File( fileName, File::MODE_READ );
+                if( !file->open() ) {
+                    delete file;
+                    file = (File*)-1;
+                }
+            }
+        }
+    }
+
+    if( m_lastSampleFile )
+        m_lastSampleFile->close();
+
+    // cache the answer
+    m_lastStsdIndex = stsdIndex;
+    m_lastSampleFile = file;
+
+    return file;
+}
+
+uint64_t MP4Track::GetSampleFileOffset(MP4SampleId sampleId)
+{
+    uint32_t stscIndex =
+        GetSampleStscIndex(sampleId);
+
+    // firstChunk is the chunk index of the first chunk with
+    // samplesPerChunk samples in the chunk.  There may be multiples -
+    // ie: several chunks with the same number of samples per chunk.
+    uint32_t firstChunk =
+        m_pStscFirstChunkProperty->GetValue(stscIndex);
+
+    MP4SampleId firstSample =
+        m_pStscFirstSampleProperty->GetValue(stscIndex);
+
+    uint32_t samplesPerChunk =
+        m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
+
+    // chunkId tells which is the absolute chunk number that this sample
+    // is stored in.
+    MP4ChunkId chunkId = firstChunk +
+                         ((sampleId - firstSample) / samplesPerChunk);
+
+    // chunkOffset is the file offset (absolute) for the start of the chunk
+    uint64_t chunkOffset = m_pChunkOffsetProperty->GetValue(chunkId - 1);
+
+    MP4SampleId firstSampleInChunk =
+        sampleId - ((sampleId - firstSample) % samplesPerChunk);
+
+    // need cumulative samples sizes from firstSample to sampleId - 1
+    uint32_t sampleOffset = 0;
+    for (MP4SampleId i = firstSampleInChunk; i < sampleId; i++) {
+        sampleOffset += GetSampleSize(i);
+    }
+
+    return chunkOffset + sampleOffset;
+}
+
+void MP4Track::UpdateSampleToChunk(MP4SampleId sampleId,
+                                   MP4ChunkId chunkId, uint32_t samplesPerChunk)
+{
+    uint32_t numStsc = m_pStscCountProperty->GetValue();
+
+    // if samplesPerChunk == samplesPerChunk of last entry
+    if (numStsc && samplesPerChunk ==
+            m_pStscSamplesPerChunkProperty->GetValue(numStsc-1)) {
+
+        // nothing to do
+
+    } else {
+        // add stsc entry
+        m_pStscFirstChunkProperty->AddValue(chunkId);
+        m_pStscSamplesPerChunkProperty->AddValue(samplesPerChunk);
+        m_pStscSampleDescrIndexProperty->AddValue(1);
+        m_pStscFirstSampleProperty->AddValue(sampleId - samplesPerChunk + 1);
+
+        m_pStscCountProperty->IncrementValue();
+    }
+}
+
+void MP4Track::UpdateChunkOffsets(uint64_t chunkOffset)
+{
+    if (m_pChunkOffsetProperty->GetType() == Integer32Property) {
+        ((MP4Integer32Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
+    } else {
+        ((MP4Integer64Property*)m_pChunkOffsetProperty)->AddValue(chunkOffset);
+    }
+    m_pChunkCountProperty->IncrementValue();
+}
+
+MP4Duration MP4Track::GetFixedSampleDuration()
+{
+    uint32_t numStts = m_pSttsCountProperty->GetValue();
+
+    if (numStts == 0) {
+        return m_fixedSampleDuration;
+    }
+    if (numStts != 1) {
+        return MP4_INVALID_DURATION;    // sample duration is not fixed
+    }
+    return m_pSttsSampleDeltaProperty->GetValue(0);
+}
+
+void MP4Track::SetFixedSampleDuration(MP4Duration duration)
+{
+    uint32_t numStts = m_pSttsCountProperty->GetValue();
+
+    // setting this is only allowed before samples have been written
+    if (numStts != 0) {
+        return;
+    }
+    m_fixedSampleDuration = duration;
+    return;
+}
+
+void MP4Track::GetSampleTimes(MP4SampleId sampleId,
+                              MP4Timestamp* pStartTime, MP4Duration* pDuration)
+{
+    uint32_t numStts = m_pSttsCountProperty->GetValue();
+    MP4SampleId sid;
+    MP4Duration elapsed;
+
+
+    if (m_cachedSttsSid != MP4_INVALID_SAMPLE_ID && sampleId >= m_cachedSttsSid) {
+        sid   = m_cachedSttsSid;
+        elapsed   = m_cachedSttsElapsed;
+    } else {
+        m_cachedSttsIndex = 0;
+        sid   = 1;
+        elapsed   = 0;
+    }
+
+    for (uint32_t sttsIndex = m_cachedSttsIndex; sttsIndex < numStts; sttsIndex++) {
+        uint32_t sampleCount =
+            m_pSttsSampleCountProperty->GetValue(sttsIndex);
+        uint32_t sampleDelta =
+            m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
+
+        if (sampleId <= sid + sampleCount - 1) {
+            if (pStartTime) {
+                *pStartTime = (sampleId - sid);
+                *pStartTime *= sampleDelta;
+                *pStartTime += elapsed;
+            }
+            if (pDuration) {
+                *pDuration = sampleDelta;
+            }
+
+            m_cachedSttsIndex = sttsIndex;
+            m_cachedSttsSid = sid;
+            m_cachedSttsElapsed = elapsed;
+
+            return;
+        }
+        sid += sampleCount;
+        elapsed += sampleCount * sampleDelta;
+    }
+
+    throw new Exception("sample id out of range",
+                        __FILE__, __LINE__, __FUNCTION__ );
+}
+
+MP4SampleId MP4Track::GetSampleIdFromTime(
+    MP4Timestamp when,
+    bool wantSyncSample)
+{
+    uint32_t numStts = m_pSttsCountProperty->GetValue();
+    MP4SampleId sid = 1;
+    MP4Duration elapsed = 0;
+
+    for (uint32_t sttsIndex = 0; sttsIndex < numStts; sttsIndex++) {
+        uint32_t sampleCount =
+            m_pSttsSampleCountProperty->GetValue(sttsIndex);
+        uint32_t sampleDelta =
+            m_pSttsSampleDeltaProperty->GetValue(sttsIndex);
+
+        if (sampleDelta == 0 && sttsIndex < numStts - 1) {
+            log.warningf("%s: \"%s\": Zero sample duration, stts entry %u",
+                         __FUNCTION__, GetFile().GetFilename().c_str(), sttsIndex);
+        }
+
+        MP4Duration d = when - elapsed;
+
+        if (d <= sampleCount * sampleDelta) {
+            MP4SampleId sampleId = sid;
+            if (sampleDelta) {
+                sampleId += (d / sampleDelta);
+            }
+
+            if (wantSyncSample) {
+                return GetNextSyncSample(sampleId);
+            }
+            return sampleId;
+        }
+
+        sid += sampleCount;
+        elapsed += sampleCount * sampleDelta;
+    }
+
+    throw new Exception("time out of range",
+                        __FILE__, __LINE__, __FUNCTION__);
+
+    return 0; // satisfy MS compiler
+}
+
+void MP4Track::UpdateSampleTimes(MP4Duration duration)
+{
+    uint32_t numStts = m_pSttsCountProperty->GetValue();
+
+    // if duration == duration of last entry
+    if (numStts
+            && duration == m_pSttsSampleDeltaProperty->GetValue(numStts-1)) {
+        // increment last entry sampleCount
+        m_pSttsSampleCountProperty->IncrementValue(1, numStts-1);
+
+    } else {
+        // add stts entry, sampleCount = 1, sampleDuration = duration
+        m_pSttsSampleCountProperty->AddValue(1);
+        m_pSttsSampleDeltaProperty->AddValue(duration);
+        m_pSttsCountProperty->IncrementValue();;
+    }
+}
+
+uint32_t MP4Track::GetSampleCttsIndex(MP4SampleId sampleId,
+                                      MP4SampleId* pFirstSampleId)
+{
+    uint32_t numCtts = m_pCttsCountProperty->GetValue();
+    MP4SampleId sid;
+
+    if (m_cachedCttsSid != MP4_INVALID_SAMPLE_ID && sampleId >= m_cachedCttsSid) {
+        sid   = m_cachedCttsSid;
+    } else {
+        m_cachedCttsIndex = 0;
+        sid = 1;
+    }
+
+    for (uint32_t cttsIndex = m_cachedCttsIndex; cttsIndex < numCtts; cttsIndex++) {
+        uint32_t sampleCount =
+            m_pCttsSampleCountProperty->GetValue(cttsIndex);
+        
+        if (sampleId <= sid + sampleCount - 1) {
+            if (pFirstSampleId) {
+                *pFirstSampleId = sid;
+            }
+
+            m_cachedCttsIndex = cttsIndex;
+            m_cachedCttsSid = sid;
+
+            return cttsIndex;
+        }
+        sid += sampleCount;
+    }
+
+    throw new Exception("sample id out of range",
+                        __FILE__, __LINE__, __FUNCTION__ );
+    return 0; // satisfy MS compiler
+}
+
+MP4Duration MP4Track::GetSampleRenderingOffset(MP4SampleId sampleId)
+{
+    if (m_pCttsCountProperty == NULL) {
+        return 0;
+    }
+    if (m_pCttsCountProperty->GetValue() == 0) {
+        return 0;
+    }
+
+    uint32_t cttsIndex = GetSampleCttsIndex(sampleId);
+
+    return m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
+}
+
+void MP4Track::UpdateRenderingOffsets(MP4SampleId sampleId,
+                                      MP4Duration renderingOffset)
+{
+    // if ctts atom doesn't exist
+    if (m_pCttsCountProperty == NULL) {
+
+        // no rendering offset, so nothing to do
+        if (renderingOffset == 0) {
+            return;
+        }
+
+        // else create a ctts atom
+        MP4Atom* pCttsAtom = AddAtom("trak.mdia.minf.stbl", "ctts");
+
+        // and get handles on the properties
+        ASSERT(pCttsAtom->FindProperty(
+                   "ctts.entryCount",
+                   (MP4Property**)&m_pCttsCountProperty));
+
+        ASSERT(pCttsAtom->FindProperty(
+                   "ctts.entries.sampleCount",
+                   (MP4Property**)&m_pCttsSampleCountProperty));
+
+        ASSERT(pCttsAtom->FindProperty(
+                   "ctts.entries.sampleOffset",
+                   (MP4Property**)&m_pCttsSampleOffsetProperty));
+
+        // if this is not the first sample
+        if (sampleId > 1) {
+            // add a ctts entry for all previous samples
+            // with rendering offset equal to zero
+            m_pCttsSampleCountProperty->AddValue(sampleId - 1);
+            m_pCttsSampleOffsetProperty->AddValue(0);
+            m_pCttsCountProperty->IncrementValue();;
+        }
+    }
+
+    // ctts atom exists (now)
+
+    uint32_t numCtts = m_pCttsCountProperty->GetValue();
+
+    // if renderingOffset == renderingOffset of last entry
+    if (numCtts && renderingOffset
+            == m_pCttsSampleOffsetProperty->GetValue(numCtts-1)) {
+
+        // increment last entry sampleCount
+        m_pCttsSampleCountProperty->IncrementValue(1, numCtts-1);
+
+    } else {
+        // add ctts entry, sampleCount = 1, sampleOffset = renderingOffset
+        m_pCttsSampleCountProperty->AddValue(1);
+        m_pCttsSampleOffsetProperty->AddValue(renderingOffset);
+        m_pCttsCountProperty->IncrementValue();
+    }
+}
+
+void MP4Track::SetSampleRenderingOffset(MP4SampleId sampleId,
+                                        MP4Duration renderingOffset)
+{
+    // check if any ctts entries exist
+    if (m_pCttsCountProperty == NULL
+            || m_pCttsCountProperty->GetValue() == 0) {
+        // if not then Update routine can be used
+        // to create a ctts entry for samples before this one
+        // and a ctts entry for this sample
+        UpdateRenderingOffsets(sampleId, renderingOffset);
+
+        // but we also need a ctts entry
+        // for all samples after this one
+        uint32_t afterSamples = GetNumberOfSamples() - sampleId;
+
+        if (afterSamples) {
+            m_pCttsSampleCountProperty->AddValue(afterSamples);
+            m_pCttsSampleOffsetProperty->AddValue(0);
+            m_pCttsCountProperty->IncrementValue();;
+        }
+
+        return;
+    }
+
+    MP4SampleId firstSampleId;
+    uint32_t cttsIndex = GetSampleCttsIndex(sampleId, &firstSampleId);
+
+    // do nothing in the degenerate case
+    if (renderingOffset ==
+            m_pCttsSampleOffsetProperty->GetValue(cttsIndex)) {
+        return;
+    }
+
+    uint32_t sampleCount =
+        m_pCttsSampleCountProperty->GetValue(cttsIndex);
+
+    // if this sample has it's own ctts entry
+    if (sampleCount == 1) {
+        // then just set the value,
+        // note we don't attempt to collapse entries
+        m_pCttsSampleOffsetProperty->SetValue(renderingOffset, cttsIndex);
+        return;
+    }
+
+    MP4SampleId lastSampleId = firstSampleId + sampleCount - 1;
+
+    // else we share this entry with other samples
+    // we need to insert our own entry
+    if (sampleId == firstSampleId) {
+        // our sample is the first one
+        m_pCttsSampleCountProperty->
+        InsertValue(1, cttsIndex);
+        m_pCttsSampleOffsetProperty->
+        InsertValue(renderingOffset, cttsIndex);
+
+        m_pCttsSampleCountProperty->
+        SetValue(sampleCount - 1, cttsIndex + 1);
+
+        m_pCttsCountProperty->IncrementValue();
+
+    } else if (sampleId == lastSampleId) {
+        // our sample is the last one
+        m_pCttsSampleCountProperty->
+        InsertValue(1, cttsIndex + 1);
+        m_pCttsSampleOffsetProperty->
+        InsertValue(renderingOffset, cttsIndex + 1);
+
+        m_pCttsSampleCountProperty->
+        SetValue(sampleCount - 1, cttsIndex);
+
+        m_pCttsCountProperty->IncrementValue();
+
+    } else {
+        // our sample is in the middle, UGH!
+
+        // insert our new entry
+        m_pCttsSampleCountProperty->
+        InsertValue(1, cttsIndex + 1);
+        m_pCttsSampleOffsetProperty->
+        InsertValue(renderingOffset, cttsIndex + 1);
+
+        // adjust count of previous entry
+        m_pCttsSampleCountProperty->
+        SetValue(sampleId - firstSampleId, cttsIndex);
+
+        // insert new entry for those samples beyond our sample
+        m_pCttsSampleCountProperty->
+        InsertValue(lastSampleId - sampleId, cttsIndex + 2);
+        uint32_t oldRenderingOffset =
+            m_pCttsSampleOffsetProperty->GetValue(cttsIndex);
+        m_pCttsSampleOffsetProperty->
+        InsertValue(oldRenderingOffset, cttsIndex + 2);
+
+        m_pCttsCountProperty->IncrementValue(2);
+    }
+}
+
+bool MP4Track::IsSyncSample(MP4SampleId sampleId)
+{
+    if (m_pStssCountProperty == NULL) {
+        return true;
+    }
+
+    uint32_t numStss = m_pStssCountProperty->GetValue();
+    uint32_t stssLIndex = 0;
+    uint32_t stssRIndex = numStss - 1;
+
+    while (stssRIndex >= stssLIndex) {
+        uint32_t stssIndex = (stssRIndex + stssLIndex) >> 1;
+        MP4SampleId syncSampleId =
+            m_pStssSampleProperty->GetValue(stssIndex);
+
+        if (sampleId == syncSampleId) {
+            return true;
+        }
+
+        if (sampleId > syncSampleId) {
+            stssLIndex = stssIndex + 1;
+        } else {
+            stssRIndex = stssIndex - 1;
+        }
+    }
+
+    return false;
+}
+
+// N.B. "next" is inclusive of this sample id
+MP4SampleId MP4Track::GetNextSyncSample(MP4SampleId sampleId)
+{
+    if (m_pStssCountProperty == NULL) {
+        return sampleId;
+    }
+
+    uint32_t numStss = m_pStssCountProperty->GetValue();
+
+    for (uint32_t stssIndex = 0; stssIndex < numStss; stssIndex++) {
+        MP4SampleId syncSampleId =
+            m_pStssSampleProperty->GetValue(stssIndex);
+
+        if (sampleId > syncSampleId) {
+            continue;
+        }
+        return syncSampleId;
+    }
+
+    // LATER check stsh for alternate sample
+
+    return MP4_INVALID_SAMPLE_ID;
+}
+
+void MP4Track::UpdateSyncSamples(MP4SampleId sampleId, bool isSyncSample)
+{
+    if (isSyncSample) {
+        // if stss atom exists, add entry
+        if (m_pStssCountProperty) {
+            m_pStssSampleProperty->AddValue(sampleId);
+            m_pStssCountProperty->IncrementValue();
+        } // else nothing to do (yet)
+
+    } else { // !isSyncSample
+        // if stss atom doesn't exist, create one
+        if (m_pStssCountProperty == NULL) {
+
+            MP4Atom* pStssAtom = AddAtom("trak.mdia.minf.stbl", "stss");
+
+            ASSERT(pStssAtom->FindProperty(
+                       "stss.entryCount",
+                       (MP4Property**)&m_pStssCountProperty));
+
+            ASSERT(pStssAtom->FindProperty(
+                       "stss.entries.sampleNumber",
+                       (MP4Property**)&m_pStssSampleProperty));
+
+            // set values for all samples that came before this one
+            uint32_t samples = GetNumberOfSamples();
+            for (MP4SampleId sid = 1; sid < samples; sid++) {
+                m_pStssSampleProperty->AddValue(sid);
+                m_pStssCountProperty->IncrementValue();
+            }
+        } // else nothing to do
+    }
+}
+
+MP4Atom* MP4Track::AddAtom(const char* parentName, const char* childName)
+{
+    MP4Atom* pParentAtom = m_trakAtom.FindAtom(parentName);
+    ASSERT(pParentAtom);
+
+    MP4Atom* pChildAtom = MP4Atom::CreateAtom(m_File, pParentAtom, childName);
+
+    pParentAtom->AddChildAtom(pChildAtom);
+
+    pChildAtom->Generate();
+
+    return pChildAtom;
+}
+
+uint64_t MP4Track::GetDuration()
+{
+    return m_pMediaDurationProperty->GetValue();
+}
+
+uint32_t MP4Track::GetTimeScale()
+{
+    return m_pTimeScaleProperty->GetValue();
+}
+
+void MP4Track::UpdateDurations(MP4Duration duration)
+{
+    // update media, track, and movie durations
+    m_pMediaDurationProperty->SetValue(
+        m_pMediaDurationProperty->GetValue() + duration);
+
+    MP4Duration movieDuration = ToMovieDuration(
+        m_pMediaDurationProperty->GetValue());
+    m_pTrackDurationProperty->SetValue(movieDuration);
+
+    m_File.UpdateDuration(m_pTrackDurationProperty->GetValue());
+}
+
+MP4Duration MP4Track::ToMovieDuration(MP4Duration trackDuration)
+{
+    return (trackDuration * m_File.GetTimeScale())
+           / m_pTimeScaleProperty->GetValue();
+}
+
+void MP4Track::UpdateModificationTimes()
+{
+    // update media and track modification times
+    MP4Timestamp now = MP4GetAbsTimestamp();
+    m_pMediaModificationProperty->SetValue(now);
+    m_pTrackModificationProperty->SetValue(now);
+}
+
+uint32_t MP4Track::GetNumberOfChunks()
+{
+    return m_pChunkOffsetProperty->GetCount();
+}
+
+uint32_t MP4Track::GetChunkStscIndex(MP4ChunkId chunkId)
+{
+    uint32_t stscIndex;
+    uint32_t numStscs = m_pStscCountProperty->GetValue();
+
+    ASSERT(chunkId);
+    ASSERT(numStscs > 0);
+
+    for (stscIndex = 0; stscIndex < numStscs; stscIndex++) {
+        if (chunkId < m_pStscFirstChunkProperty->GetValue(stscIndex)) {
+            ASSERT(stscIndex != 0);
+            break;
+        }
+    }
+    return stscIndex - 1;
+}
+
+MP4Timestamp MP4Track::GetChunkTime(MP4ChunkId chunkId)
+{
+    uint32_t stscIndex = GetChunkStscIndex(chunkId);
+
+    MP4ChunkId firstChunkId =
+        m_pStscFirstChunkProperty->GetValue(stscIndex);
+
+    MP4SampleId firstSample =
+        m_pStscFirstSampleProperty->GetValue(stscIndex);
+
+    uint32_t samplesPerChunk =
+        m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
+
+    MP4SampleId firstSampleInChunk =
+        firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
+
+    MP4Timestamp chunkTime;
+
+    GetSampleTimes(firstSampleInChunk, &chunkTime, NULL);
+
+    return chunkTime;
+}
+
+uint32_t MP4Track::GetChunkSize(MP4ChunkId chunkId)
+{
+    uint32_t stscIndex = GetChunkStscIndex(chunkId);
+
+    MP4ChunkId firstChunkId =
+        m_pStscFirstChunkProperty->GetValue(stscIndex);
+
+    MP4SampleId firstSample =
+        m_pStscFirstSampleProperty->GetValue(stscIndex);
+
+    uint32_t samplesPerChunk =
+        m_pStscSamplesPerChunkProperty->GetValue(stscIndex);
+
+    MP4SampleId firstSampleInChunk =
+        firstSample + ((chunkId - firstChunkId) * samplesPerChunk);
+
+    // need cumulative sizes of samples in chunk
+    uint32_t chunkSize = 0;
+    for (uint32_t i = 0; i < samplesPerChunk; i++) {
+        chunkSize += GetSampleSize(firstSampleInChunk + i);
+    }
+
+    return chunkSize;
+}
+
+void MP4Track::ReadChunk(MP4ChunkId chunkId,
+                         uint8_t** ppChunk, uint32_t* pChunkSize)
+{
+    ASSERT(chunkId);
+    ASSERT(ppChunk);
+    ASSERT(pChunkSize);
+
+    uint64_t chunkOffset =
+        m_pChunkOffsetProperty->GetValue(chunkId - 1);
+
+    *pChunkSize = GetChunkSize(chunkId);
+    *ppChunk = (uint8_t*)MP4Malloc(*pChunkSize);
+
+    log.verbose3f("\"%s\": ReadChunk: track %u id %u offset 0x%" PRIx64 " size %u (0x%x)",
+                  GetFile().GetFilename().c_str(),
+                  m_trackId, chunkId, chunkOffset, *pChunkSize, *pChunkSize);
+
+    uint64_t oldPos = m_File.GetPosition(); // only used in mode == 'w'
+    try {
+        m_File.SetPosition( chunkOffset );
+        m_File.ReadBytes( *ppChunk, *pChunkSize );
+    }
+    catch( Exception* x ) {
+        MP4Free( *ppChunk );
+        *ppChunk = NULL;
+
+        if( m_File.IsWriteMode() )
+            m_File.SetPosition( oldPos );
+
+        throw x;
+    }
+
+    if( m_File.IsWriteMode() )
+        m_File.SetPosition( oldPos );
+}
+
+void MP4Track::RewriteChunk(MP4ChunkId chunkId,
+                            uint8_t* pChunk, uint32_t chunkSize)
+{
+    uint64_t chunkOffset = m_File.GetPosition();
+
+    m_File.WriteBytes(pChunk, chunkSize);
+
+    m_pChunkOffsetProperty->SetValue(chunkOffset, chunkId - 1);
+
+    log.verbose3f("\"%s\": RewriteChunk: track %u id %u offset 0x%" PRIx64 " size %u (0x%x)",
+                  GetFile().GetFilename().c_str(),
+                  m_trackId, chunkId, chunkOffset, chunkSize, chunkSize);
+}
+
+// map track type name aliases to official names
+
+
+bool MP4Track::InitEditListProperties()
+{
+    m_pElstCountProperty = NULL;
+    m_pElstMediaTimeProperty = NULL;
+    m_pElstDurationProperty = NULL;
+    m_pElstRateProperty = NULL;
+    m_pElstReservedProperty = NULL;
+
+    MP4Atom* pElstAtom =
+        m_trakAtom.FindAtom("trak.edts.elst");
+
+    if (!pElstAtom) {
+        return false;
+    }
+
+    (void)pElstAtom->FindProperty(
+        "elst.entryCount",
+        (MP4Property**)&m_pElstCountProperty);
+    (void)pElstAtom->FindProperty(
+        "elst.entries.mediaTime",
+        (MP4Property**)&m_pElstMediaTimeProperty);
+    (void)pElstAtom->FindProperty(
+        "elst.entries.segmentDuration",
+        (MP4Property**)&m_pElstDurationProperty);
+    (void)pElstAtom->FindProperty(
+        "elst.entries.mediaRate",
+        (MP4Property**)&m_pElstRateProperty);
+
+    (void)pElstAtom->FindProperty(
+        "elst.entries.reserved",
+        (MP4Property**)&m_pElstReservedProperty);
+
+    return m_pElstCountProperty
+           && m_pElstMediaTimeProperty
+           && m_pElstDurationProperty
+           && m_pElstRateProperty
+           && m_pElstReservedProperty;
+}
+
+MP4EditId MP4Track::AddEdit(MP4EditId editId)
+{
+    if (!m_pElstCountProperty) {
+        (void)m_File.AddDescendantAtoms(&m_trakAtom, "edts.elst");
+        if (InitEditListProperties() == false) return MP4_INVALID_EDIT_ID;
+    }
+
+    if (editId == MP4_INVALID_EDIT_ID) {
+        editId = m_pElstCountProperty->GetValue() + 1;
+    }
+
+    m_pElstMediaTimeProperty->InsertValue(0, editId - 1);
+    m_pElstDurationProperty->InsertValue(0, editId - 1);
+    m_pElstRateProperty->InsertValue(1, editId - 1);
+    m_pElstReservedProperty->InsertValue(0, editId - 1);
+
+    m_pElstCountProperty->IncrementValue();
+
+    return editId;
+}
+
+void MP4Track::DeleteEdit(MP4EditId editId)
+{
+    if (editId == MP4_INVALID_EDIT_ID) {
+        throw new Exception("edit id can't be zero",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    if (!m_pElstCountProperty
+            || m_pElstCountProperty->GetValue() == 0) {
+        throw new Exception("no edits exist",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    m_pElstMediaTimeProperty->DeleteValue(editId - 1);
+    m_pElstDurationProperty->DeleteValue(editId - 1);
+    m_pElstRateProperty->DeleteValue(editId - 1);
+    m_pElstReservedProperty->DeleteValue(editId - 1);
+
+    m_pElstCountProperty->IncrementValue(-1);
+
+    // clean up if last edit is deleted
+    if (m_pElstCountProperty->GetValue() == 0) {
+        m_pElstCountProperty = NULL;
+        m_pElstMediaTimeProperty = NULL;
+        m_pElstDurationProperty = NULL;
+        m_pElstRateProperty = NULL;
+        m_pElstReservedProperty = NULL;
+
+        m_trakAtom.DeleteChildAtom(
+            m_trakAtom.FindAtom("trak.edts"));
+    }
+}
+
+MP4Timestamp MP4Track::GetEditStart(
+    MP4EditId editId)
+{
+    if (editId == MP4_INVALID_EDIT_ID) {
+        return MP4_INVALID_TIMESTAMP;
+    } else if (editId == 1) {
+        return 0;
+    }
+    return (MP4Timestamp)GetEditTotalDuration(editId - 1);
+}
+
+MP4Duration MP4Track::GetEditTotalDuration(
+    MP4EditId editId)
+{
+    uint32_t numEdits = 0;
+
+    if (m_pElstCountProperty) {
+        numEdits = m_pElstCountProperty->GetValue();
+    }
+
+    if (editId == MP4_INVALID_EDIT_ID) {
+        editId = numEdits;
+    }
+
+    if (numEdits == 0 || editId > numEdits) {
+        return MP4_INVALID_DURATION;
+    }
+
+    MP4Duration totalDuration = 0;
+
+    for (MP4EditId eid = 1; eid <= editId; eid++) {
+        totalDuration +=
+            m_pElstDurationProperty->GetValue(eid - 1);
+    }
+
+    return totalDuration;
+}
+
+MP4SampleId MP4Track::GetSampleIdFromEditTime(
+    MP4Timestamp editWhen,
+    MP4Timestamp* pStartTime,
+    MP4Duration* pDuration)
+{
+    MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID;
+    uint32_t numEdits = 0;
+
+    if (m_pElstCountProperty) {
+        numEdits = m_pElstCountProperty->GetValue();
+    }
+
+    if (numEdits) {
+        MP4Duration editElapsedDuration = 0;
+
+        for (MP4EditId editId = 1; editId <= numEdits; editId++) {
+            // remember edit segment's start time (in edit timeline)
+            MP4Timestamp editStartTime =
+                (MP4Timestamp)editElapsedDuration;
+
+            // accumulate edit segment's duration
+            editElapsedDuration +=
+                m_pElstDurationProperty->GetValue(editId - 1);
+
+            // calculate difference between the specified edit time
+            // and the end of this edit segment
+            if (editElapsedDuration - editWhen <= 0) {
+                // the specified time has not yet been reached
+                continue;
+            }
+
+            // 'editWhen' is within this edit segment
+
+            // calculate the specified edit time
+            // relative to just this edit segment
+            MP4Duration editOffset =
+                editWhen - editStartTime;
+
+            // calculate the media (track) time that corresponds
+            // to the specified edit time based on the edit list
+            MP4Timestamp mediaWhen =
+                m_pElstMediaTimeProperty->GetValue(editId - 1)
+                + editOffset;
+
+            // lookup the sample id for the media time
+            sampleId = GetSampleIdFromTime(mediaWhen, false);
+
+            // lookup the sample's media start time and duration
+            MP4Timestamp sampleStartTime;
+            MP4Duration sampleDuration;
+
+            GetSampleTimes(sampleId, &sampleStartTime, &sampleDuration);
+
+            // calculate the difference if any between when the sample
+            // would naturally start and when it starts in the edit timeline
+            MP4Duration sampleStartOffset =
+                mediaWhen - sampleStartTime;
+
+            // calculate the start time for the sample in the edit time line
+            MP4Timestamp editSampleStartTime =
+                editWhen - min(editOffset, sampleStartOffset);
+
+            MP4Duration editSampleDuration = 0;
+
+            // calculate how long this sample lasts in the edit list timeline
+            if (m_pElstRateProperty->GetValue(editId - 1) == 0) {
+                // edit segment is a "dwell"
+                // so sample duration is that of the edit segment
+                editSampleDuration =
+                    m_pElstDurationProperty->GetValue(editId - 1);
+
+            } else {
+                // begin with the natural sample duration
+                editSampleDuration = sampleDuration;
+
+                // now shorten that if the edit segment starts
+                // after the sample would naturally start
+                if (editOffset < sampleStartOffset) {
+                    editSampleDuration -= sampleStartOffset - editOffset;
+                }
+
+                // now shorten that if the edit segment ends
+                // before the sample would naturally end
+                if (editElapsedDuration
+                        < editSampleStartTime + sampleDuration) {
+                    editSampleDuration -= (editSampleStartTime + sampleDuration)
+                                          - editElapsedDuration;
+                }
+            }
+
+            if (pStartTime) {
+                *pStartTime = editSampleStartTime;
+            }
+
+            if (pDuration) {
+                *pDuration = editSampleDuration;
+            }
+
+            log.verbose2f("\"%s\": GetSampleIdFromEditTime: when %" PRIu64 " "
+                          "sampleId %u start %" PRIu64 " duration %" PRId64,
+                          GetFile().GetFilename().c_str(),
+                          editWhen, sampleId,
+                          editSampleStartTime, editSampleDuration);
+
+            return sampleId;
+        }
+
+        throw new Exception("time out of range",
+                            __FILE__, __LINE__, __FUNCTION__ );
+
+    } else { // no edit list
+        sampleId = GetSampleIdFromTime(editWhen, false);
+
+        if (pStartTime || pDuration) {
+            GetSampleTimes(sampleId, pStartTime, pDuration);
+        }
+    }
+
+    return sampleId;
+}
+
+void MP4Track::CalculateBytesPerSample ()
+{
+    MP4Atom *pMedia = m_trakAtom.FindAtom("trak.mdia.minf.stbl.stsd");
+    MP4Atom *pMediaData;
+    const char *media_data_name;
+    if (pMedia == NULL) return;
+
+    if (pMedia->GetNumberOfChildAtoms() != 1) return;
+
+    pMediaData = pMedia->GetChildAtom(0);
+    media_data_name = pMediaData->GetType();
+    if ((ATOMID(media_data_name) == ATOMID("twos")) ||
+            (ATOMID(media_data_name) == ATOMID("sowt"))) {
+        MP4IntegerProperty *chan, *sampleSize;
+        chan = (MP4IntegerProperty *)pMediaData->GetProperty(4);
+        sampleSize = (MP4IntegerProperty *)pMediaData->GetProperty(5);
+        m_bytesPerSample = chan->GetValue() * (sampleSize->GetValue() / 8);
+    }
+}
+
+MP4Duration MP4Track::GetDurationPerChunk()
+{
+    return m_durationPerChunk;
+}
+
+void MP4Track::SetDurationPerChunk( MP4Duration duration )
+{
+    m_durationPerChunk = duration;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/mp4track.h b/src/mp4track.h
new file mode 100644
index 0000000..2a649f2
--- /dev/null
+++ b/src/mp4track.h
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ *              Ximpo Group Ltd.        mp4v2@ximpo.com
+ */
+
+#ifndef MP4V2_IMPL_MP4TRACK_H
+#define MP4V2_IMPL_MP4TRACK_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef uint32_t MP4ChunkId;
+
+// forward declarations
+class MP4File;
+class MP4Atom;
+class MP4Property;
+class MP4IntegerProperty;
+class MP4Integer8Property;
+class MP4Integer16Property;
+class MP4Integer32Property;
+class MP4Integer64Property;
+class MP4StringProperty;
+
+class MP4Track
+{
+public:
+    MP4Track(MP4File& file, MP4Atom& trakAtom);
+
+    virtual ~MP4Track();
+
+    MP4TrackId GetId() {
+        return m_trackId;
+    }
+
+    const char* GetType();
+
+    void SetType(const char* type);
+
+    MP4File& GetFile() {
+        return m_File;
+    }
+
+    MP4Atom& GetTrakAtom() {
+        return m_trakAtom;
+    }
+
+    void ReadSample(
+        // input parameters
+        MP4SampleId sampleId,
+        // output parameters
+        uint8_t**     ppBytes,
+        uint32_t*     pNumBytes,
+        MP4Timestamp* pStartTime = NULL,
+        MP4Duration*  pDuration = NULL,
+        MP4Duration*  pRenderingOffset = NULL,
+        bool*         pIsSyncSample = NULL,
+        bool*         hasDependencyFlags = NULL,
+        uint32_t*     dependencyFlags = NULL );
+
+    void WriteSample(
+        const uint8_t* pBytes,
+        uint32_t numBytes,
+        MP4Duration duration = 0,
+        MP4Duration renderingOffset = 0,
+        bool isSyncSample = true);
+
+    void WriteSampleDependency(
+        const uint8_t* pBytes,
+        uint32_t       numBytes,
+        MP4Duration    duration,
+        MP4Duration    renderingOffset,
+        bool           isSyncSample,
+        uint32_t       dependencyFlags );
+
+    virtual void FinishWrite(uint32_t options = 0);
+
+    uint64_t    GetDuration();      // in track timeScale units
+    uint32_t    GetTimeScale();
+    uint32_t    GetNumberOfSamples();
+    uint32_t    GetSampleSize(MP4SampleId sampleId);
+    uint32_t    GetMaxSampleSize();
+    uint64_t    GetTotalOfSampleSizes();
+    uint32_t    GetAvgBitrate();    // in bps
+    uint32_t    GetMaxBitrate();    // in bps
+
+    MP4Duration GetFixedSampleDuration();
+    void        SetFixedSampleDuration(MP4Duration duration);
+
+    void        GetSampleTimes(MP4SampleId sampleId,
+                               MP4Timestamp* pStartTime, MP4Duration* pDuration);
+
+    bool        IsSyncSample(MP4SampleId sampleId);
+
+    MP4SampleId GetSampleIdFromTime(
+        MP4Timestamp when,
+        bool wantSyncSample = false);
+
+    MP4Duration GetSampleRenderingOffset(MP4SampleId sampleId);
+    void        SetSampleRenderingOffset(MP4SampleId sampleId,
+                                         MP4Duration renderingOffset);
+
+    MP4EditId   AddEdit(
+        MP4EditId editId = MP4_INVALID_EDIT_ID);
+
+    void        DeleteEdit(
+        MP4EditId editId);
+
+    MP4Timestamp GetEditStart(
+        MP4EditId editId);
+
+    MP4Timestamp GetEditTotalDuration(
+        MP4EditId editId);
+
+    MP4SampleId GetSampleIdFromEditTime(
+        MP4Timestamp editWhen,
+        MP4Timestamp* pStartTime = NULL,
+        MP4Duration* pDuration = NULL);
+
+    // special operation for use during hint track packet assembly
+    void ReadSampleFragment(
+        MP4SampleId sampleId,
+        uint32_t sampleOffset,
+        uint16_t sampleLength,
+        uint8_t* pDest);
+
+    // special operations for use during optimization
+
+    uint32_t GetNumberOfChunks();
+
+    MP4Timestamp GetChunkTime(MP4ChunkId chunkId);
+
+    void ReadChunk(MP4ChunkId chunkId,
+                   uint8_t** ppChunk, uint32_t* pChunkSize);
+
+    void RewriteChunk(MP4ChunkId chunkId,
+                      uint8_t* pChunk, uint32_t chunkSize);
+
+    MP4Duration GetDurationPerChunk();
+    void        SetDurationPerChunk( MP4Duration );
+
+protected:
+    bool        InitEditListProperties();
+
+    File*       GetSampleFile( MP4SampleId sampleId );
+    uint64_t    GetSampleFileOffset(MP4SampleId sampleId);
+    uint32_t    GetSampleStscIndex(MP4SampleId sampleId);
+    uint32_t    GetChunkStscIndex(MP4ChunkId chunkId);
+    uint32_t    GetChunkSize(MP4ChunkId chunkId);
+    uint32_t    GetSampleCttsIndex(MP4SampleId sampleId,
+                                   MP4SampleId* pFirstSampleId = NULL);
+    MP4SampleId GetNextSyncSample(MP4SampleId sampleId);
+
+    void UpdateSampleSizes(MP4SampleId sampleId,
+                           uint32_t numBytes);
+    bool IsChunkFull(MP4SampleId sampleId);
+    void UpdateSampleToChunk(MP4SampleId sampleId,
+                             MP4ChunkId chunkId, uint32_t samplesPerChunk);
+    void UpdateChunkOffsets(uint64_t chunkOffset);
+    void UpdateSampleTimes(MP4Duration duration);
+    void UpdateRenderingOffsets(MP4SampleId sampleId,
+                                MP4Duration renderingOffset);
+    void UpdateSyncSamples(MP4SampleId sampleId,
+                           bool isSyncSample);
+
+    MP4Atom* AddAtom(const char* parentName, const char* childName);
+
+    void UpdateDurations(MP4Duration duration);
+    MP4Duration ToMovieDuration(MP4Duration trackDuration);
+
+    void UpdateModificationTimes();
+
+    void WriteChunkBuffer();
+
+    void CalculateBytesPerSample();
+
+    void FinishSdtp();
+
+protected:
+    MP4File&    m_File;
+    MP4Atom&    m_trakAtom;         // moov.trak[]
+    MP4TrackId  m_trackId;          // moov.trak[].tkhd.trackId
+    MP4StringProperty* m_pTypeProperty; // moov.trak[].mdia.hdlr.handlerType
+
+    uint32_t m_lastStsdIndex;
+    File*    m_lastSampleFile;
+
+    // for efficient construction of hint track packets
+    MP4SampleId m_cachedReadSampleId;
+    uint8_t*    m_pCachedReadSample;
+    uint32_t    m_cachedReadSampleSize;
+
+    // for writing
+    MP4SampleId m_writeSampleId;
+    MP4Duration m_fixedSampleDuration;
+    uint8_t*    m_pChunkBuffer;
+    uint32_t    m_chunkBufferSize;          // Actual size of our chunk buffer.
+    uint32_t    m_sizeOfDataInChunkBuffer;  // Size of the data in the chunk buffer.
+    uint32_t    m_chunkSamples;
+    MP4Duration m_chunkDuration;
+
+    // controls for chunking
+    uint32_t    m_samplesPerChunk;
+    MP4Duration m_durationPerChunk;
+
+    uint32_t       m_bytesPerSample;
+
+    // controls for AMR chunking
+    int     m_isAmr;
+    uint8_t m_curMode;
+
+    MP4Integer32Property* m_pTimeScaleProperty;
+    MP4IntegerProperty* m_pTrackDurationProperty;       // 32 or 64 bits
+    MP4IntegerProperty* m_pMediaDurationProperty;       // 32 or 64 bits
+    MP4IntegerProperty* m_pTrackModificationProperty;   // 32 or 64 bits
+    MP4IntegerProperty* m_pMediaModificationProperty;   // 32 or 64 bits
+
+    MP4Integer32Property* m_pStszFixedSampleSizeProperty;
+    MP4Integer32Property* m_pStszSampleCountProperty;
+
+    void SampleSizePropertyAddValue(uint32_t bytes);
+    uint8_t m_stsz_sample_bits;
+    bool m_have_stz2_4bit_sample;
+    uint8_t m_stz2_4bit_sample_value;
+    MP4IntegerProperty* m_pStszSampleSizeProperty;
+
+    MP4Integer32Property* m_pStscCountProperty;
+    MP4Integer32Property* m_pStscFirstChunkProperty;
+    MP4Integer32Property* m_pStscSamplesPerChunkProperty;
+    MP4Integer32Property* m_pStscSampleDescrIndexProperty;
+    MP4Integer32Property* m_pStscFirstSampleProperty;
+
+    MP4Integer32Property* m_pChunkCountProperty;
+    MP4IntegerProperty*   m_pChunkOffsetProperty;       // 32 or 64 bits
+
+    MP4Integer32Property* m_pSttsCountProperty;
+    MP4Integer32Property* m_pSttsSampleCountProperty;
+    MP4Integer32Property* m_pSttsSampleDeltaProperty;
+
+    // for improve sequental timestamp index access
+    uint32_t    m_cachedSttsIndex;
+    MP4SampleId m_cachedSttsSid;
+    MP4Timestamp    m_cachedSttsElapsed;
+
+    uint32_t    m_cachedCttsIndex;
+    MP4SampleId m_cachedCttsSid;
+
+    MP4Integer32Property* m_pCttsCountProperty;
+    MP4Integer32Property* m_pCttsSampleCountProperty;
+    MP4Integer32Property* m_pCttsSampleOffsetProperty;
+
+    MP4Integer32Property* m_pStssCountProperty;
+    MP4Integer32Property* m_pStssSampleProperty;
+
+    MP4Integer32Property* m_pElstCountProperty;
+    MP4IntegerProperty*   m_pElstMediaTimeProperty;     // 32 or 64 bits
+    MP4IntegerProperty*   m_pElstDurationProperty;      // 32 or 64 bits
+    MP4Integer16Property* m_pElstRateProperty;
+    MP4Integer16Property* m_pElstReservedProperty;
+
+    string m_sdtpLog; // records frame types for H264 samples
+};
+
+MP4ARRAY_DECL(MP4Track, MP4Track*);
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4TRACK_H
diff --git a/src/mp4util.cpp b/src/mp4util.cpp
new file mode 100644
index 0000000..47bd74e
--- /dev/null
+++ b/src/mp4util.cpp
@@ -0,0 +1,352 @@
+/*
+ * 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-2005.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ *              Bill May                wmay@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool MP4NameFirstMatches(const char* s1, const char* s2)
+{
+    if (s1 == NULL || *s1 == '\0' || s2 == NULL || *s2 == '\0') {
+        return false;
+    }
+
+    if (*s2 == '*') {
+        return true;
+    }
+
+    while (*s1 != '\0') {
+        if (*s2 == '\0' || strchr("[.", *s2)) {
+            break;
+        }
+        if (tolower(*s1) != tolower(*s2)) {
+            return false;
+        }
+        s1++;
+        s2++;
+    }
+    return true;
+}
+
+bool MP4NameFirstIndex(const char* s, uint32_t* pIndex)
+{
+    if (s == NULL) {
+        return false;
+    }
+
+    while (*s != '\0' && *s != '.') {
+        if (*s == '[') {
+            s++;
+            ASSERT(pIndex);
+            if (sscanf(s, "%u", pIndex) != 1) {
+                return false;
+            }
+            return true;
+        }
+        s++;
+    }
+    return false;
+}
+
+char* MP4NameFirst(const char *s)
+{
+    if (s == NULL) {
+        return NULL;
+    }
+
+    const char* end = s;
+
+    while (*end != '\0' && *end != '.') {
+        end++;
+    }
+
+    char* first = (char*)MP4Calloc((end - s) + 1);
+
+    if (first) {
+        strncpy(first, s, end - s);
+    }
+
+    return first;
+}
+
+const char* MP4NameAfterFirst(const char *s)
+{
+    if (s == NULL) {
+        return NULL;
+    }
+
+    while (*s != '\0') {
+        if (*s == '.') {
+            s++;
+            if (*s == '\0') {
+                return NULL;
+            }
+            return s;
+        }
+        s++;
+    }
+    return NULL;
+}
+
+char* MP4ToBase16(const uint8_t* pData, uint32_t dataSize)
+{
+    if (dataSize) {
+        ASSERT(pData);
+    }
+    uint32_t size = 2 * dataSize + 1;
+    char* s = (char*)MP4Calloc(size);
+
+    uint32_t i, j;
+    for (i = 0, j = 0; i < dataSize; i++) {
+        size -= snprintf(&s[j], size, "%02x", pData[i]);
+        j += 2;
+    }
+
+    return s;   /* N.B. caller is responsible for free'ing s */
+}
+
+char* MP4ToBase64(const uint8_t* pData, uint32_t dataSize)
+{
+    if (pData == NULL || dataSize == 0) return NULL;
+
+    static const char encoding[64] = {
+        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+        'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+        'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+        'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+    };
+
+    char* s = (char*)MP4Calloc((((dataSize + 2) * 4) / 3) + 1);
+
+    const uint8_t* src = pData;
+    if (pData == NULL) return NULL;
+    char* dest = s;
+    uint32_t numGroups = dataSize / 3;
+
+    for (uint32_t i = 0; i < numGroups; i++) {
+        *dest++ = encoding[src[0] >> 2];
+        *dest++ = encoding[((src[0] & 0x03) << 4) | (src[1] >> 4)];
+        *dest++ = encoding[((src[1] & 0x0F) << 2) | (src[2] >> 6)];
+        *dest++ = encoding[src[2] & 0x3F];
+        src += 3;
+    }
+
+    if (dataSize % 3 == 1) {
+        *dest++ = encoding[src[0] >> 2];
+        *dest++ = encoding[((src[0] & 0x03) << 4)];
+        *dest++ = '=';
+        *dest++ = '=';
+    } else if (dataSize % 3 == 2) {
+        *dest++ = encoding[src[0] >> 2];
+        *dest++ = encoding[((src[0] & 0x03) << 4) | (src[1] >> 4)];
+        *dest++ = encoding[((src[1] & 0x0F) << 2)];
+        *dest++ = '=';
+    }
+    *dest = '\0';
+    return s;   /* N.B. caller is responsible for free'ing s */
+}
+
+static bool convertBase64 (const char data, uint8_t *value)
+{
+    static const uint8_t decodingarr64[128] = {
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+        0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+        0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+        0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+        0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+        0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+        0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+        0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+        0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+        0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff,
+    };
+    uint8_t index = (uint8_t)data;
+    if ((index & 0x80) != 0) return false;
+
+    if (decodingarr64[index] == 0xff) return false;
+    *value = decodingarr64[index];
+    return true;
+}
+
+uint8_t *Base64ToBinary (const char *pData, uint32_t decodeSize, uint32_t *pDataSize)
+{
+    uint8_t *ret;
+    uint32_t size, ix, groups;
+    if (pData == NULL ||  decodeSize == 0 || pDataSize == NULL)
+        return NULL;
+
+    if ((decodeSize % 4) != 0) {
+        // must be multiples of 4 characters
+        return NULL;
+    }
+    size = (decodeSize * 3) / 4;
+    groups = decodeSize / 4;
+    ret = (uint8_t *)MP4Calloc(size);
+    if (ret == NULL) return NULL;
+    for (ix = 0; ix < groups; ix++) {
+        uint8_t value[4];
+        for (uint8_t jx = 0; jx < 4; jx++) {
+            if (pData[jx] == '=') {
+                if (ix != (groups - 1)) {
+                    free(ret);
+                    return NULL;
+                }
+                size--;
+                value[jx] = 0;
+            } else if (convertBase64(pData[jx], &value[jx]) == false) {
+                free(ret);
+                return NULL;
+            }
+        }
+        ret[(ix * 3)] = value[0] << 2 | ((value[1] >> 4) & 0x3);
+        ret[(ix * 3) + 1] = (value[1] << 4) | (value[2] >> 2 & 0xf);
+        ret[(ix * 3) + 2] = ((value[2] & 0x3) << 6) | value[3];
+        pData += 4;
+    }
+    *pDataSize = size;
+    return ret;
+}
+
+// log2 of value, rounded up
+static uint8_t ilog2(uint64_t value)
+{
+    uint64_t powerOf2 = 1;
+    for (uint8_t i = 0; i < 64; i++) {
+        if (value <= powerOf2) {
+            return i;
+        }
+        powerOf2 <<= 1;
+    }
+    return 64;
+}
+
+uint64_t MP4ConvertTime(uint64_t t,
+                        uint32_t oldTimeScale, uint32_t newTimeScale)
+{
+    // avoid float point exception
+    if (oldTimeScale == 0) {
+        throw new Exception("division by zero", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    if (oldTimeScale == newTimeScale) return t;
+
+    // check if we can safely use integer operations
+    if (ilog2(t) + ilog2(newTimeScale) <= 64) {
+        return (t * newTimeScale) / oldTimeScale;
+    }
+
+    // final resort is to use floating point
+    double d = (double)newTimeScale;
+    d *= double(t);
+    d /= (double)oldTimeScale;
+    d += 0.5; // round up.
+
+    return (uint64_t)d;
+}
+
+const char* MP4NormalizeTrackType (const char* type)
+{
+    if (!strcasecmp(type, "vide")
+            || !strcasecmp(type, "video")
+            || !strcasecmp(type, "mp4v")
+            || !strcasecmp(type, "avc1")
+            || !strcasecmp(type, "s263")  // 3GPP H.263
+            || !strcasecmp(type, "encv")) {
+        return MP4_VIDEO_TRACK_TYPE;
+    }
+
+    if (!strcasecmp(type, "soun")
+            || !strcasecmp(type, "sound")
+            || !strcasecmp(type, "audio")
+            || !strcasecmp(type, "enca")
+            || !strcasecmp(type, "samr")  // 3GPP AMR
+            || !strcasecmp(type, "sawb")  // 3GPP AMR/WB
+            || !strcasecmp(type, "mp4a")) {
+        return MP4_AUDIO_TRACK_TYPE;
+    }
+
+    if (!strcasecmp(type, "sdsm")
+            || !strcasecmp(type, "scene")
+            || !strcasecmp(type, "bifs")) {
+        return MP4_SCENE_TRACK_TYPE;
+    }
+
+    if (!strcasecmp(type, "odsm")
+            || !strcasecmp(type, "od")) {
+        return MP4_OD_TRACK_TYPE;
+    }
+    if (strcasecmp(type, "cntl") == 0) {
+        return MP4_CNTL_TRACK_TYPE;
+    }
+
+    log.verbose1f("Attempt to normalize %s did not match",type);
+
+    return type;
+}
+
+MP4Timestamp MP4GetAbsTimestamp() {
+    /* MP4 epoch is midnight, January 1, 1904
+     * offset from midnight, January 1, 1970 is 2082844800 seconds
+     * 208284480 is (((1970 - 1904) * 365) + 17) * 24 * 60 * 60
+     */
+    return time::getLocalTimeSeconds() + 2082844800;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t STRTOINT32( const char* s )
+{
+#if defined( MP4V2_INTSTRING_ALIGNMENT )
+    // it seems ARM integer instructions require 4-byte alignment so we
+    // manually copy string-data into the integer before performing ops
+    uint32_t tmp;
+    memcpy( &tmp, s, sizeof(tmp) );
+    return MP4V2_NTOHL( tmp );
+#else
+    return MP4V2_NTOHL(*(uint32_t *)s);
+#endif
+}
+
+void INT32TOSTR( uint32_t i, char* s )
+{
+#if defined( MP4V2_INTSTRING_ALIGNMENT )
+    // it seems ARM integer instructions require 4-byte alignment so we
+    // manually copy string-data into the integer before performing ops
+    uint32_t tmp = MP4V2_HTONL( i );
+    memcpy( s, &tmp, sizeof(tmp) );
+#else
+    *(uint32_t *)s = MP4V2_HTONL(i);
+#endif
+    s[4] = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/mp4util.h b/src/mp4util.h
new file mode 100644
index 0000000..1fbbd81
--- /dev/null
+++ b/src/mp4util.h
@@ -0,0 +1,113 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_MP4UTIL_H
+#define MP4V2_IMPL_MP4UTIL_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <assert.h>
+
+#define LIBMPV42_STRINGIFY(x)  #x
+
+#ifndef ASSERT
+#   define ASSERT(expr) \
+        if (!(expr)) { \
+            throw new Exception("assert failure: "LIBMPV42_STRINGIFY((expr)), __FILE__, __LINE__, __FUNCTION__ ); \
+        }
+#endif
+
+#define WARNING(expr) \
+    if (expr) { \
+        log.errorf("Warning (%s) in %s at line %u", \
+                         LIBMPV42_STRINGIFY(expr), __FILE__, __LINE__); \
+    }
+
+///////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_AND_FREE(a) if ((a) != NULL) { free((void *)(a)); (a) = NULL;}
+
+#define NUM_ELEMENTS_IN_ARRAY(name) ((sizeof((name))) / (sizeof(*(name))))
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline void* MP4Malloc(size_t size) {
+    if (size == 0) return NULL;
+    void* p = malloc(size);
+    if (p == NULL && size > 0) {
+        throw new PlatformException("malloc failed",errno,__FILE__,__LINE__,__FUNCTION__);
+    }
+    return p;
+}
+
+inline void* MP4Calloc(size_t size) {
+    if (size == 0) return NULL;
+    return memset(MP4Malloc(size), 0, size);
+}
+
+inline char* MP4Stralloc(const char* s1) {
+    char* s2 = (char*)MP4Malloc(strlen(s1) + 1);
+    strcpy(s2, s1);
+    return s2;
+}
+
+inline void* MP4Realloc(void* p, uint32_t newSize) {
+    // workaround library bug
+    if (p == NULL && newSize == 0) {
+        return NULL;
+    }
+
+    void* temp = realloc(p, newSize);
+    if (temp == NULL && newSize > 0) {
+        throw new PlatformException("malloc failed",errno,__FILE__,__LINE__,__FUNCTION__);
+    }
+    return temp;
+}
+
+uint32_t STRTOINT32( const char* );
+void     INT32TOSTR( uint32_t, char* );
+
+MP4Timestamp MP4GetAbsTimestamp();
+
+uint64_t MP4ConvertTime(uint64_t t,
+                        uint32_t oldTimeScale, uint32_t newTimeScale);
+
+bool MP4NameFirstMatches(const char* s1, const char* s2);
+
+bool MP4NameFirstIndex(const char* s, uint32_t* pIndex);
+
+char* MP4NameFirst(const char *s);
+
+const char* MP4NameAfterFirst(const char *s);
+
+char* MP4ToBase16(const uint8_t* pData, uint32_t dataSize);
+
+char* MP4ToBase64(const uint8_t* pData, uint32_t dataSize);
+
+const char* MP4NormalizeTrackType(const char* type);
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_MP4UTIL_H
diff --git a/src/ocidescriptors.cpp b/src/ocidescriptors.cpp
new file mode 100644
index 0000000..0f78850
--- /dev/null
+++ b/src/ocidescriptors.cpp
@@ -0,0 +1,320 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ContentClassDescriptor::MP4ContentClassDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "classificationEntity"));
+    AddProperty( /* 1 */
+        new MP4Integer16Property(parentAtom, "classificationTable"));
+    AddProperty( /* 2 */
+        new MP4BytesProperty(parentAtom, "contentClassificationData"));
+}
+
+void MP4ContentClassDescriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    /* byte properties need to know how long they are before reading */
+    ((MP4BytesProperty*)m_pProperties[2])->SetValueSize(m_size - 6);
+
+    ReadProperties(file);
+}
+
+MP4KeywordDescriptor::MP4KeywordDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4BytesProperty(parentAtom, "languageCode", 3));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "isUTF8String", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 7));
+    MP4Integer8Property* pCount =
+        new MP4Integer8Property(parentAtom, "keywordCount");
+    AddProperty(pCount); /* 3 */
+
+    MP4TableProperty* pTable = new MP4TableProperty(parentAtom, "keywords", pCount);
+    AddProperty(pTable); /* 4 */
+
+    pTable->AddProperty( /* 4, 0 */
+        new MP4StringProperty(pTable->GetParentAtom(), "string", Counted));
+
+    SetReadMutate(2);
+}
+
+void MP4KeywordDescriptor::Mutate()
+{
+    bool utf8Flag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+    MP4Property* pProperty =
+        ((MP4TableProperty*)m_pProperties[4])->GetProperty(0);
+    ASSERT(pProperty);
+    ((MP4StringProperty*)pProperty)->SetUnicode(!utf8Flag);
+}
+
+MP4RatingDescriptor::MP4RatingDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "ratingEntity"));
+    AddProperty( /* 1 */
+        new MP4Integer16Property(parentAtom, "ratingCriteria"));
+    AddProperty( /* 2 */
+        new MP4BytesProperty(parentAtom, "ratingInfo"));
+}
+
+void MP4RatingDescriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    /* byte properties need to know how long they are before reading */
+    ((MP4BytesProperty*)m_pProperties[2])->SetValueSize(m_size - 6);
+
+    ReadProperties(file);
+}
+
+MP4LanguageDescriptor::MP4LanguageDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4BytesProperty(parentAtom, "languageCode", 3));
+}
+
+MP4ShortTextDescriptor::MP4ShortTextDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4BytesProperty(parentAtom, "languageCode", 3));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "isUTF8String", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 7));
+    AddProperty( /* 3 */
+        new MP4StringProperty(parentAtom, "eventName", Counted));
+    AddProperty( /* 4 */
+        new MP4StringProperty(parentAtom, "eventText", Counted));
+
+    SetReadMutate(2);
+}
+
+void MP4ShortTextDescriptor::Mutate()
+{
+    bool utf8Flag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+    ((MP4StringProperty*)m_pProperties[3])->SetUnicode(!utf8Flag);
+    ((MP4StringProperty*)m_pProperties[4])->SetUnicode(!utf8Flag);
+}
+
+MP4ExpandedTextDescriptor::MP4ExpandedTextDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4BytesProperty(parentAtom, "languageCode", 3));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "isUTF8String", 1));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(parentAtom, "reserved", 7));
+    MP4Integer8Property* pCount =
+        new MP4Integer8Property(parentAtom, "itemCount");
+    AddProperty(pCount); /* 3 */
+
+    MP4TableProperty* pTable = new MP4TableProperty(parentAtom, "items", pCount);
+    AddProperty(pTable); /* 4 */
+
+    pTable->AddProperty( /* Table 0 */
+        new MP4StringProperty(pTable->GetParentAtom(), "itemDescription", Counted));
+    pTable->AddProperty( /* Table 1 */
+        new MP4StringProperty(pTable->GetParentAtom(), "itemText", Counted));
+
+    AddProperty( /* 5 */
+        new MP4StringProperty(parentAtom, "nonItemText"));
+    ((MP4StringProperty*)m_pProperties[5])->SetExpandedCountedFormat(true);
+
+    SetReadMutate(2);
+}
+
+void MP4ExpandedTextDescriptor::Mutate()
+{
+    bool utf8Flag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue();
+
+    MP4Property* pProperty =
+        ((MP4TableProperty*)m_pProperties[4])->GetProperty(0);
+    ASSERT(pProperty);
+    ((MP4StringProperty*)pProperty)->SetUnicode(!utf8Flag);
+
+    pProperty = ((MP4TableProperty*)m_pProperties[4])->GetProperty(1);
+    ASSERT(pProperty);
+    ((MP4StringProperty*)pProperty)->SetUnicode(!utf8Flag);
+
+    ((MP4StringProperty*)m_pProperties[5])->SetUnicode(!utf8Flag);
+}
+
+class MP4CreatorTableProperty : public MP4TableProperty {
+public:
+    MP4CreatorTableProperty(MP4Atom& parentAtom, const char* name, MP4Integer8Property* pCountProperty) :
+            MP4TableProperty(parentAtom, name, pCountProperty) {
+    };
+protected:
+    void ReadEntry(MP4File& file, uint32_t index);
+    void WriteEntry(MP4File& file, uint32_t index);
+private:
+    MP4CreatorTableProperty();
+    MP4CreatorTableProperty ( const MP4CreatorTableProperty &src );
+    MP4CreatorTableProperty &operator= ( const MP4CreatorTableProperty &src );
+};
+
+MP4CreatorDescriptor::MP4CreatorDescriptor(MP4Atom& parentAtom, uint8_t tag)
+        : MP4Descriptor(parentAtom, tag)
+{
+    MP4Integer8Property* pCount =
+        new MP4Integer8Property(parentAtom, "creatorCount");
+    AddProperty(pCount); /* 0 */
+
+    MP4TableProperty* pTable = new MP4CreatorTableProperty(parentAtom, "creators", pCount);
+    AddProperty(pTable); /* 1 */
+
+    pTable->AddProperty( /* Table 0 */
+        new MP4BytesProperty(pTable->GetParentAtom(), "languageCode", 3, 3));
+    pTable->AddProperty( /* Table 1 */
+        new MP4BitfieldProperty(pTable->GetParentAtom(), "isUTF8String", 1));
+    pTable->AddProperty( /* Table 2 */
+        new MP4BitfieldProperty(pTable->GetParentAtom(), "reserved", 7));
+    pTable->AddProperty( /* Table 3 */
+        new MP4StringProperty(pTable->GetParentAtom(), "name", Counted));
+}
+
+void MP4CreatorTableProperty::ReadEntry(MP4File& file, uint32_t index)
+{
+    m_pProperties[0]->Read(file, index);
+    m_pProperties[1]->Read(file, index);
+
+    bool utf8Flag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue(index);
+    ((MP4StringProperty*)m_pProperties[3])->SetUnicode(!utf8Flag);
+
+    m_pProperties[2]->Read(file, index);
+    m_pProperties[3]->Read(file, index);
+}
+
+void MP4CreatorTableProperty::WriteEntry(MP4File& file, uint32_t index)
+{
+    bool utf8Flag = ((MP4BitfieldProperty*)m_pProperties[1])->GetValue(index);
+    ((MP4StringProperty*)m_pProperties[3])->SetUnicode(!utf8Flag);
+
+    MP4TableProperty::WriteEntry(file, index);
+}
+
+MP4CreationDescriptor::MP4CreationDescriptor(MP4Atom& parentAtom, uint8_t tag)
+        : MP4Descriptor(parentAtom, tag)
+{
+    AddProperty( /* 0 */
+        new MP4BitfieldProperty(parentAtom, "contentCreationDate", 40));
+}
+
+MP4SmpteCameraDescriptor::MP4SmpteCameraDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    MP4Integer8Property* pCount =
+        new MP4Integer8Property(parentAtom, "parameterCount");
+    AddProperty(pCount);
+
+    MP4TableProperty* pTable = new MP4TableProperty(parentAtom, "parameters", pCount);
+    AddProperty(pTable);
+
+    pTable->AddProperty(
+        new MP4Integer8Property(parentAtom, "id"));
+    pTable->AddProperty(
+        new MP4Integer32Property(parentAtom, "value"));
+}
+
+MP4UnknownOCIDescriptor::MP4UnknownOCIDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4BytesProperty(parentAtom, "data"));
+}
+
+void MP4UnknownOCIDescriptor::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    /* byte properties need to know how long they are before reading */
+    ((MP4BytesProperty*)m_pProperties[0])->SetValueSize(m_size);
+
+    ReadProperties(file);
+}
+
+MP4Descriptor* CreateOCIDescriptor(MP4Atom& parentAtom, uint8_t tag)
+{
+    MP4Descriptor* pDescriptor = NULL;
+
+    switch (tag) {
+    case MP4ContentClassDescrTag:
+        pDescriptor = new MP4ContentClassDescriptor(parentAtom);
+        break;
+    case MP4KeywordDescrTag:
+        pDescriptor = new MP4KeywordDescriptor(parentAtom);
+        break;
+    case MP4RatingDescrTag:
+        pDescriptor = new MP4RatingDescriptor(parentAtom);
+        break;
+    case MP4LanguageDescrTag:
+        pDescriptor = new MP4LanguageDescriptor(parentAtom);
+        break;
+    case MP4ShortTextDescrTag:
+        pDescriptor = new MP4ShortTextDescriptor(parentAtom);
+        break;
+    case MP4ExpandedTextDescrTag:
+        pDescriptor = new MP4ExpandedTextDescriptor(parentAtom);
+        break;
+    case MP4ContentCreatorDescrTag:
+    case MP4OCICreatorDescrTag:
+        pDescriptor = new MP4CreatorDescriptor(parentAtom, tag);
+        break;
+    case MP4ContentCreationDescrTag:
+    case MP4OCICreationDescrTag:
+        pDescriptor = new MP4CreationDescriptor(parentAtom, tag);
+        break;
+    case MP4SmpteCameraDescrTag:
+        pDescriptor = new MP4SmpteCameraDescriptor(parentAtom);
+        break;
+    }
+
+    if (pDescriptor == NULL) {
+        if (tag >= MP4OCIDescrTagsStart && tag <= MP4OCIDescrTagsEnd) {
+            pDescriptor = new MP4UnknownOCIDescriptor(parentAtom);
+            pDescriptor->SetTag(tag);
+        }
+    }
+
+    return pDescriptor;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/ocidescriptors.h b/src/ocidescriptors.h
new file mode 100644
index 0000000..c301850
--- /dev/null
+++ b/src/ocidescriptors.h
@@ -0,0 +1,151 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_OCIDESCRIPTORS_H
+#define MP4V2_IMPL_OCIDESCRIPTORS_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint8_t MP4OCIDescrTagsStart          = 0x40;
+const uint8_t MP4ContentClassDescrTag       = 0x40;
+const uint8_t MP4KeywordDescrTag            = 0x41;
+const uint8_t MP4RatingDescrTag             = 0x42;
+const uint8_t MP4LanguageDescrTag           = 0x43;
+const uint8_t MP4ShortTextDescrTag          = 0x44;
+const uint8_t MP4ExpandedTextDescrTag       = 0x45;
+const uint8_t MP4ContentCreatorDescrTag = 0x46;
+const uint8_t MP4ContentCreationDescrTag    = 0x47;
+const uint8_t MP4OCICreatorDescrTag     = 0x48;
+const uint8_t MP4OCICreationDescrTag        = 0x49;
+const uint8_t MP4SmpteCameraDescrTag        = 0x4A;
+const uint8_t MP4OCIDescrTagsEnd            = 0x5F;
+
+class MP4ContentClassDescriptor : public MP4Descriptor {
+public:
+    MP4ContentClassDescriptor(MP4Atom& parentAtom);
+    void Read(MP4File& file);
+private:
+    MP4ContentClassDescriptor();
+    MP4ContentClassDescriptor ( const MP4ContentClassDescriptor &src );
+    MP4ContentClassDescriptor &operator= ( const MP4ContentClassDescriptor &src );
+};
+
+class MP4KeywordDescriptor : public MP4Descriptor {
+public:
+    MP4KeywordDescriptor(MP4Atom& parentAtom);
+protected:
+    void Mutate();
+private:
+    MP4KeywordDescriptor();
+    MP4KeywordDescriptor ( const MP4KeywordDescriptor &src );
+    MP4KeywordDescriptor &operator= ( const MP4KeywordDescriptor &src );
+};
+
+class MP4RatingDescriptor : public MP4Descriptor {
+public:
+    MP4RatingDescriptor(MP4Atom& parentAtom);
+    void Read(MP4File& file);
+private:
+    MP4RatingDescriptor();
+    MP4RatingDescriptor ( const MP4RatingDescriptor &src );
+    MP4RatingDescriptor &operator= ( const MP4RatingDescriptor &src );
+};
+
+class MP4LanguageDescriptor : public MP4Descriptor {
+public:
+    MP4LanguageDescriptor(MP4Atom& parentAtom);
+private:
+    MP4LanguageDescriptor();
+    MP4LanguageDescriptor ( const MP4LanguageDescriptor &src );
+    MP4LanguageDescriptor &operator= ( const MP4LanguageDescriptor &src );
+};
+
+class MP4ShortTextDescriptor : public MP4Descriptor {
+public:
+    MP4ShortTextDescriptor(MP4Atom& parentAtom);
+protected:
+    void Mutate();
+private:
+    MP4ShortTextDescriptor();
+    MP4ShortTextDescriptor ( const MP4ShortTextDescriptor &src );
+    MP4ShortTextDescriptor &operator= ( const MP4ShortTextDescriptor &src );
+};
+
+class MP4ExpandedTextDescriptor : public MP4Descriptor {
+public:
+    MP4ExpandedTextDescriptor(MP4Atom& parentAtom);
+protected:
+    void Mutate();
+private:
+    MP4ExpandedTextDescriptor();
+    MP4ExpandedTextDescriptor ( const MP4ExpandedTextDescriptor &src );
+    MP4ExpandedTextDescriptor &operator= ( const MP4ExpandedTextDescriptor &src );
+};
+
+class MP4CreatorDescriptor : public MP4Descriptor {
+public:
+    MP4CreatorDescriptor(MP4Atom& parentAtom, uint8_t tag);
+private:
+    MP4CreatorDescriptor();
+    MP4CreatorDescriptor ( const MP4CreatorDescriptor &src );
+    MP4CreatorDescriptor &operator= ( const MP4CreatorDescriptor &src );
+};
+
+class MP4CreationDescriptor : public MP4Descriptor {
+public:
+    MP4CreationDescriptor(MP4Atom& parentAtom, uint8_t tag);
+private:
+    MP4CreationDescriptor();
+    MP4CreationDescriptor ( const MP4CreationDescriptor &src );
+    MP4CreationDescriptor &operator= ( const MP4CreationDescriptor &src );
+};
+
+class MP4SmpteCameraDescriptor : public MP4Descriptor {
+public:
+    MP4SmpteCameraDescriptor(MP4Atom& parentAtom);
+private:
+    MP4SmpteCameraDescriptor();
+    MP4SmpteCameraDescriptor ( const MP4SmpteCameraDescriptor &src );
+    MP4SmpteCameraDescriptor &operator= ( const MP4SmpteCameraDescriptor &src );
+};
+
+class MP4UnknownOCIDescriptor : public MP4Descriptor {
+public:
+    MP4UnknownOCIDescriptor(MP4Atom& parentAtom);
+    void Read(MP4File& file);
+private:
+    MP4UnknownOCIDescriptor();
+    MP4UnknownOCIDescriptor ( const MP4UnknownOCIDescriptor &src );
+    MP4UnknownOCIDescriptor &operator= ( const MP4UnknownOCIDescriptor &src );
+};
+
+
+extern MP4Descriptor *CreateOCIDescriptor(MP4Atom& parentAtom, uint8_t tag);
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_OCIDESCRIPTORS_H
diff --git a/src/odcommands.cpp b/src/odcommands.cpp
new file mode 100644
index 0000000..7cf4f74
--- /dev/null
+++ b/src/odcommands.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+MP4ODUpdateDescriptor::MP4ODUpdateDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4ODUpdateODCommandTag)
+{
+    // just a container for ObjectDescriptors
+    AddProperty( /* 0 */
+        new MP4DescriptorProperty(parentAtom, NULL,
+                                  MP4FileODescrTag, 0, Required, Many));
+}
+
+MP4ODRemoveDescriptor::MP4ODRemoveDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4ODRemoveODCommandTag)
+{
+    MP4Integer32Property* pCount =
+        new MP4Integer32Property(parentAtom, "entryCount");
+    pCount->SetImplicit();
+    AddProperty(pCount); /* 0 */
+
+    MP4TableProperty* pTable =
+        new MP4TableProperty(parentAtom, "entries", pCount);
+    AddProperty(pTable); /* 1 */
+
+    pTable->AddProperty( /* 1, 0 */
+        new MP4BitfieldProperty(pTable->GetParentAtom(), "objectDescriptorId", 10));
+}
+
+void MP4ODRemoveDescriptor::Read(MP4File& file)
+{
+    // table entry count computed from descriptor size
+    ((MP4Integer32Property*)m_pProperties[0])->SetReadOnly(false);
+    ((MP4Integer32Property*)m_pProperties[0])->SetValue((m_size * 8) / 10);
+    ((MP4Integer32Property*)m_pProperties[0])->SetReadOnly(true);
+
+    MP4Descriptor::Read(file);
+}
+
+MP4ESUpdateDescriptor::MP4ESUpdateDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4ESUpdateODCommandTag)
+{
+    AddProperty( /* 0 */
+        new MP4BitfieldProperty(parentAtom, "objectDescriptorId", 10));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "pad", 6));
+    AddProperty( /* 2 */
+        new MP4DescriptorProperty(parentAtom, "esIdRefs",
+                                  MP4ESIDRefDescrTag, 0, Required, Many));
+}
+
+// LATER might be able to combine with ESUpdateDescriptor
+MP4ESRemoveDescriptor::MP4ESRemoveDescriptor(MP4Atom& parentAtom)
+        : MP4Descriptor(parentAtom, MP4ESRemoveODCommandTag)
+{
+    AddProperty( /* 0 */
+        new MP4BitfieldProperty(parentAtom, "objectDescriptorId", 10));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(parentAtom, "pad", 6));
+    AddProperty( /* 2 */
+        new MP4DescriptorProperty(parentAtom, "esIdRefs",
+                                  MP4ESIDRefDescrTag, 0, Required, Many));
+}
+
+MP4Descriptor* CreateODCommand(MP4Atom& parentAtom, uint8_t tag)
+{
+    MP4Descriptor* pDescriptor = NULL;
+
+    switch (tag) {
+    case MP4ODUpdateODCommandTag:
+        pDescriptor = new MP4ODUpdateDescriptor(parentAtom);
+        break;
+    case MP4ODRemoveODCommandTag:
+        pDescriptor = new MP4ODRemoveDescriptor(parentAtom);
+        break;
+    case MP4ESUpdateODCommandTag:
+        pDescriptor = new MP4ESUpdateDescriptor(parentAtom);
+        break;
+    case MP4ESRemoveODCommandTag:
+        pDescriptor = new MP4ESRemoveDescriptor(parentAtom);
+        break;
+    }
+    return pDescriptor;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/odcommands.h b/src/odcommands.h
new file mode 100644
index 0000000..08f1197
--- /dev/null
+++ b/src/odcommands.h
@@ -0,0 +1,83 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_ODCOMMANDS_H
+#define MP4V2_IMPL_ODCOMMANDS_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// OD stream command descriptors
+const uint8_t MP4ODUpdateODCommandTag           = 0x01;
+const uint8_t MP4ODRemoveODCommandTag           = 0x02;
+const uint8_t MP4ESUpdateODCommandTag           = 0x03;
+const uint8_t MP4ESRemoveODCommandTag           = 0x04;
+const uint8_t MP4IPMPUpdateODCommandTag     = 0x05;
+const uint8_t MP4IPMPRemoveODCommandTag     = 0x06;
+const uint8_t MP4ESRemoveRefODCommandTag        = 0x07;
+
+class MP4ODUpdateDescriptor : public MP4Descriptor {
+public:
+    MP4ODUpdateDescriptor(MP4Atom& parentAtom);
+private:
+    MP4ODUpdateDescriptor();
+    MP4ODUpdateDescriptor ( const MP4ODUpdateDescriptor &src );
+    MP4ODUpdateDescriptor &operator= ( const MP4ODUpdateDescriptor &src );
+};
+
+class MP4ODRemoveDescriptor : public MP4Descriptor {
+public:
+    MP4ODRemoveDescriptor(MP4Atom& parentAtom);
+    void Read(MP4File& file);
+private:
+    MP4ODRemoveDescriptor();
+    MP4ODRemoveDescriptor ( const MP4ODRemoveDescriptor &src );
+    MP4ODRemoveDescriptor &operator= ( const MP4ODRemoveDescriptor &src );
+};
+
+class MP4ESUpdateDescriptor : public MP4Descriptor {
+public:
+    MP4ESUpdateDescriptor(MP4Atom& parentAtom);
+private:
+    MP4ESUpdateDescriptor();
+    MP4ESUpdateDescriptor ( const MP4ESUpdateDescriptor &src );
+    MP4ESUpdateDescriptor &operator= ( const MP4ESUpdateDescriptor &src );
+};
+
+class MP4ESRemoveDescriptor : public MP4Descriptor {
+public:
+    MP4ESRemoveDescriptor(MP4Atom& parentAtom);
+private:
+    MP4ESRemoveDescriptor();
+    MP4ESRemoveDescriptor ( const MP4ESRemoveDescriptor &src );
+    MP4ESRemoveDescriptor &operator= ( const MP4ESRemoveDescriptor &src );
+};
+
+MP4Descriptor* CreateODCommand(MP4Atom& parentAtom, uint8_t tag);
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_ODCOMMANDS_H
diff --git a/src/qosqualifiers.cpp b/src/qosqualifiers.cpp
new file mode 100644
index 0000000..02d54c1
--- /dev/null
+++ b/src/qosqualifiers.cpp
@@ -0,0 +1,174 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if 1
+MP4QosDescriptorBase::MP4QosDescriptorBase (MP4Atom& parentAtom, uint8_t tag)
+        : MP4Descriptor(parentAtom, tag)
+{
+    switch (tag) {
+    case MP4QosDescrTag:
+        AddProperty( /* 0 */
+            new MP4Integer8Property(parentAtom, "predefined"));
+        AddProperty( /* 1 */
+            new MP4QosQualifierProperty(parentAtom, "qualifiers",
+                                        MP4QosTagsStart,
+                                        MP4QosTagsEnd, Optional, Many));
+        break;
+    case MP4MaxDelayQosTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "maxDelay"));
+        break;
+    case MP4PrefMaxDelayQosTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "prefMaxDelay"));
+        break;
+    case MP4LossProbQosTag:
+        AddProperty( /* 0 */
+            new MP4Float32Property(parentAtom, "lossProb"));
+        break;
+    case MP4MaxGapLossQosTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "maxGapLoss"));
+        break;
+    case MP4MaxAUSizeQosTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "maxAUSize"));
+        break;
+    case MP4AvgAUSizeQosTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "avgAUSize"));
+        break;
+    case MP4MaxAURateQosTag:
+        AddProperty( /* 0 */
+            new MP4Integer32Property(parentAtom, "maxAURate"));
+        break;
+    }
+}
+
+#else
+MP4QosDescriptor::MP4QosDescriptor(MP4Atom &parentAtom)
+        : MP4Descriptor(parentAtom, MP4QosDescrTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer8Property(parentAtom, "predefined"));
+    AddProperty( /* 1 */
+        new MP4QosQualifierProperty(parentAtom, "qualifiers",
+                                    MP4QosTagsStart, MP4QosTagsEnd, Optional, Many));
+}
+
+MP4MaxDelayQosQualifier::MP4MaxDelayQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4MaxDelayQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "maxDelay"));
+}
+
+MP4PrefMaxDelayQosQualifier::MP4PrefMaxDelayQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4PrefMaxDelayQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "prefMaxDelay"));
+}
+
+MP4LossProbQosQualifier::MP4LossProbQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4LossProbQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Float32Property(parentAtom, "lossProb"));
+}
+
+MP4MaxGapLossQosQualifier::MP4MaxGapLossQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4MaxGapLossQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "maxGapLoss"));
+}
+
+MP4MaxAUSizeQosQualifier::MP4MaxAUSizeQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4MaxAUSizeQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "maxAUSize"));
+}
+
+MP4AvgAUSizeQosQualifier::MP4AvgAUSizeQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4AvgAUSizeQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "avgAUSize"));
+}
+
+MP4MaxAURateQosQualifier::MP4MaxAURateQosQualifier(MP4Atom &parentAtom)
+        : MP4QosQualifier(parentAtom, MP4MaxAURateQosTag)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(parentAtom, "maxAURate"));
+}
+#endif
+MP4UnknownQosQualifier::MP4UnknownQosQualifier(MP4Atom &parentAtom)
+        : MP4Descriptor(parentAtom)
+{
+    AddProperty( /* 0 */
+        new MP4BytesProperty(parentAtom, "data"));
+}
+
+void MP4UnknownQosQualifier::Read(MP4File& file)
+{
+    ReadHeader(file);
+
+    /* byte properties need to know how long they are before reading */
+    ((MP4BytesProperty*)m_pProperties[0])->SetValueSize(m_size);
+
+    ReadProperties(file);
+}
+
+MP4Descriptor* MP4QosQualifierProperty::CreateDescriptor(MP4Atom &parentAtom, uint8_t tag)
+{
+    MP4Descriptor* pDescriptor = NULL;
+    switch (tag) {
+    case MP4MaxDelayQosTag:
+    case MP4PrefMaxDelayQosTag:
+    case MP4LossProbQosTag:
+    case MP4MaxGapLossQosTag:
+    case MP4MaxAUSizeQosTag:
+    case MP4AvgAUSizeQosTag:
+    case MP4MaxAURateQosTag:
+        pDescriptor = new MP4QosDescriptorBase(parentAtom, tag);
+        break;
+    default:
+        pDescriptor = new MP4UnknownQosQualifier(parentAtom);
+        pDescriptor->SetTag(tag);
+    }
+
+    return pDescriptor;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/qosqualifiers.h b/src/qosqualifiers.h
new file mode 100644
index 0000000..5bd4bd0
--- /dev/null
+++ b/src/qosqualifiers.h
@@ -0,0 +1,65 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_QOSQUALIFIERS_H
+#define MP4V2_IMPL_QOSQUALIFIERS_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+const uint8_t MP4QosDescrTag                = 0x0C;
+const uint8_t MP4QosTagsStart               = 0x01;
+const uint8_t MP4MaxDelayQosTag         = 0x01;
+const uint8_t MP4PrefMaxDelayQosTag     = 0x02;
+const uint8_t MP4LossProbQosTag         = 0x03;
+const uint8_t MP4MaxGapLossQosTag           = 0x04;
+const uint8_t MP4MaxAUSizeQosTag            = 0x41;
+const uint8_t MP4AvgAUSizeQosTag            = 0x42;
+const uint8_t MP4MaxAURateQosTag            = 0x43;
+const uint8_t MP4QosTagsEnd             = 0xFF;
+
+class MP4QosDescriptorBase : public MP4Descriptor {
+public:
+    MP4QosDescriptorBase(MP4Atom &parentAtom, uint8_t tag);
+private:
+    MP4QosDescriptorBase();
+    MP4QosDescriptorBase ( const MP4QosDescriptorBase &src );
+    MP4QosDescriptorBase &operator= ( const MP4QosDescriptorBase &src );
+};
+
+class MP4UnknownQosQualifier : public MP4Descriptor {
+public:
+    MP4UnknownQosQualifier(MP4Atom &parentAtom);
+    void Read(MP4File& file);
+private:
+    MP4UnknownQosQualifier();
+    MP4UnknownQosQualifier ( const MP4UnknownQosQualifier &src );
+    MP4UnknownQosQualifier &operator= ( const MP4UnknownQosQualifier &src );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_QOSQUALIFIERS_H
diff --git a/src/qtff/ColorParameterBox.cpp b/src/qtff/ColorParameterBox.cpp
new file mode 100644
index 0000000..7581c63
--- /dev/null
+++ b/src/qtff/ColorParameterBox.cpp
@@ -0,0 +1,345 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 qtff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+    const string BOX_CODE = "colr";
+
+    bool findColorParameterBox( MP4FileHandle file, MP4Atom& coding, MP4Atom*& colr );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::add( MP4FileHandle file, uint16_t trackIndex, const Item& item )
+{
+    MP4Atom* coding;
+
+    if( !MP4_IS_VALID_FILE_HANDLE( file ))
+        throw new Exception( "invalid file handle", __FILE__, __LINE__, __FUNCTION__ );
+
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* colr;
+    if( !findColorParameterBox( file, *coding, colr ))
+        throw new Exception( "colr-box already exists", __FILE__, __LINE__, __FUNCTION__ );
+
+    colr = MP4Atom::CreateAtom( *((MP4File *)file), coding, BOX_CODE.c_str() );
+    coding->AddChildAtom( colr );
+    colr->Generate();
+
+    MP4StringProperty*    type;
+    MP4Integer16Property* primariesIndex;
+    MP4Integer16Property* transferFunctionIndex;
+    MP4Integer16Property* matrixIndex;
+
+    if( colr->FindProperty( "colr.colorParameterType", (MP4Property**)&type ))
+        type->SetValue( "nclc" );
+
+    if( colr->FindProperty( "colr.primariesIndex", (MP4Property**)&primariesIndex ))
+        primariesIndex->SetValue( item.primariesIndex );
+
+    if( colr->FindProperty( "colr.transferFunctionIndex", (MP4Property**)&transferFunctionIndex ))
+        transferFunctionIndex->SetValue( item.transferFunctionIndex );
+
+    if( colr->FindProperty( "colr.matrixIndex", (MP4Property**)&matrixIndex ))
+        matrixIndex->SetValue( item.matrixIndex );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::add( MP4FileHandle file, MP4TrackId trackId, const Item& item )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return add( file, mp4.FindTrackIndex( trackId ), item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::get( MP4FileHandle file, uint16_t trackIndex, Item& item )
+{
+    item.reset();
+
+    MP4Atom* coding;
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* colr;
+    if( findColorParameterBox( file, *coding, colr ))
+        throw new Exception( "colr-box not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Integer16Property* primariesIndex;
+    MP4Integer16Property* transferFunctionIndex;
+    MP4Integer16Property* matrixIndex;
+
+    if( colr->FindProperty( "colr.primariesIndex", (MP4Property**)&primariesIndex ))
+        item.primariesIndex = primariesIndex->GetValue();
+
+    if( colr->FindProperty( "colr.transferFunctionIndex", (MP4Property**)&transferFunctionIndex ))
+        item.transferFunctionIndex = transferFunctionIndex->GetValue();
+
+    if( colr->FindProperty( "colr.matrixIndex", (MP4Property**)&matrixIndex ))
+        item.matrixIndex = matrixIndex->GetValue();
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::get( MP4FileHandle file, MP4TrackId trackId, Item& item )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return get( file, mp4.FindTrackIndex( trackId ), item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::list( MP4FileHandle file, ItemList& itemList )
+{
+    itemList.clear();
+    MP4File& mp4 = *((MP4File*)file);
+
+    const uint16_t trackc = mp4.GetNumberOfTracks();
+    for( uint16_t i = 0; i < trackc; i++) {
+        MP4TrackId id = mp4.FindTrackId( i );
+        if( id == MP4_INVALID_TRACK_ID )
+            continue;
+
+        const char* type = mp4.GetTrackType( id );
+        if( !type )
+            continue;
+
+        itemList.resize( itemList.size() + 1 );
+        IndexedItem& xitem = itemList[itemList.size()-1];
+
+        xitem.trackIndex = i;
+        xitem.trackId    = id;
+
+        bool success = false;
+        try {
+            success = !get( file, i, xitem.item );
+        }
+        catch( Exception* x ) {
+            delete x;
+        }
+
+        if( !success ) {
+            itemList.resize( itemList.size() - 1 );
+            continue;
+        }
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::remove( MP4FileHandle file, uint16_t trackIndex )
+{
+    MP4Atom* coding;
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* colr;
+    if( findColorParameterBox( file, *coding, colr ))
+        throw new Exception( "colr-box not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    coding->DeleteChildAtom( colr );
+    delete colr;
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::remove( MP4FileHandle file, MP4TrackId trackId )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return remove( file, mp4.FindTrackIndex( trackId ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::set( MP4FileHandle file, uint16_t trackIndex, const Item& item )
+{
+    MP4Atom* coding;
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* colr;
+    if( findColorParameterBox( file, *coding, colr ))
+        throw new Exception( "colr-box not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Integer16Property* primariesIndex;
+    MP4Integer16Property* transferFunctionIndex;
+    MP4Integer16Property* matrixIndex;
+
+    if( colr->FindProperty( "colr.primariesIndex", (MP4Property**)&primariesIndex ))
+        primariesIndex->SetValue( item.primariesIndex );
+
+    if( colr->FindProperty( "colr.transferFunctionIndex", (MP4Property**)&transferFunctionIndex ))
+        transferFunctionIndex->SetValue( item.transferFunctionIndex );
+
+    if( colr->FindProperty( "colr.matrixIndex", (MP4Property**)&matrixIndex ))
+        matrixIndex->SetValue( item.matrixIndex );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ColorParameterBox::set( MP4FileHandle file, MP4TrackId trackId, const Item& item )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return set( file, mp4.FindTrackIndex( trackId ), item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ColorParameterBox::IndexedItem::IndexedItem()
+    : trackIndex ( numeric_limits<uint16_t>::max() )
+    , trackId    ( MP4_INVALID_TRACK_ID )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ColorParameterBox::Item::Item()
+    : primariesIndex        ( 6 )
+    , transferFunctionIndex ( 1 )
+    , matrixIndex           ( 6 )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+ColorParameterBox::Item::convertFromCSV( const string& text )
+{
+    istringstream iss( text );
+    char delim;
+
+    iss >> primariesIndex;
+    iss >> delim;
+    iss >> transferFunctionIndex;
+    iss >> delim;
+    iss >> matrixIndex;
+
+    // input was good if we end up with only eofbit set
+    if( iss.rdstate() != ios::eofbit ) {
+        reset();
+        ostringstream xss;
+        xss << "invalid ColorParameterBox format"
+            << " (expecting: INDEX1,INDEX2,INDEX3)"
+            << " got: " << text;
+        throw new Exception( xss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+ColorParameterBox::Item::convertToCSV() const
+{
+    string buffer;
+    return convertToCSV( buffer );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string&
+ColorParameterBox::Item::convertToCSV( string& buffer ) const
+{
+    ostringstream oss;
+    oss << primariesIndex << ',' << transferFunctionIndex << ',' << matrixIndex;
+    buffer = oss.str();
+    return buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+ColorParameterBox::Item::reset()
+{
+    primariesIndex        = 6;
+    transferFunctionIndex = 1;
+    matrixIndex           = 6;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+findColorParameterBox( MP4FileHandle file, MP4Atom& coding, MP4Atom*& colr )
+{
+    colr = NULL;
+
+    MP4Atom* found = NULL;
+    const uint32_t atomc = coding.GetNumberOfChildAtoms();
+    for( uint32_t i = 0; i < atomc; i++ ) {
+        MP4Atom* atom = coding.GetChildAtom( i );
+        if( BOX_CODE != atom->GetType() )
+            continue;
+        found = atom;
+    }
+    if( !found )
+        return true;
+
+    MP4StringProperty* type;
+    if( !found->FindProperty( "colr.colorParameterType", (MP4Property**)&type ))
+        return true;
+
+    const string type_nclc = "nclc";
+    if( type_nclc != type->GetValue() )
+        return true;
+
+    colr = found;
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+} // anonymous
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::qtff
diff --git a/src/qtff/ColorParameterBox.h b/src/qtff/ColorParameterBox.h
new file mode 100644
index 0000000..9a27fcd
--- /dev/null
+++ b/src/qtff/ColorParameterBox.h
@@ -0,0 +1,207 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_QTFF_COLORPARAMETERBOX_H
+#define MP4V2_IMPL_QTFF_COLORPARAMETERBOX_H
+
+namespace mp4v2 { namespace impl { namespace qtff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Functional class for colr-box (Color Parameter Box) support.
+///
+/// A colr-box is expected to be contained in a video track which is one of
+/// the following coding types:
+///     @li avc1
+///     @li mp4v
+///
+/// This implementation assumes a maximum count of <b>1</b> for
+/// VideoSampleEntry of the supported codings.
+///
+/// This implementation supports parameter-type 'nclc' only.
+///
+class MP4V2_EXPORT ColorParameterBox
+{
+public:
+    /// Data object for colr-box item.
+    /// This object correlates to one colr-box (Color Parameter Box).
+    ///
+    class MP4V2_EXPORT Item
+    {
+    public:
+        Item();
+
+        /// reset to state of newly constructed object.
+        void reset();
+
+        // convert from string CSV format.
+        void convertFromCSV( const string& csv );
+
+        // convert to string CSV format.
+        string convertToCSV() const;
+
+        // convert to string CSV format with buffer.
+        string& convertToCSV( string& buffer ) const;
+
+    public:
+        /// a 16-bit unsigned integer index.
+        /// Specifies an index into a table specifying the CIE 1931 xy
+        /// chromaticity coordinates of the white point and the red, green, and
+        /// blue primaries. The table of primaries specifies the white point and
+        /// the red, green, and blue primary color points for a video system.
+        uint16_t primariesIndex;
+
+        /// a 16-bit unsigned integer index.
+        /// Specifies an an index into a table specifying the nonlinear transfer
+        /// function coefficients used to translate between RGB color space values
+        /// and Y′CbCr values. The table of transfer function coefficients
+        /// specifies the nonlinear function coefficients used to translate
+        /// between the stored Y′CbCr values and a video capture or display
+        /// system.
+        uint16_t transferFunctionIndex;
+
+        /// a 16-bit unsigned integer index.
+        /// Specifies an index into a table specifying the transformation matrix
+        /// coefficients used to translate between RGB color space values and
+        /// Y′CbCr values. The table of matrixes specifies the matrix used during
+        /// the translation.
+        uint16_t matrixIndex;
+    };
+
+    class MP4V2_EXPORT IndexedItem {
+    public:
+        IndexedItem();
+
+        uint16_t trackIndex;
+        uint16_t trackId;
+        Item     item;
+    };
+
+    typedef vector<IndexedItem> ItemList;
+
+    static bool list( MP4FileHandle file, ItemList& itemList );
+
+    /// Add colr-box by track-index.
+    ///
+    /// This function adds a colr-box to <b>trackId</b> of <b>file</b>.
+    /// The track must be a video-track and match one of the supporting
+    /// codings.
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    /// @param item colr-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool add( MP4FileHandle file, uint16_t trackIndex, const Item& item );
+
+    /// Add colr-box by track-id.
+    ///
+    /// This function adds a colr-box to <b>trackId</b> of <b>file</b>.
+    /// The track must be a video-track and match one of the supporting
+    /// codings.
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    /// @param item colr-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool add( MP4FileHandle file, MP4TrackId trackId, const Item& item );
+
+    /// Store colr-box (Color Parameter Box) properties by track-index.
+    ///
+    /// This function sets the properties of a <b>colr-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    /// @param item colr-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool set( MP4FileHandle file, uint16_t trackIndex, const Item& item );
+
+    /// Store colr-box (Color Parameter Box) properties by track-id.
+    ///
+    /// This function sets the properties of a <b>colr-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    /// @param item colr-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool set( MP4FileHandle file, MP4TrackId trackId, const Item& item );
+
+    /// Fetch colr-box (Color Parameter Box) properties by track-index.
+    ///
+    /// This function gets the properties of a <b>colr-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    /// @param item colr-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool get( MP4FileHandle file, uint16_t trackIndex, Item& item );
+
+    /// Fetch colr-box (Color Parameter Box) properties by track-id.
+    ///
+    /// This function gets the properties of a <b>colr-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    /// @param item colr-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool get( MP4FileHandle file, MP4TrackId trackId, Item& item );
+
+    /// Remove colr-box (Color Parameter Box) by track-index.
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool remove( MP4FileHandle file, uint16_t trackIndex );
+
+    /// Remove colr-box (Color Parameter Box) by track-id.
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool remove( MP4FileHandle file, MP4TrackId trackId );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::qtff
+
+#endif // MP4V2_IMPL_QTTF_COLORPARAMETERBOX_H
diff --git a/src/qtff/PictureAspectRatioBox.cpp b/src/qtff/PictureAspectRatioBox.cpp
new file mode 100644
index 0000000..4fe5916
--- /dev/null
+++ b/src/qtff/PictureAspectRatioBox.cpp
@@ -0,0 +1,313 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 qtff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+    const string BOX_CODE = "pasp";
+
+    bool findPictureAspectRatioBox( MP4FileHandle file, MP4Atom& coding, MP4Atom*& pasp );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::add( MP4FileHandle file, uint16_t trackIndex, const Item& item )
+{
+    MP4Atom* coding;
+
+    if( !MP4_IS_VALID_FILE_HANDLE( file ))
+        throw new Exception( "invalid file handle", __FILE__, __LINE__, __FUNCTION__ );
+
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* pasp;
+    if( !findPictureAspectRatioBox( file, *coding, pasp ))
+        throw new Exception( "pasp-box already exists", __FILE__, __LINE__, __FUNCTION__ );
+
+    pasp = MP4Atom::CreateAtom( *((MP4File *)file), coding, BOX_CODE.c_str() );
+    coding->AddChildAtom( pasp );
+    pasp->Generate();
+
+    MP4Integer16Property* hSpacing;
+    MP4Integer16Property* vSpacing;
+
+    if( pasp->FindProperty( "pasp.hSpacing", (MP4Property**)&hSpacing ))
+        hSpacing->SetValue( item.hSpacing );
+
+    if( pasp->FindProperty( "pasp.vSpacing", (MP4Property**)&vSpacing ))
+        vSpacing->SetValue( item.vSpacing );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::add( MP4FileHandle file, MP4TrackId trackId, const Item& item )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return add( file, mp4.FindTrackIndex( trackId ), item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::get( MP4FileHandle file, uint16_t trackIndex, Item& item )
+{
+    item.reset();
+
+    MP4Atom* coding;
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* pasp;
+    if( findPictureAspectRatioBox( file, *coding, pasp ))
+        throw new Exception( "pasp-box not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Integer16Property* hSpacing;
+    MP4Integer16Property* vSpacing;
+
+    if( pasp->FindProperty( "pasp.hSpacing", (MP4Property**)&hSpacing ))
+        item.hSpacing = hSpacing->GetValue();
+
+    if( pasp->FindProperty( "pasp.vSpacing", (MP4Property**)&vSpacing ))
+        item.vSpacing = vSpacing->GetValue();
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::get( MP4FileHandle file, MP4TrackId trackId, Item& item )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return get( file, mp4.FindTrackIndex( trackId ), item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::list( MP4FileHandle file, ItemList& itemList )
+{
+    itemList.clear();
+    MP4File& mp4 = *((MP4File*)file);
+
+    const uint16_t trackc = mp4.GetNumberOfTracks();
+    for( uint16_t i = 0; i < trackc; i++) {
+        MP4TrackId id = mp4.FindTrackId( i );
+        if( id == MP4_INVALID_TRACK_ID )
+            continue;
+
+        const char* type = mp4.GetTrackType( id );
+        if( !type )
+            continue;
+
+        itemList.resize( itemList.size() + 1 );
+        IndexedItem& xitem = itemList[itemList.size()-1];
+
+        xitem.trackIndex = i;
+        xitem.trackId    = id;
+
+        bool success = false;
+        try {
+            success = !get( file, i, xitem.item );
+        }
+        catch( Exception* x ) {
+            delete x;
+        }
+
+        if( !success ) {
+            itemList.resize( itemList.size() - 1 );
+            continue;
+        }
+    }
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::remove( MP4FileHandle file, uint16_t trackIndex )
+{
+    MP4Atom* coding;
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* pasp;
+    if( findPictureAspectRatioBox( file, *coding, pasp ))
+        throw new Exception( "pasp-box not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    coding->DeleteChildAtom( pasp );
+    delete pasp;
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::remove( MP4FileHandle file, MP4TrackId trackId )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return remove( file, mp4.FindTrackIndex( trackId ));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::set( MP4FileHandle file, uint16_t trackIndex, const Item& item )
+{
+    MP4Atom* coding;
+    if( findCoding( file, trackIndex, coding ))
+        throw new Exception( "supported coding not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Atom* pasp;
+    if( findPictureAspectRatioBox( file, *coding, pasp ))
+        throw new Exception( "pasp-box not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4Integer16Property* hSpacing;
+    MP4Integer16Property* vSpacing;
+
+    if( pasp->FindProperty( "pasp.hSpacing", (MP4Property**)&hSpacing ))
+        hSpacing->SetValue( item.hSpacing );
+
+    if( pasp->FindProperty( "pasp.vSpacing", (MP4Property**)&vSpacing ))
+        vSpacing->SetValue( item.vSpacing );
+
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+PictureAspectRatioBox::set( MP4FileHandle file, MP4TrackId trackId, const Item& item )
+{
+    MP4File& mp4 = *((MP4File*)file);
+    return set( file, mp4.FindTrackIndex( trackId ), item );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+PictureAspectRatioBox::IndexedItem::IndexedItem()
+    : trackIndex ( numeric_limits<uint16_t>::max() )
+    , trackId    ( MP4_INVALID_TRACK_ID )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+PictureAspectRatioBox::Item::Item()
+    : hSpacing ( 1 )
+    , vSpacing ( 1 )
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+PictureAspectRatioBox::Item::reset()
+{
+    hSpacing = 1;
+    vSpacing = 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+PictureAspectRatioBox::Item::convertFromCSV( const string& text )
+{
+    istringstream iss( text );
+    char delim;
+
+    iss >> hSpacing;
+    iss >> delim;
+    iss >> vSpacing;
+
+    // input was good if we end up with only eofbit set
+    if( iss.rdstate() != ios::eofbit ) {
+        reset();
+        ostringstream xss;
+        xss << "invalid PcitureAspectRatioBox format"
+            << " (expecting: hSpacing,vSpacing)"
+            << " got: " << text;
+        throw new Exception( xss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+PictureAspectRatioBox::Item::convertToCSV() const
+{
+    string buffer;
+    return convertToCSV( buffer );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string&
+PictureAspectRatioBox::Item::convertToCSV( string& buffer ) const
+{
+    ostringstream oss;
+    oss << hSpacing << ',' << vSpacing;
+    buffer = oss.str();
+    return buffer;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+findPictureAspectRatioBox( MP4FileHandle file, MP4Atom& coding, MP4Atom*& pasp )
+{
+    pasp = NULL;
+
+    MP4Atom* found = NULL;
+    const uint32_t atomc = coding.GetNumberOfChildAtoms();
+    for( uint32_t i = 0; i < atomc; i++ ) {
+        MP4Atom* atom = coding.GetChildAtom( i );
+        if( BOX_CODE != atom->GetType() )
+            continue;
+        found = atom;
+    }
+    if( !found )
+        return true;
+
+    pasp = found;
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}}} // namespace mp4v2::impl::qtff::anonymous
diff --git a/src/qtff/PictureAspectRatioBox.h b/src/qtff/PictureAspectRatioBox.h
new file mode 100644
index 0000000..75e4c8f
--- /dev/null
+++ b/src/qtff/PictureAspectRatioBox.h
@@ -0,0 +1,188 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_QTFF_PICTUREAPSECTRATIOBOX_H
+#define MP4V2_IMPL_QTFF_PICTUREAPSECTRATIOBOX_H
+
+namespace mp4v2 { namespace impl { namespace qtff {
+    using namespace std;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/// Functional class for pasp-box (Picture Aspect Ratio Box) support.
+///
+/// A pasp-box is expected to be contained in a video track which is one of
+/// the following coding types:
+///     @li avc1
+///     @li mp4v
+///
+/// This implementation assumes a maximum count of <b>1</b> for
+/// VideoSampleEntry of the supported codings.
+///
+class MP4V2_EXPORT PictureAspectRatioBox
+{
+public:
+    /// Data object for pasp-box item.
+    /// This object correlates to one pasp-box (Picture Aspect Ratio Box).
+    class MP4V2_EXPORT Item
+    {
+    public:
+        Item ();
+
+        /// reset to state of newly constructed object.
+        void reset();
+
+        // convert from string CSV format.
+        void convertFromCSV( const string& csv );
+
+        // convert to string CSV format.
+        string convertToCSV() const;
+
+        // convert to string CSV format with buffer.
+        string& convertToCSV( string& buffer ) const;
+
+    public:
+        /// an unsigned 32-bit integer specifying the vertical spacing of pixels.
+        uint32_t hSpacing;
+
+        /// an unsigned 32-bit integer specifying the horizontal spacing of pixels.
+        uint32_t vSpacing;
+    };
+
+    class MP4V2_EXPORT IndexedItem {
+    public:
+        IndexedItem();
+
+        uint16_t trackIndex;
+        uint16_t trackId;
+        Item     item;
+    };
+
+    typedef vector<IndexedItem> ItemList;
+
+    static bool list( MP4FileHandle file, ItemList& itemList );
+
+    /// Add pasp-box by track-index.
+    ///
+    /// This function adds a pasp-box to <b>trackId</b> of <b>file</b>.
+    /// The track must be a video-track and match one of the supporting
+    /// codings.
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    /// @param item pasp-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool add( MP4FileHandle file, uint16_t trackIndex, const Item& item );
+
+    /// Add pasp-box by track-id.
+    ///
+    /// This function adds a pasp-box to <b>trackId</b> of <b>file</b>.
+    /// The track must be a video-track and match one of the supporting
+    /// codings.
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    /// @param item pasp-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool add( MP4FileHandle file, MP4TrackId trackId, const Item& item );
+
+    /// Store pasp-box (Color Parameter Box) properties by track-index.
+    ///
+    /// This function sets the properties of a <b>pasp-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    /// @param item pasp-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool set( MP4FileHandle file, uint16_t trackIndex, const Item& item );
+
+    /// Store pasp-box (Color Parameter Box) properties by track-id.
+    ///
+    /// This function sets the properties of a <b>pasp-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    /// @param item pasp-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool set( MP4FileHandle file, MP4TrackId trackId, const Item& item );
+
+    /// Fetch pasp-box (Color Parameter Box) properties by track-index.
+    ///
+    /// This function gets the properties of a <b>pasp-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    /// @param item pasp-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool get( MP4FileHandle file, uint16_t trackIndex, Item& item );
+
+    /// Fetch pasp-box (Color Parameter Box) properties by track-id.
+    ///
+    /// This function gets the properties of a <b>pasp-box</b>
+    /// (Color Parameter Box).
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    /// @param item pasp-box properties to set.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool get( MP4FileHandle file, MP4TrackId trackId, Item& item );
+
+    /// Remove pasp-box (Color Parameter Box) by track-index.
+    ///
+    /// @param file on which to operate.
+    /// @param trackIndex on which to operate.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool remove( MP4FileHandle file, uint16_t trackIndex );
+
+    /// Remove pasp-box (Color Parameter Box) by track-id.
+    ///
+    /// @param file on which to operate.
+    /// @param trackId on which to operate.
+    ///
+    /// @return <b>true</b> on failure, <b>false</b> on success.
+    ///
+    static bool remove( MP4FileHandle file, MP4TrackId trackId );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::qtff
+
+#endif // MP4V2_IMPL_QTTF_PICTUREAPSECTRATIOBOX_H
diff --git a/src/qtff/coding.cpp b/src/qtff/coding.cpp
new file mode 100644
index 0000000..29eaf77
--- /dev/null
+++ b/src/qtff/coding.cpp
@@ -0,0 +1,95 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 qtff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+    class StaticData
+    {
+    public:
+        StaticData()
+        {
+            supportedCodings.insert( "avc1" );
+            supportedCodings.insert( "mp4v" );
+        }
+
+        set<string> supportedCodings;
+    };
+
+    const StaticData STATIC_DATA;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+findCoding( MP4FileHandle file, uint16_t trackIndex, MP4Atom*& coding )
+{
+    coding = NULL;
+    MP4File& mp4 = *((MP4File*)file);
+
+    if( trackIndex == numeric_limits<uint16_t>::max() ) {
+        ostringstream xss;
+        xss << "invalid track-index: " << trackIndex;
+        throw new Exception( xss.str(), __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    ostringstream oss;
+    oss << "moov.trak[" << trackIndex << "].mdia.hdlr";
+    MP4Atom* hdlr = mp4.FindAtom( oss.str().c_str() );
+    if( !hdlr )
+        throw new Exception( "media handler not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    MP4StringProperty* handlerType;
+    if( !hdlr->FindProperty( "hdlr.handlerType", (MP4Property**)&handlerType ))
+        throw new Exception( "media handler type-property not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    const string video = "vide";
+    if( video != handlerType->GetValue() )
+        throw new Exception( "video-track required", __FILE__, __LINE__, __FUNCTION__ );
+
+    oss.str( "" );
+    oss.clear();
+    oss << "moov.trak[" << trackIndex << "].mdia.minf.stbl.stsd";
+    MP4Atom* stsd = mp4.FindAtom( oss.str().c_str() );
+    if( !stsd )
+        throw new Exception( "media handler type-property not found", __FILE__, __LINE__, __FUNCTION__ );
+
+    // find first atom which is a supported coding
+    const uint32_t atomc = stsd->GetNumberOfChildAtoms();
+    for( uint32_t i = 0; i < atomc; i++ ) {
+        MP4Atom* atom = stsd->GetChildAtom( i );
+        if( STATIC_DATA.supportedCodings.find( atom->GetType() ) == STATIC_DATA.supportedCodings.end() )
+            continue;
+        coding = atom;
+    }
+
+    return coding == NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::qtff
diff --git a/src/qtff/coding.h b/src/qtff/coding.h
new file mode 100644
index 0000000..819c5cc
--- /dev/null
+++ b/src/qtff/coding.h
@@ -0,0 +1,37 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_QTFF_CODING_H
+#define MP4V2_IMPL_QTFF_CODING_H
+
+namespace mp4v2 { namespace impl { namespace qtff {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool findCoding( MP4FileHandle file, uint16_t trackIndex, MP4Atom*& coding );
+
+///////////////////////////////////////////////////////////////////////////////
+
+}}} // namespace mp4v2::impl::qtff
+
+#endif // MP4V2_IMPL_QTTF_CODING_H
diff --git a/src/qtff/impl.h b/src/qtff/impl.h
new file mode 100644
index 0000000..27c7edb
--- /dev/null
+++ b/src/qtff/impl.h
@@ -0,0 +1,34 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_QTFF_IMPL_H
+#define MP4V2_IMPL_QTFF_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "src/impl.h"
+#include "qtff.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_QTFF_IMPL_H
diff --git a/src/qtff/qtff.h b/src/qtff/qtff.h
new file mode 100644
index 0000000..6403821
--- /dev/null
+++ b/src/qtff/qtff.h
@@ -0,0 +1,45 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MP4V2_IMPL_QTFF_QTFF_H
+#define MP4V2_IMPL_QTFF_QTFF_H
+
+/// @namespace mp4v2::impl::qtff (private) QuickTime File Format.
+/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b>
+///
+/// This namespace implements some features that are specified by the
+/// QuickTime File Format Specification, revision 2007-09-04.
+///
+namespace mp4v2 { namespace impl { namespace qtff {
+    ;
+}}}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "ColorParameterBox.h"
+#include "PictureAspectRatioBox.h"
+#include "coding.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_QTTF_QTFF_H
diff --git a/src/rtphint.cpp b/src/rtphint.cpp
new file mode 100644
index 0000000..e07309d
--- /dev/null
+++ b/src/rtphint.cpp
@@ -0,0 +1,1358 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#include "src/impl.h"
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* rtp hint track operations */
+
+MP4RtpHintTrack::MP4RtpHintTrack(MP4File& file, MP4Atom& trakAtom)
+        : MP4Track(file, trakAtom)
+{
+    m_pRefTrack = NULL;
+
+    m_pRtpMapProperty = NULL;
+    m_pPayloadNumberProperty = NULL;
+    m_pMaxPacketSizeProperty = NULL;
+    m_pSnroProperty = NULL;
+    m_pTsroProperty = NULL;
+
+    m_pReadHint = NULL;
+    m_pReadHintSample = NULL;
+    m_readHintSampleSize = 0;
+
+    m_pWriteHint = NULL;
+    m_writeHintId = MP4_INVALID_SAMPLE_ID;
+    m_writePacketId = 0;
+
+    m_pTrpy = NULL;
+    m_pNump = NULL;
+    m_pTpyl = NULL;
+    m_pMaxr = NULL;
+    m_pDmed = NULL;
+    m_pDimm = NULL;
+    m_pPmax = NULL;
+    m_pDmax = NULL;
+
+    m_pMaxPdu = NULL;
+    m_pAvgPdu = NULL;
+    m_pMaxBitRate = NULL;
+    m_pAvgBitRate = NULL;
+
+    m_thisSec = 0;
+    m_bytesThisSec = 0;
+    m_bytesThisHint = 0;
+    m_bytesThisPacket = 0;
+}
+
+MP4RtpHintTrack::~MP4RtpHintTrack()
+{
+    delete m_pReadHint; m_pReadHint = NULL;
+    MP4Free( m_pReadHintSample ); m_pReadHintSample = NULL;
+    delete m_pWriteHint; m_pWriteHint = NULL;
+}
+
+void MP4RtpHintTrack::InitRefTrack()
+{
+    if (m_pRefTrack == NULL) {
+        MP4Integer32Property* pRefTrackIdProperty = NULL;
+        (void)m_trakAtom.FindProperty(
+            "trak.tref.hint.entries[0].trackId",
+            (MP4Property**)&pRefTrackIdProperty);
+        ASSERT(pRefTrackIdProperty);
+
+        m_pRefTrack = m_File.GetTrack(pRefTrackIdProperty->GetValue());
+    }
+}
+
+void MP4RtpHintTrack::InitRtpStart()
+{
+    number::srandom( time::getLocalTimeMilliseconds() );
+
+    (void)m_trakAtom.FindProperty(
+        "trak.udta.hnti.rtp .snro.offset",
+        (MP4Property**)&m_pSnroProperty);
+
+    if (m_pSnroProperty) {
+        m_rtpSequenceStart = m_pSnroProperty->GetValue();
+    } else {
+        m_rtpSequenceStart = number::random32();
+    }
+
+    (void)m_trakAtom.FindProperty(
+        "trak.udta.hnti.rtp .tsro.offset",
+        (MP4Property**)&m_pTsroProperty);
+
+    if (m_pTsroProperty) {
+        m_rtpTimestampStart = m_pTsroProperty->GetValue();
+    } else {
+        m_rtpTimestampStart = number::random32();
+    }
+}
+
+void MP4RtpHintTrack::ReadHint(
+    MP4SampleId hintSampleId,
+    uint16_t* pNumPackets)
+{
+    if (m_pRefTrack == NULL) {
+        InitRefTrack();
+        InitRtpStart();
+    }
+
+    // dispose of any old hint
+    delete m_pReadHint; m_pReadHint = NULL;
+    MP4Free( m_pReadHintSample ); m_pReadHintSample = NULL;
+    m_readHintSampleSize = 0;
+
+    // read the desired hint sample into memory
+    ReadSample(
+        hintSampleId,
+        &m_pReadHintSample,
+        &m_readHintSampleSize,
+        &m_readHintTimestamp);
+
+    m_File.EnableMemoryBuffer(m_pReadHintSample, m_readHintSampleSize);
+
+    m_pReadHint = new MP4RtpHint(*this);
+    m_pReadHint->Read(m_File);
+
+    m_File.DisableMemoryBuffer();
+
+    if (pNumPackets) {
+        *pNumPackets = GetHintNumberOfPackets();
+    }
+}
+
+uint16_t MP4RtpHintTrack::GetHintNumberOfPackets()
+{
+    if (m_pReadHint == NULL) {
+        throw new Exception("no hint has been read",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    return m_pReadHint->GetNumberOfPackets();
+}
+
+bool MP4RtpHintTrack::GetPacketBFrame(uint16_t packetIndex)
+{
+    if (m_pReadHint == NULL) {
+        throw new Exception("no hint has been read",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    MP4RtpPacket* pPacket =
+        m_pReadHint->GetPacket(packetIndex);
+
+    return pPacket->IsBFrame();
+}
+
+uint16_t MP4RtpHintTrack::GetPacketTransmitOffset(uint16_t packetIndex)
+{
+    if (m_pReadHint == NULL) {
+        throw new Exception("no hint has been read",
+                            __FILE__, __LINE__, __FUNCTION__);
+     }
+
+    MP4RtpPacket* pPacket =
+        m_pReadHint->GetPacket(packetIndex);
+
+    return pPacket->GetTransmitOffset();
+}
+
+void MP4RtpHintTrack::ReadPacket(
+    uint16_t packetIndex,
+    uint8_t** ppBytes,
+    uint32_t* pNumBytes,
+    uint32_t ssrc,
+    bool addHeader,
+    bool addPayload)
+{
+    if (m_pReadHint == NULL) {
+        throw new Exception("no hint has been read",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+    if (!addHeader && !addPayload) {
+        throw new Exception("no data requested",
+                             __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    MP4RtpPacket* pPacket =
+        m_pReadHint->GetPacket(packetIndex);
+
+    *pNumBytes = 0;
+    if (addHeader) {
+        *pNumBytes += 12;
+    }
+    if (addPayload) {
+        *pNumBytes += pPacket->GetDataSize();
+    }
+
+    // if needed, allocate the packet memory
+    bool buffer_malloc = false;
+
+    if (*ppBytes == NULL) {
+        *ppBytes = (uint8_t*)MP4Malloc(*pNumBytes);
+        buffer_malloc = true;
+    }
+
+    try {
+        uint8_t* pDest = *ppBytes;
+
+        if (addHeader) {
+            *pDest++ =
+                0x80 | (pPacket->GetPBit() << 5) | (pPacket->GetXBit() << 4);
+
+            *pDest++ =
+                (pPacket->GetMBit() << 7) | pPacket->GetPayload();
+
+            *((uint16_t*)pDest) =
+                MP4V2_HTONS(m_rtpSequenceStart + pPacket->GetSequenceNumber());
+            pDest += 2;
+
+            *((uint32_t*)pDest) =
+                MP4V2_HTONL(m_rtpTimestampStart + (uint32_t)m_readHintTimestamp);
+            pDest += 4;
+
+            *((uint32_t*)pDest) =
+                MP4V2_HTONL(ssrc);
+            pDest += 4;
+        }
+
+        if (addPayload) {
+            pPacket->GetData(pDest);
+        }
+    }
+    catch (Exception* x) {
+        if (buffer_malloc) {
+            MP4Free(*ppBytes);
+            *ppBytes = NULL;
+        }
+        throw x;
+    }
+
+    log.hexDump(0, MP4_LOG_VERBOSE1, *ppBytes, *pNumBytes,
+                "\"%s\": %u ", GetFile().GetFilename().c_str(),
+                packetIndex);
+}
+
+MP4Timestamp MP4RtpHintTrack::GetRtpTimestampStart()
+{
+    if (m_pRefTrack == NULL) {
+        InitRefTrack();
+        InitRtpStart();
+    }
+
+    return m_rtpTimestampStart;
+}
+
+void MP4RtpHintTrack::SetRtpTimestampStart(MP4Timestamp start)
+{
+    if (!m_pTsroProperty) {
+        MP4Atom* pTsroAtom =
+            m_File.AddDescendantAtoms(&m_trakAtom, "udta.hnti.rtp .tsro");
+
+        ASSERT(pTsroAtom);
+
+        (void)pTsroAtom->FindProperty("offset",
+                                      (MP4Property**)&m_pTsroProperty);
+
+        ASSERT(m_pTsroProperty);
+    }
+
+    m_pTsroProperty->SetValue(start);
+    m_rtpTimestampStart = start;
+}
+
+void MP4RtpHintTrack::InitPayload()
+{
+    if (m_pRtpMapProperty == NULL) {
+        (void)m_trakAtom.FindProperty(
+            "trak.udta.hinf.payt.rtpMap",
+            (MP4Property**)&m_pRtpMapProperty);
+    }
+
+    if (m_pPayloadNumberProperty == NULL) {
+        (void)m_trakAtom.FindProperty(
+            "trak.udta.hinf.payt.payloadNumber",
+            (MP4Property**)&m_pPayloadNumberProperty);
+    }
+
+    if (m_pMaxPacketSizeProperty == NULL) {
+        (void)m_trakAtom.FindProperty(
+            "trak.mdia.minf.stbl.stsd.rtp .maxPacketSize",
+            (MP4Property**)&m_pMaxPacketSizeProperty);
+    }
+}
+
+void MP4RtpHintTrack::GetPayload(
+    char** ppPayloadName,
+    uint8_t* pPayloadNumber,
+    uint16_t* pMaxPayloadSize,
+    char **ppEncodingParams)
+{
+    const char* pRtpMap;
+    const char* pSlash;
+    uint32_t length;
+    InitPayload();
+
+    if (ppPayloadName || ppEncodingParams) {
+        if (ppPayloadName)
+            *ppPayloadName = NULL;
+        if (ppEncodingParams)
+            *ppEncodingParams = NULL;
+        if (m_pRtpMapProperty) {
+            pRtpMap = m_pRtpMapProperty->GetValue();
+            pSlash = strchr(pRtpMap, '/');
+
+            if (pSlash) {
+                length = pSlash - pRtpMap;
+            } else {
+                length = (uint32_t)strlen(pRtpMap);
+            }
+
+            if (ppPayloadName) {
+                *ppPayloadName = (char*)MP4Calloc(length + 1);
+                strncpy(*ppPayloadName, pRtpMap, length);
+            }
+            if (pSlash && ppEncodingParams) {
+                pSlash++;
+                pSlash = strchr(pSlash, '/');
+                if (pSlash != NULL) {
+                    pSlash++;
+                    if (pSlash != '\0') {
+                        length = (uint32_t)strlen(pRtpMap) - (pSlash - pRtpMap);
+                        *ppEncodingParams = (char *)MP4Calloc(length + 1);
+                        strncpy(*ppEncodingParams, pSlash, length);
+                    }
+                }
+            }
+        }
+    }
+
+    if (pPayloadNumber) {
+        if (m_pPayloadNumberProperty) {
+            *pPayloadNumber = m_pPayloadNumberProperty->GetValue();
+        } else {
+            *pPayloadNumber = 0;
+        }
+    }
+
+    if (pMaxPayloadSize) {
+        if (m_pMaxPacketSizeProperty) {
+            *pMaxPayloadSize = m_pMaxPacketSizeProperty->GetValue();
+        } else {
+            *pMaxPayloadSize = 0;
+        }
+    }
+}
+
+void MP4RtpHintTrack::SetPayload(
+    const char* payloadName,
+    uint8_t payloadNumber,
+    uint16_t maxPayloadSize,
+    const char *encoding_parms,
+    bool include_rtp_map,
+    bool include_mpeg4_esid)
+{
+    InitRefTrack();
+    InitPayload();
+
+    ASSERT(m_pRtpMapProperty);
+    ASSERT(m_pPayloadNumberProperty);
+    ASSERT(m_pMaxPacketSizeProperty);
+
+    size_t len = strlen(payloadName) + 16;
+    if (encoding_parms != NULL) {
+        size_t temp = strlen(encoding_parms);
+        if (temp == 0) {
+            encoding_parms = NULL;
+        } else {
+            len += temp;
+        }
+    }
+
+    char* rtpMapBuf = (char*)MP4Malloc(len);
+    snprintf(rtpMapBuf, len, "%s/%u%c%s",
+             payloadName,
+             GetTimeScale(),
+             encoding_parms != NULL ? '/' : '\0',
+             encoding_parms == NULL ? "" : encoding_parms);
+    m_pRtpMapProperty->SetValue(rtpMapBuf);
+
+    m_pPayloadNumberProperty->SetValue(payloadNumber);
+
+    if (maxPayloadSize == 0) {
+        maxPayloadSize = 1460;
+    }
+    m_pMaxPacketSizeProperty->SetValue(maxPayloadSize);
+
+    // set sdp media type
+    const char* sdpMediaType;
+    if (!strcmp(m_pRefTrack->GetType(), MP4_AUDIO_TRACK_TYPE)) {
+        sdpMediaType = "audio";
+    } else if (!strcmp(m_pRefTrack->GetType(), MP4_VIDEO_TRACK_TYPE)) {
+        sdpMediaType = "video";
+    } else if (!strcmp(m_pRefTrack->GetType(), MP4_CNTL_TRACK_TYPE)) {
+        sdpMediaType = "control";
+    } else {
+        sdpMediaType = "application";
+    }
+
+    uint32_t maxlen =
+        (uint32_t)strlen(sdpMediaType) + (uint32_t)strlen(rtpMapBuf) + 256;
+    char* sdpBuf = (char*)MP4Malloc(maxlen);
+    uint32_t buflen;
+    buflen = snprintf(sdpBuf, maxlen,
+                      "m=%s 0 RTP/AVP %u\015\012"
+                      "a=control:trackID=%u\015\012",
+                      sdpMediaType, payloadNumber,
+                      m_trackId);
+    if (include_rtp_map) {
+        buflen += snprintf(sdpBuf + buflen, maxlen - buflen,
+                           "a=rtpmap:%u %s\015\012",
+                           payloadNumber, rtpMapBuf);
+    }
+    if (include_mpeg4_esid) {
+        snprintf(sdpBuf + buflen, maxlen - buflen,
+                 "a=mpeg4-esid:%u\015\012",
+                 m_pRefTrack->GetId());
+    }
+
+    MP4StringProperty* pSdpProperty = NULL;
+    (void)m_trakAtom.FindProperty("trak.udta.hnti.sdp .sdpText",
+                                    (MP4Property**)&pSdpProperty);
+    ASSERT(pSdpProperty);
+    pSdpProperty->SetValue(sdpBuf);
+
+    // cleanup
+    MP4Free(rtpMapBuf);
+    MP4Free(sdpBuf);
+}
+
+void MP4RtpHintTrack::AddHint(bool isBFrame, uint32_t timestampOffset)
+{
+    // on first hint, need to lookup the reference track
+    if (m_writeHintId == MP4_INVALID_SAMPLE_ID) {
+        InitRefTrack();
+        InitStats();
+    }
+
+    if (m_pWriteHint) {
+        throw new Exception("unwritten hint is still pending", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    m_pWriteHint = new MP4RtpHint(*this);
+    m_pWriteHint->SetBFrame(isBFrame);
+    m_pWriteHint->SetTimestampOffset(timestampOffset);
+
+    m_bytesThisHint = 0;
+    m_writeHintId++;
+}
+
+void MP4RtpHintTrack::AddPacket(bool setMbit, int32_t transmitOffset)
+{
+    if (m_pWriteHint == NULL) {
+        throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    MP4RtpPacket* pPacket = m_pWriteHint->AddPacket();
+
+    ASSERT(m_pPayloadNumberProperty);
+
+    pPacket->Set(
+        m_pPayloadNumberProperty->GetValue(),
+        m_writePacketId++,
+        setMbit);
+    pPacket->SetTransmitOffset(transmitOffset);
+
+    m_bytesThisHint += 12;
+    if (m_bytesThisPacket > m_pPmax->GetValue()) {
+        m_pPmax->SetValue(m_bytesThisPacket);
+    }
+    m_bytesThisPacket = 12;
+    m_pNump->IncrementValue();
+    m_pTrpy->IncrementValue(12); // RTP packet header size
+}
+
+void MP4RtpHintTrack::AddImmediateData(
+    const uint8_t* pBytes,
+    uint32_t numBytes)
+{
+    if (m_pWriteHint == NULL) {
+        throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    MP4RtpPacket* pPacket = m_pWriteHint->GetCurrentPacket();
+    if (pPacket == NULL) {
+        throw new Exception("no packet pending", __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    if (pBytes == NULL || numBytes == 0) {
+        throw new Exception("no data",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+    if (numBytes > 14) {
+        throw new Exception("data size is larger than 14 bytes",
+                            __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    MP4RtpImmediateData* pData = new MP4RtpImmediateData(*pPacket);
+    pData->Set(pBytes, numBytes);
+
+    pPacket->AddData(pData);
+
+    m_bytesThisHint += numBytes;
+    m_bytesThisPacket += numBytes;
+    m_pDimm->IncrementValue(numBytes);
+    m_pTpyl->IncrementValue(numBytes);
+    m_pTrpy->IncrementValue(numBytes);
+}
+
+void MP4RtpHintTrack::AddSampleData(
+    MP4SampleId sampleId,
+    uint32_t dataOffset,
+    uint32_t dataLength)
+{
+    if (m_pWriteHint == NULL) {
+        throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    MP4RtpPacket* pPacket = m_pWriteHint->GetCurrentPacket();
+    if (pPacket == NULL) {
+        throw new Exception("no packet pending", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    MP4RtpSampleData* pData = new MP4RtpSampleData(*pPacket);
+
+    pData->SetReferenceSample(sampleId, dataOffset, dataLength);
+
+    pPacket->AddData(pData);
+
+    m_bytesThisHint += dataLength;
+    m_bytesThisPacket += dataLength;
+    m_pDmed->IncrementValue(dataLength);
+    m_pTpyl->IncrementValue(dataLength);
+    m_pTrpy->IncrementValue(dataLength);
+}
+
+void MP4RtpHintTrack::AddESConfigurationPacket()
+{
+    if (m_pWriteHint == NULL) {
+        throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    uint8_t* pConfig = NULL;
+    uint32_t configSize = 0;
+
+    m_File.GetTrackESConfiguration(m_pRefTrack->GetId(),
+                                   &pConfig, &configSize);
+
+    if (pConfig == NULL) {
+        return;
+    }
+
+    ASSERT(m_pMaxPacketSizeProperty);
+
+    if (configSize > m_pMaxPacketSizeProperty->GetValue()) {
+        throw new Exception("ES configuration is too large for RTP payload", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    AddPacket(false);
+
+    MP4RtpPacket* pPacket = m_pWriteHint->GetCurrentPacket();
+    ASSERT(pPacket);
+
+    // This is ugly!
+    // To get the ES configuration data somewhere known
+    // we create a sample data reference that points to
+    // this hint track (not the media track)
+    // and this sample of the hint track
+    // the offset into this sample is filled in during the write process
+    MP4RtpSampleData* pData = new MP4RtpSampleData(*pPacket);
+
+    pData->SetEmbeddedImmediate(m_writeSampleId, pConfig, configSize);
+
+    pPacket->AddData(pData);
+
+    m_bytesThisHint += configSize;
+    m_bytesThisPacket += configSize;
+    m_pTpyl->IncrementValue(configSize);
+    m_pTrpy->IncrementValue(configSize);
+}
+
+void MP4RtpHintTrack::WriteHint(MP4Duration duration, bool isSyncSample)
+{
+    if (m_pWriteHint == NULL) {
+        throw new Exception("no hint pending", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    uint8_t* pBytes;
+    uint64_t numBytes;
+
+    m_File.EnableMemoryBuffer();
+
+    m_pWriteHint->Write(m_File);
+
+    m_File.DisableMemoryBuffer(&pBytes, &numBytes);
+
+    WriteSample(pBytes, numBytes, duration, 0, isSyncSample);
+
+    MP4Free(pBytes);
+
+    // update statistics
+    if (m_bytesThisPacket > m_pPmax->GetValue()) {
+        m_pPmax->SetValue(m_bytesThisPacket);
+    }
+
+    if (duration > m_pDmax->GetValue()) {
+        m_pDmax->SetValue(duration);
+    }
+
+    MP4Timestamp startTime;
+
+    GetSampleTimes(m_writeHintId, &startTime, NULL);
+
+    if (startTime < m_thisSec + GetTimeScale()) {
+        m_bytesThisSec += m_bytesThisHint;
+    } else {
+        if (m_bytesThisSec > m_pMaxr->GetValue()) {
+            m_pMaxr->SetValue(m_bytesThisSec);
+        }
+        m_thisSec = startTime - (startTime % GetTimeScale());
+        m_bytesThisSec = m_bytesThisHint;
+    }
+
+    // cleanup
+    delete m_pWriteHint;
+    m_pWriteHint = NULL;
+}
+
+void MP4RtpHintTrack::FinishWrite(uint32_t option)
+{
+    if (m_writeHintId != MP4_INVALID_SAMPLE_ID) {
+        m_pMaxPdu->SetValue(m_pPmax->GetValue());
+        if (m_pNump->GetValue()) {
+            m_pAvgPdu->SetValue(m_pTrpy->GetValue() / m_pNump->GetValue());
+        }
+
+        m_pMaxBitRate->SetValue(m_pMaxr->GetValue() * 8);
+        if (GetDuration()) {
+            m_pAvgBitRate->SetValue(
+                m_pTrpy->GetValue() * 8 * GetTimeScale() / GetDuration());
+        }
+    }
+
+    MP4Track::FinishWrite();
+}
+
+void MP4RtpHintTrack::InitStats()
+{
+    MP4Atom* pHinfAtom = m_trakAtom.FindAtom("trak.udta.hinf");
+
+    ASSERT(pHinfAtom);
+
+    (void)pHinfAtom->FindProperty("hinf.trpy.bytes", (MP4Property**)&m_pTrpy);
+    (void)pHinfAtom->FindProperty("hinf.nump.packets", (MP4Property**)&m_pNump);
+    (void)pHinfAtom->FindProperty("hinf.tpyl.bytes", (MP4Property**)&m_pTpyl);
+    (void)pHinfAtom->FindProperty("hinf.maxr.bytes", (MP4Property**)&m_pMaxr);
+    (void)pHinfAtom->FindProperty("hinf.dmed.bytes", (MP4Property**)&m_pDmed);
+    (void)pHinfAtom->FindProperty("hinf.dimm.bytes", (MP4Property**)&m_pDimm);
+    (void)pHinfAtom->FindProperty("hinf.pmax.bytes", (MP4Property**)&m_pPmax);
+    (void)pHinfAtom->FindProperty("hinf.dmax.milliSecs", (MP4Property**)&m_pDmax);
+
+    MP4Atom* pHmhdAtom = m_trakAtom.FindAtom("trak.mdia.minf.hmhd");
+
+    ASSERT(pHmhdAtom);
+
+    (void)pHmhdAtom->FindProperty("hmhd.maxPduSize", (MP4Property**)&m_pMaxPdu);
+    (void)pHmhdAtom->FindProperty("hmhd.avgPduSize", (MP4Property**)&m_pAvgPdu);
+    (void)pHmhdAtom->FindProperty("hmhd.maxBitRate", (MP4Property**)&m_pMaxBitRate);
+    (void)pHmhdAtom->FindProperty("hmhd.avgBitRate", (MP4Property**)&m_pAvgBitRate);
+
+    MP4Integer32Property* pMaxrPeriod = NULL;
+    (void)pHinfAtom->FindProperty("hinf.maxr.granularity",
+                                  (MP4Property**)&pMaxrPeriod);
+    if (pMaxrPeriod) {
+        pMaxrPeriod->SetValue(1000);    // 1 second
+    }
+}
+
+
+MP4RtpHint::MP4RtpHint(MP4RtpHintTrack& track)
+    : m_track(track)
+{
+    AddProperty( /* 0 */
+        new MP4Integer16Property(this->GetTrack().GetTrakAtom(), "packetCount"));
+    AddProperty( /* 1 */
+        new MP4Integer16Property(this->GetTrack().GetTrakAtom(), "reserved"));
+}
+
+MP4RtpHint::~MP4RtpHint()
+{
+    for (uint32_t i = 0; i < m_rtpPackets.Size(); i++) {
+        delete m_rtpPackets[i];
+    }
+}
+
+MP4RtpPacket* MP4RtpHint::AddPacket()
+{
+    MP4RtpPacket* pPacket = new MP4RtpPacket(*this);
+    m_rtpPackets.Add(pPacket);
+
+    // packetCount property
+    ((MP4Integer16Property*)m_pProperties[0])->IncrementValue();
+
+    pPacket->SetBFrame(m_isBFrame);
+    pPacket->SetTimestampOffset(m_timestampOffset);
+
+    return pPacket;
+}
+
+void MP4RtpHint::Read(MP4File& file)
+{
+    // call base class Read for required properties
+    MP4Container::Read(file);
+
+    uint16_t numPackets =
+        ((MP4Integer16Property*)m_pProperties[0])->GetValue();
+
+    for (uint16_t i = 0; i < numPackets; i++) {
+        MP4RtpPacket* pPacket = new MP4RtpPacket(*this);
+
+        m_rtpPackets.Add(pPacket);
+
+        pPacket->Read(file);
+    }
+
+    if (log.verbosity >= MP4_LOG_VERBOSE1) {
+        log.verbose1f("\"%s\": ReadHint:", GetTrack().GetFile().GetFilename().c_str());
+        Dump(10, false);
+    }
+}
+
+void MP4RtpHint::Write(MP4File& file)
+{
+    uint64_t hintStartPos = file.GetPosition();
+
+    MP4Container::Write(file);
+
+    uint64_t packetStartPos = file.GetPosition();
+
+    uint32_t i;
+
+    // first write out packet (and data) entries
+    for (i = 0; i < m_rtpPackets.Size(); i++) {
+        m_rtpPackets[i]->Write(file);
+    }
+
+    // now let packets write their extra data into the hint sample
+    for (i = 0; i < m_rtpPackets.Size(); i++) {
+        m_rtpPackets[i]->WriteEmbeddedData(file, hintStartPos);
+    }
+
+    uint64_t endPos = file.GetPosition();
+
+    file.SetPosition(packetStartPos);
+
+    // finally rewrite the packet and data entries
+    // which now contain the correct offsets for the embedded data
+    for (i = 0; i < m_rtpPackets.Size(); i++) {
+        m_rtpPackets[i]->Write(file);
+    }
+
+    file.SetPosition(endPos);
+
+    if (log.verbosity >= MP4_LOG_VERBOSE1) {
+        log.verbose1f("\"%s\": WriteRtpHint:", GetTrack().GetFile().GetFilename().c_str());
+        Dump(14, false);
+    }
+}
+
+void MP4RtpHint::Dump(uint8_t indent, bool dumpImplicits)
+{
+    MP4Container::Dump(indent, dumpImplicits);
+
+    for (uint32_t i = 0; i < m_rtpPackets.Size(); i++) {
+        log.dump(indent, MP4_LOG_VERBOSE1,"\"%s\": RtpPacket: %u",
+                 GetTrack().GetFile().GetFilename().c_str(), i);
+        m_rtpPackets[i]->Dump(indent + 1, dumpImplicits);
+    }
+}
+
+MP4RtpPacket::MP4RtpPacket(MP4RtpHint& hint)
+    : m_hint(hint)
+{
+    AddProperty( /* 0 */
+        new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "relativeXmitTime"));
+    AddProperty( /* 1 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "reserved1", 2));
+    AddProperty( /* 2 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "Pbit", 1));
+    AddProperty( /* 3 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "Xbit", 1));
+    AddProperty( /* 4 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "reserved2", 4));
+    AddProperty( /* 5 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "Mbit", 1));
+    AddProperty( /* 6 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "payloadType", 7));
+    AddProperty( /* 7  */
+        new MP4Integer16Property(this->GetHint().GetTrack().GetTrakAtom(), "sequenceNumber"));
+    AddProperty( /* 8 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "reserved3", 13));
+    AddProperty( /* 9 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "extraFlag", 1));
+    AddProperty( /* 10 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "bFrameFlag", 1));
+    AddProperty( /* 11 */
+        new MP4BitfieldProperty(this->GetHint().GetTrack().GetTrakAtom(), "repeatFlag", 1));
+    AddProperty( /* 12 */
+        new MP4Integer16Property(this->GetHint().GetTrack().GetTrakAtom(), "entryCount"));
+}
+
+MP4RtpPacket::~MP4RtpPacket()
+{
+    for (uint32_t i = 0; i < m_rtpData.Size(); i++) {
+        delete m_rtpData[i];
+    }
+}
+
+void MP4RtpPacket::AddExtraProperties()
+{
+    AddProperty( /* 13 */
+        new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "extraInformationLength"));
+
+    // This is a bit of a hack, since the tlv entries are really defined
+    // as atoms but there is only one type defined now, rtpo, and getting
+    // our atom code hooked up here would be a major pain with little gain
+
+    AddProperty( /* 14 */
+        new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "tlvLength"));
+    AddProperty( /* 15 */
+        new MP4StringProperty(this->GetHint().GetTrack().GetTrakAtom(), "tlvType"));
+    AddProperty( /* 16 */
+        new MP4Integer32Property(this->GetHint().GetTrack().GetTrakAtom(), "timestampOffset"));
+
+    ((MP4Integer32Property*)m_pProperties[13])->SetValue(16);
+    ((MP4Integer32Property*)m_pProperties[14])->SetValue(12);
+    ((MP4StringProperty*)m_pProperties[15])->SetFixedLength(4);
+    ((MP4StringProperty*)m_pProperties[15])->SetValue("rtpo");
+}
+
+void MP4RtpPacket::Read(MP4File& file)
+{
+    // call base class Read for required properties
+    MP4Container::Read(file);
+
+    // read extra info if present
+    // we only support the rtpo field!
+    if (((MP4BitfieldProperty*)m_pProperties[9])->GetValue() == 1) {
+        ReadExtra(file);
+    }
+
+    uint16_t numDataEntries =
+        ((MP4Integer16Property*)m_pProperties[12])->GetValue();
+
+    // read data entries
+    for (uint16_t i = 0; i < numDataEntries; i++) {
+        uint8_t dataType;
+        file.PeekBytes(&dataType, 1);
+
+        MP4RtpData* pData;
+
+        switch (dataType) {
+        case 0:
+            pData = new MP4RtpNullData(*this);
+            break;
+        case 1:
+            pData = new MP4RtpImmediateData(*this);
+            break;
+        case 2:
+            pData = new MP4RtpSampleData(*this);
+            break;
+        case 3:
+            pData = new MP4RtpSampleDescriptionData(*this);
+            break;
+        default:
+            throw new Exception("unknown packet data entry type", __FILE__, __LINE__, __FUNCTION__ );
+        }
+
+        m_rtpData.Add(pData);
+
+        // read data entry's properties
+        pData->Read(file);
+    }
+}
+
+void MP4RtpPacket::ReadExtra(MP4File& file)
+{
+    AddExtraProperties();
+
+    int32_t extraLength = (int32_t)file.ReadUInt32();
+
+    if (extraLength < 4) {
+        throw new Exception("bad packet extra info length", __FILE__, __LINE__, __FUNCTION__ );
+    }
+    extraLength -= 4;
+
+    while (extraLength > 0) {
+        uint32_t entryLength = file.ReadUInt32();
+        uint32_t entryTag = file.ReadUInt32();
+
+        if (entryLength < 8) {
+            throw new Exception("bad packet extra info entry length", __FILE__, __LINE__, __FUNCTION__ );
+        }
+
+        if (entryTag == STRTOINT32("rtpo") && entryLength == 12) {
+            // read the rtp timestamp offset
+            m_pProperties[16]->Read(file);
+        } else {
+            // ignore it, LATER carry it along
+            file.SetPosition(file.GetPosition() + entryLength - 8);
+        }
+
+        extraLength -= entryLength;
+    }
+
+    if (extraLength < 0) {
+        throw new Exception("invalid packet extra info length", __FILE__, __LINE__, __FUNCTION__ );
+    }
+}
+
+void MP4RtpPacket::Set(uint8_t payloadNumber,
+                       uint32_t packetId, bool setMbit)
+{
+    ((MP4BitfieldProperty*)m_pProperties[5])->SetValue(setMbit);
+    ((MP4BitfieldProperty*)m_pProperties[6])->SetValue(payloadNumber);
+    ((MP4Integer16Property*)m_pProperties[7])->SetValue(packetId);
+}
+
+int32_t MP4RtpPacket::GetTransmitOffset()
+{
+    return ((MP4Integer32Property*)m_pProperties[0])->GetValue();
+}
+
+void MP4RtpPacket::SetTransmitOffset(int32_t transmitOffset)
+{
+    ((MP4Integer32Property*)m_pProperties[0])->SetValue(transmitOffset);
+}
+
+bool MP4RtpPacket::GetPBit()
+{
+    return ((MP4BitfieldProperty*)m_pProperties[2])->GetValue();
+}
+
+bool MP4RtpPacket::GetXBit()
+{
+    return ((MP4BitfieldProperty*)m_pProperties[3])->GetValue();
+}
+
+bool MP4RtpPacket::GetMBit()
+{
+    return ((MP4BitfieldProperty*)m_pProperties[5])->GetValue();
+}
+
+uint8_t MP4RtpPacket::GetPayload()
+{
+    return ((MP4BitfieldProperty*)m_pProperties[6])->GetValue();
+}
+
+uint16_t MP4RtpPacket::GetSequenceNumber()
+{
+    return ((MP4Integer16Property*)m_pProperties[7])->GetValue();
+}
+
+bool MP4RtpPacket::IsBFrame()
+{
+    return ((MP4BitfieldProperty*)m_pProperties[10])->GetValue();
+}
+
+void MP4RtpPacket::SetBFrame(bool isBFrame)
+{
+    ((MP4BitfieldProperty*)m_pProperties[10])->SetValue(isBFrame);
+}
+
+void MP4RtpPacket::SetTimestampOffset(uint32_t timestampOffset)
+{
+    if (timestampOffset == 0) {
+        return;
+    }
+
+    ASSERT(((MP4BitfieldProperty*)m_pProperties[9])->GetValue() == 0);
+
+    // set X bit
+    ((MP4BitfieldProperty*)m_pProperties[9])->SetValue(1);
+
+    AddExtraProperties();
+
+    ((MP4Integer32Property*)m_pProperties[16])->SetValue(timestampOffset);
+}
+
+void MP4RtpPacket::AddData(MP4RtpData* pData)
+{
+    m_rtpData.Add(pData);
+
+    // increment entry count property
+    ((MP4Integer16Property*)m_pProperties[12])->IncrementValue();
+}
+
+uint32_t MP4RtpPacket::GetDataSize()
+{
+    uint32_t totalDataSize = 0;
+
+    for (uint32_t i = 0; i < m_rtpData.Size(); i++) {
+        totalDataSize += m_rtpData[i]->GetDataSize();
+    }
+
+    return totalDataSize;
+}
+
+void MP4RtpPacket::GetData(uint8_t* pDest)
+{
+    for (uint32_t i = 0; i < m_rtpData.Size(); i++) {
+        m_rtpData[i]->GetData(pDest);
+        pDest += m_rtpData[i]->GetDataSize();
+    }
+}
+
+void MP4RtpPacket::Write(MP4File& file)
+{
+    MP4Container::Write(file);
+
+    for (uint32_t i = 0; i < m_rtpData.Size(); i++) {
+        m_rtpData[i]->Write(file);
+    }
+}
+
+void MP4RtpPacket::WriteEmbeddedData(MP4File& file, uint64_t startPos)
+{
+    for (uint32_t i = 0; i < m_rtpData.Size(); i++) {
+        m_rtpData[i]->WriteEmbeddedData(file, startPos);
+    }
+}
+
+void MP4RtpPacket::Dump(uint8_t indent, bool dumpImplicits)
+{
+    MP4Container::Dump(indent, dumpImplicits);
+
+    for (uint32_t i = 0; i < m_rtpData.Size(); i++) {
+        log.dump(indent, MP4_LOG_VERBOSE1, "\"%s\": RtpData: %u",
+                 GetHint().GetTrack().GetFile().GetFilename().c_str(), i);
+        m_rtpData[i]->Dump(indent + 1, dumpImplicits);
+    }
+}
+
+MP4RtpData::MP4RtpData(MP4RtpPacket& packet)
+    : m_packet(packet)
+{
+    AddProperty( /* 0 */
+        new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "type"));
+}
+
+MP4Track* MP4RtpData::FindTrackFromRefIndex(uint8_t refIndex)
+{
+    MP4Track* pTrack;
+
+    if (refIndex == (uint8_t)-1) {
+        // ourselves
+        pTrack = &GetPacket().GetHint().GetTrack();
+    } else if (refIndex == 0) {
+        // our reference track
+        pTrack = GetPacket().GetHint().GetTrack().GetRefTrack();
+    } else {
+        // some other track
+        MP4RtpHintTrack* pHintTrack =
+            &GetPacket().GetHint().GetTrack();
+
+        MP4Atom& trakAtom = pHintTrack->GetTrakAtom();
+
+        MP4Integer32Property* pTrackIdProperty = NULL;
+        (void)trakAtom.FindProperty(
+            "trak.tref.hint.entries",
+            (MP4Property**)&pTrackIdProperty);
+        ASSERT(pTrackIdProperty);
+
+        uint32_t refTrackId =
+            pTrackIdProperty->GetValue(refIndex - 1);
+
+        pTrack = pHintTrack->GetFile().GetTrack(refTrackId);
+    }
+
+    return pTrack;
+}
+
+MP4RtpNullData::MP4RtpNullData(MP4RtpPacket& packet)
+        : MP4RtpData(packet)
+{
+    ((MP4Integer8Property*)m_pProperties[0])->SetValue(0);
+
+    AddProperty( /* 1 */
+        new MP4BytesProperty(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "pad", 15));
+
+    ((MP4BytesProperty*)m_pProperties[1])->SetFixedSize(15);
+}
+
+MP4RtpImmediateData::MP4RtpImmediateData(MP4RtpPacket& packet)
+        : MP4RtpData(packet)
+{
+    ((MP4Integer8Property*)m_pProperties[0])->SetValue(1);
+
+    AddProperty( /* 1 */
+        new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "count"));
+    AddProperty( /* 2 */
+        new MP4BytesProperty(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "data", 14));
+
+    ((MP4BytesProperty*)m_pProperties[2])->SetFixedSize(14);
+}
+
+void MP4RtpImmediateData::Set(const uint8_t* pBytes, uint8_t numBytes)
+{
+    ((MP4Integer8Property*)m_pProperties[1])->SetValue(numBytes);
+    ((MP4BytesProperty*)m_pProperties[2])->SetValue(pBytes, numBytes);
+}
+
+uint16_t MP4RtpImmediateData::GetDataSize()
+{
+    return ((MP4Integer8Property*)m_pProperties[1])->GetValue();
+}
+
+void MP4RtpImmediateData::GetData(uint8_t* pDest)
+{
+    uint8_t* pValue;
+    uint32_t valueSize;
+    ((MP4BytesProperty*)m_pProperties[2])->GetValue(&pValue, &valueSize);
+
+    memcpy(pDest, pValue, GetDataSize());
+    MP4Free(pValue);
+}
+
+MP4RtpSampleData::MP4RtpSampleData(MP4RtpPacket& packet)
+        : MP4RtpData(packet)
+{
+    ((MP4Integer8Property*)m_pProperties[0])->SetValue(2);
+
+    AddProperty( /* 1 */
+        new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "trackRefIndex"));
+    AddProperty( /* 2 */
+        new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "length"));
+    AddProperty( /* 3 */
+        new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleNumber"));
+    AddProperty( /* 4 */
+        new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleOffset"));
+    AddProperty( /* 5 */
+        new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "bytesPerBlock"));
+    AddProperty( /* 6 */
+        new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "samplesPerBlock"));
+
+    ((MP4Integer16Property*)m_pProperties[5])->SetValue(1);
+    ((MP4Integer16Property*)m_pProperties[6])->SetValue(1);
+
+    m_pRefData = NULL;
+    m_pRefTrack = NULL;
+    m_refSampleId = MP4_INVALID_SAMPLE_ID;
+    m_refSampleOffset = 0;
+}
+
+void MP4RtpSampleData::SetEmbeddedImmediate(MP4SampleId sampleId,
+        uint8_t* pData, uint16_t dataLength)
+{
+    ((MP4Integer8Property*)m_pProperties[1])->SetValue((uint8_t)-1);
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(dataLength);
+    ((MP4Integer32Property*)m_pProperties[3])->SetValue(sampleId);
+    ((MP4Integer32Property*)m_pProperties[4])->SetValue(0);
+    CHECK_AND_FREE(m_pRefData);
+    m_pRefData = pData;
+}
+
+void MP4RtpSampleData::SetReferenceSample(
+    MP4SampleId refSampleId, uint32_t refSampleOffset,
+    uint16_t sampleLength)
+{
+    ((MP4Integer8Property*)m_pProperties[1])->SetValue(0);
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(sampleLength);
+    ((MP4Integer32Property*)m_pProperties[3])->SetValue(refSampleId);
+    ((MP4Integer32Property*)m_pProperties[4])->SetValue(refSampleOffset);
+}
+
+void MP4RtpSampleData::SetEmbeddedSample(
+    MP4SampleId sampleId, MP4Track* pRefTrack,
+    MP4SampleId refSampleId, uint32_t refSampleOffset,
+    uint16_t sampleLength)
+{
+    ((MP4Integer8Property*)m_pProperties[1])->SetValue((uint8_t)-1);
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(sampleLength);
+    ((MP4Integer32Property*)m_pProperties[3])->SetValue(sampleId);
+    ((MP4Integer32Property*)m_pProperties[4])->SetValue(0);
+    m_pRefTrack = pRefTrack;
+    m_refSampleId = refSampleId;
+    m_refSampleOffset = refSampleOffset;
+}
+
+uint16_t MP4RtpSampleData::GetDataSize()
+{
+    return ((MP4Integer16Property*)m_pProperties[2])->GetValue();
+}
+
+void MP4RtpSampleData::GetData(uint8_t* pDest)
+{
+    uint8_t trackRefIndex =
+        ((MP4Integer8Property*)m_pProperties[1])->GetValue();
+
+    MP4Track* pSampleTrack =
+        FindTrackFromRefIndex(trackRefIndex);
+
+    pSampleTrack->ReadSampleFragment(
+        ((MP4Integer32Property*)m_pProperties[3])->GetValue(),  // sampleId
+        ((MP4Integer32Property*)m_pProperties[4])->GetValue(),  // sampleOffset
+        ((MP4Integer16Property*)m_pProperties[2])->GetValue(),  // sampleLength
+        pDest);
+}
+
+void MP4RtpSampleData::WriteEmbeddedData(MP4File& file, uint64_t startPos)
+{
+    // if not using embedded data, nothing to do
+    if (((MP4Integer8Property*)m_pProperties[1])->GetValue() != (uint8_t)-1) {
+        return;
+    }
+
+    // figure out the offset within this hint sample for this embedded data
+    uint64_t offset = file.GetPosition() - startPos;
+    ASSERT(offset <= 0xFFFFFFFF);
+    ((MP4Integer32Property*)m_pProperties[4])->SetValue((uint32_t)offset);
+
+    uint16_t length = ((MP4Integer16Property*)m_pProperties[2])->GetValue();
+
+    if (m_pRefData) {
+        file.WriteBytes(m_pRefData, length);
+        return;
+    }
+
+    if (m_refSampleId != MP4_INVALID_SAMPLE_ID) {
+        uint8_t* pSample = NULL;
+        uint32_t sampleSize = 0;
+
+        ASSERT(m_pRefTrack);
+        m_pRefTrack->ReadSample(m_refSampleId, &pSample, &sampleSize);
+
+        ASSERT(m_refSampleOffset + length <= sampleSize);
+
+        file.WriteBytes(&pSample[m_refSampleOffset], length);
+
+        MP4Free(pSample);
+        return;
+    }
+}
+
+MP4RtpSampleDescriptionData::MP4RtpSampleDescriptionData(MP4RtpPacket& packet)
+        : MP4RtpData(packet)
+{
+    ((MP4Integer8Property*)m_pProperties[0])->SetValue(3);
+
+    AddProperty( /* 1 */
+        new MP4Integer8Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "trackRefIndex"));
+    AddProperty( /* 2 */
+        new MP4Integer16Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "length"));
+    AddProperty( /* 3 */
+        new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleDescriptionIndex"));
+    AddProperty( /* 4 */
+        new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "sampleDescriptionOffset"));
+    AddProperty( /* 5 */
+        new MP4Integer32Property(this->GetPacket().GetHint().GetTrack().GetTrakAtom(), "reserved"));
+}
+
+void MP4RtpSampleDescriptionData::Set(uint32_t sampleDescrIndex,
+                                      uint32_t offset, uint16_t length)
+{
+    ((MP4Integer16Property*)m_pProperties[2])->SetValue(length);
+    ((MP4Integer32Property*)m_pProperties[3])->SetValue(sampleDescrIndex);
+    ((MP4Integer32Property*)m_pProperties[4])->SetValue(offset);
+}
+
+uint16_t MP4RtpSampleDescriptionData::GetDataSize()
+{
+    return ((MP4Integer16Property*)m_pProperties[2])->GetValue();
+}
+
+void MP4RtpSampleDescriptionData::GetData(uint8_t* pDest)
+{
+    // we start with the index into our track references
+    uint8_t trackRefIndex =
+        ((MP4Integer8Property*)m_pProperties[1])->GetValue();
+
+    // from which we can find the track structure
+    MP4Track* pSampleTrack =
+        FindTrackFromRefIndex(trackRefIndex);
+
+    // next find the desired atom in the track's sample description table
+    uint32_t sampleDescrIndex =
+        ((MP4Integer32Property*)m_pProperties[3])->GetValue();
+
+    MP4Atom& trakAtom =
+        pSampleTrack->GetTrakAtom();
+
+    char sdName[64];
+    snprintf(sdName, 64, "trak.mdia.minf.stbl.stsd.*[%u]", sampleDescrIndex);
+
+    MP4Atom* pSdAtom =
+        trakAtom.FindAtom(sdName);
+
+    // bad reference
+    if (pSdAtom == NULL) {
+        throw new Exception("invalid sample description index", __FILE__, __LINE__, __FUNCTION__ );
+    }
+
+    // check validity of the upcoming copy
+    uint16_t length =
+        ((MP4Integer16Property*)m_pProperties[2])->GetValue();
+    uint32_t offset =
+        ((MP4Integer32Property*)m_pProperties[4])->GetValue();
+
+    if (offset + length > pSdAtom->GetSize()) {
+        throw new Exception("offset and/or length are too large",
+                            __FILE__, __LINE__, __FUNCTION__);
+    }
+
+    // now we use the raw file to get the desired bytes
+
+    MP4File& file = GetPacket().GetHint().GetTrack().GetFile();
+
+    uint64_t orgPos = file.GetPosition();
+
+    // It's not entirely clear from the spec whether the offset is from
+    // the start of the sample descirption atom, or the start of the atom's
+    // data. I believe it is the former, but the commented out code will
+    // realize the latter interpretation if I turn out to be wrong.
+    uint64_t dataPos = pSdAtom->GetStart();
+    //uint64_t dataPos = pSdAtom->GetEnd() - pSdAtom->GetSize();
+
+    file.SetPosition(dataPos + offset);
+
+    file.ReadBytes(pDest, length);
+
+    file.SetPosition(orgPos);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
diff --git a/src/rtphint.h b/src/rtphint.h
new file mode 100644
index 0000000..a2a4fa8
--- /dev/null
+++ b/src/rtphint.h
@@ -0,0 +1,362 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+#ifndef MP4V2_IMPL_RTPHINT_H
+#define MP4V2_IMPL_RTPHINT_H
+
+namespace mp4v2 {
+namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+// forward declarations
+class MP4RtpHintTrack;
+class MP4RtpHint;
+class MP4RtpPacket;
+
+class MP4RtpData : public MP4Container {
+public:
+    MP4RtpData(MP4RtpPacket& packet);
+
+    MP4RtpPacket& GetPacket() {
+        return m_packet;
+    }
+
+    virtual uint16_t GetDataSize() = 0;
+    virtual void GetData(uint8_t* pDest) = 0;
+
+    MP4Track* FindTrackFromRefIndex(uint8_t refIndex);
+
+    virtual void WriteEmbeddedData(MP4File& file, uint64_t startPos) {
+        // default is no-op
+    }
+
+protected:
+    MP4RtpPacket& m_packet;
+};
+
+MP4ARRAY_DECL(MP4RtpData, MP4RtpData*)
+
+class MP4RtpNullData : public MP4RtpData {
+public:
+    MP4RtpNullData(MP4RtpPacket& packet);
+
+    uint16_t GetDataSize() {
+        return 0;
+    }
+
+    void GetData(uint8_t* pDest) {
+        // no-op
+    }
+};
+
+class MP4RtpImmediateData : public MP4RtpData {
+public:
+    MP4RtpImmediateData(MP4RtpPacket& packet);
+
+    void Set(const uint8_t* pBytes, uint8_t numBytes);
+
+    uint16_t GetDataSize();
+
+    void GetData(uint8_t* pDest);
+};
+
+class MP4RtpSampleData : public MP4RtpData {
+public:
+    MP4RtpSampleData(MP4RtpPacket& packet);
+
+    ~MP4RtpSampleData(void) {
+        CHECK_AND_FREE(m_pRefData);
+    };
+
+    void SetEmbeddedImmediate(
+        MP4SampleId sampleId,
+        uint8_t* pData, uint16_t dataLength);
+
+    void SetReferenceSample(
+        MP4SampleId refSampleId, uint32_t refSampleOffset,
+        uint16_t sampleLength);
+
+    void SetEmbeddedSample(
+        MP4SampleId sampleId, MP4Track* pRefTrack,
+        MP4SampleId refSampleId, uint32_t refSampleOffset,
+        uint16_t sampleLength);
+
+    uint16_t GetDataSize();
+
+    void GetData(uint8_t* pDest);
+
+    void WriteEmbeddedData(MP4File& file, uint64_t startPos);
+
+protected:
+    uint8_t*        m_pRefData;
+
+    MP4Track*       m_pRefTrack;
+    MP4SampleId     m_refSampleId;
+    uint32_t        m_refSampleOffset;
+};
+
+class MP4RtpSampleDescriptionData : public MP4RtpData {
+public:
+    MP4RtpSampleDescriptionData(MP4RtpPacket& packet);
+
+    void Set(uint32_t sampleDescrIndex,
+             uint32_t offset, uint16_t length);
+
+    uint16_t GetDataSize();
+
+    void GetData(uint8_t* pDest);
+};
+
+class MP4RtpPacket : public MP4Container {
+public:
+    MP4RtpPacket(MP4RtpHint& hint);
+
+    ~MP4RtpPacket();
+
+    void AddExtraProperties();
+
+    MP4RtpHint& GetHint() {
+        return m_hint;
+    }
+
+    void Set(uint8_t payloadNumber, uint32_t packetId, bool setMbit);
+
+    int32_t GetTransmitOffset();
+
+    bool GetPBit();
+
+    bool GetXBit();
+
+    bool GetMBit();
+
+    uint8_t GetPayload();
+
+    uint16_t GetSequenceNumber();
+
+    void SetTransmitOffset(int32_t transmitOffset);
+
+    bool IsBFrame();
+
+    void SetBFrame(bool isBFrame);
+
+    void SetTimestampOffset(uint32_t timestampOffset);
+
+    void AddData(MP4RtpData* pData);
+
+    uint32_t GetDataSize();
+
+    void GetData(uint8_t* pDest);
+
+    void Read(MP4File& file);
+
+    void ReadExtra(MP4File& file);
+
+    void Write(MP4File& file);
+
+    void WriteEmbeddedData(MP4File& file, uint64_t startPos);
+
+    void Dump(uint8_t indent, bool dumpImplicits);
+
+protected:
+    MP4RtpHint&         m_hint;
+    MP4RtpDataArray     m_rtpData;
+};
+
+MP4ARRAY_DECL(MP4RtpPacket, MP4RtpPacket*)
+
+class MP4RtpHint : public MP4Container {
+public:
+    MP4RtpHint(MP4RtpHintTrack& track);
+
+    ~MP4RtpHint();
+
+    MP4RtpHintTrack& GetTrack() {
+        return m_track;
+    }
+
+    uint16_t GetNumberOfPackets() {
+        return m_rtpPackets.Size();
+    }
+
+    bool IsBFrame() {
+        return m_isBFrame;
+    }
+    void SetBFrame(bool isBFrame) {
+        m_isBFrame = isBFrame;
+    }
+
+    uint32_t GetTimestampOffset() {
+        return m_timestampOffset;
+    }
+    void SetTimestampOffset(uint32_t timestampOffset) {
+        m_timestampOffset = timestampOffset;
+    }
+
+    MP4RtpPacket* AddPacket();
+
+    MP4RtpPacket* GetPacket(uint16_t index) {
+        return m_rtpPackets[index];
+    }
+
+    MP4RtpPacket* GetCurrentPacket() {
+        if (m_rtpPackets.Size() == 0) {
+            return NULL;
+        }
+        return m_rtpPackets[m_rtpPackets.Size() - 1];
+    }
+
+    void Read(MP4File& file);
+
+    void Write(MP4File& file);
+
+    void Dump(uint8_t indent, bool dumpImplicits);
+
+protected:
+    MP4RtpHintTrack&    m_track;
+    MP4RtpPacketArray   m_rtpPackets;
+
+    // values when adding packets to a hint (write mode)
+    bool                m_isBFrame;
+    uint32_t            m_timestampOffset;
+};
+
+class MP4RtpHintTrack : public MP4Track {
+public:
+    MP4RtpHintTrack(MP4File& file, MP4Atom& trakAtom);
+
+    ~MP4RtpHintTrack();
+
+    void InitRefTrack();
+
+    void InitPayload();
+
+    void InitRtpStart();
+
+    void InitStats();
+
+    MP4Track* GetRefTrack() {
+        InitRefTrack();
+        return m_pRefTrack;
+    }
+
+    void GetPayload(
+        char** ppPayloadName = NULL,
+        uint8_t* pPayloadNumber = NULL,
+        uint16_t* pMaxPayloadSize = NULL,
+        char **ppEncodingParams = NULL);
+
+    void SetPayload(
+        const char* payloadName,
+        uint8_t payloadNumber,
+        uint16_t maxPayloadSize,
+        const char *encoding_parms,
+        bool add_rtpmap,
+        bool add_mpeg4_esid);
+
+    void ReadHint(
+        MP4SampleId hintSampleId,
+        uint16_t* pNumPackets = NULL);
+
+    uint16_t GetHintNumberOfPackets();
+
+    bool GetPacketBFrame(uint16_t packetIndex);
+
+    uint16_t GetPacketTransmitOffset(uint16_t packetIndex);
+
+    void ReadPacket(
+        uint16_t packetIndex,
+        uint8_t** ppBytes,
+        uint32_t* pNumBytes,
+        uint32_t ssrc,
+        bool includeHeader = true,
+        bool includePayload = true);
+
+    MP4Timestamp GetRtpTimestampStart();
+
+    void SetRtpTimestampStart(MP4Timestamp start);
+
+    void AddHint(bool isBFrame, uint32_t timestampOffset);
+
+    void AddPacket(bool setMbit, int32_t transmitOffset = 0);
+
+    void AddImmediateData(const uint8_t* pBytes, uint32_t numBytes);
+
+    void AddSampleData(MP4SampleId sampleId,
+                       uint32_t dataOffset, uint32_t dataLength);
+
+    void AddESConfigurationPacket();
+
+    void WriteHint(MP4Duration duration, bool isSyncSample);
+
+    void FinishWrite(uint32_t options = 0);
+
+protected:
+    MP4Track*   m_pRefTrack;
+
+    MP4StringProperty*      m_pRtpMapProperty;
+    MP4Integer32Property*   m_pPayloadNumberProperty;
+    MP4Integer32Property*   m_pMaxPacketSizeProperty;
+    MP4Integer32Property*   m_pSnroProperty;
+    MP4Integer32Property*   m_pTsroProperty;
+    uint32_t                m_rtpSequenceStart;
+    uint32_t                m_rtpTimestampStart;
+
+    // reading
+    MP4RtpHint* m_pReadHint;
+    uint8_t*    m_pReadHintSample;
+    uint32_t    m_readHintSampleSize;
+    MP4Timestamp m_readHintTimestamp;
+
+    // writing
+    MP4RtpHint* m_pWriteHint;
+    MP4SampleId m_writeHintId;
+    uint32_t    m_writePacketId;
+
+    // statistics
+    // in trak.udta.hinf
+    MP4Integer64Property*   m_pTrpy;
+    MP4Integer64Property*   m_pNump;
+    MP4Integer64Property*   m_pTpyl;
+    MP4Integer32Property*   m_pMaxr;
+    MP4Integer64Property*   m_pDmed;
+    MP4Integer64Property*   m_pDimm;
+    MP4Integer32Property*   m_pPmax;
+    MP4Integer32Property*   m_pDmax;
+
+    // in trak.mdia.minf.hmhd
+    MP4Integer16Property*   m_pMaxPdu;
+    MP4Integer16Property*   m_pAvgPdu;
+    MP4Integer32Property*   m_pMaxBitRate;
+    MP4Integer32Property*   m_pAvgBitRate;
+
+    MP4Timestamp            m_thisSec;
+    uint32_t                m_bytesThisSec;
+    uint32_t                m_bytesThisHint;
+    uint32_t                m_bytesThisPacket;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}
+} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_RTPHINT_H
diff --git a/src/src.h b/src/src.h
new file mode 100644
index 0000000..472d8a0
--- /dev/null
+++ b/src/src.h
@@ -0,0 +1,52 @@
+#ifndef MP4V2_IMPL_SRC_H
+#define MP4V2_IMPL_SRC_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libplatform/platform.h"
+#include <mp4v2/mp4v2.h>
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace mp4v2 { namespace impl {
+    using namespace mp4v2::platform;
+    using io::File;
+    using io::FileSystem;
+}} // namspace mp4v2::impl
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "text.h"
+#include "enum.h"
+#include "exception.h"
+
+#include "bmff/typebmff.h"
+#include "itmf/type.h"
+
+#include "util.h"
+#include "log.h"
+#include "mp4util.h"
+#include "mp4array.h"
+#include "mp4track.h"
+#include "mp4file.h"
+#include "mp4property.h"
+#include "mp4container.h"
+
+#include "mp4atom.h"
+#include "atoms.h"
+
+#include "bmff/bmff.h"
+#include "itmf/itmf.h"
+#include "qtff/qtff.h"
+
+#include "mp4descriptor.h"
+#include "descriptors.h"
+#include "ocidescriptors.h"
+
+#include "qosqualifiers.h"
+#include "odcommands.h"
+#include "rtphint.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_IMPL_SRC_H
diff --git a/src/text.cpp b/src/text.cpp
new file mode 100644
index 0000000..5790f96
--- /dev/null
+++ b/src/text.cpp
@@ -0,0 +1,41 @@
+#include "src/impl.h"
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+LessIgnoreCase::operator()( const string& xstr, const string& ystr ) const
+{
+    const string::size_type xlen = xstr.length();
+    const string::size_type ylen = ystr.length();
+
+    if( xlen < ylen ) {
+        for( string::size_type i = 0; i < xlen; i++ ) {
+            const char x = std::toupper( xstr[i] );
+            const char y = std::toupper( ystr[i] );
+
+            if( x < y )
+                return true;
+            else if ( x > y )
+                return false;
+        }
+        return true;
+    }
+    else {
+        for( string::size_type i = 0; i < ylen; i++ ) {
+            const char x = std::toupper( xstr[i] );
+            const char y = std::toupper( ystr[i] );
+
+            if( x < y )
+                return true;
+            else if ( x > y )
+                return false;
+        }
+        return false;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
diff --git a/src/text.h b/src/text.h
new file mode 100644
index 0000000..4afd336
--- /dev/null
+++ b/src/text.h
@@ -0,0 +1,17 @@
+#ifndef MP4V2_IMPL_TEXT_H
+#define MP4V2_IMPL_TEXT_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct MP4V2_EXPORT LessIgnoreCase : less<string>
+{
+    bool operator()( const string&, const string& ) const;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_TEXT_H
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..aa3aba6
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,82 @@
+#ifndef MP4V2_IMPL_UTIL_H
+#define MP4V2_IMPL_UTIL_H
+
+namespace mp4v2 { namespace impl {
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline int8_t max( int8_t a, int8_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+inline int16_t max( int16_t a, int16_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+inline int32_t max( int32_t a, int32_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+inline int64_t max( int64_t a, int64_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline uint8_t max( uint8_t  a, uint8_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+inline uint16_t max( uint16_t a, uint16_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+inline uint32_t max( uint32_t a, uint32_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+inline uint64_t max( uint64_t a, uint64_t b ) {
+    return ( a > b ) ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline int8_t min( int8_t  a, int8_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+inline int16_t min( int16_t a, int16_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+inline int32_t min( int32_t a, int32_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+inline int64_t min( int64_t a, int64_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+inline uint8_t min( uint8_t  a, uint8_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+inline uint16_t min( uint16_t a, uint16_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+inline uint32_t min( uint32_t a, uint32_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+inline uint64_t min( uint64_t a, uint64_t b ) {
+    return ( a < b ) ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::impl
+
+#endif // MP4V2_IMPL_UTIL_H
diff --git a/test/OLD/c_api.c b/test/OLD/c_api.c
new file mode 100644
index 0000000..0b417ce
--- /dev/null
+++ b/test/OLD/c_api.c
@@ -0,0 +1,62 @@
+/*
+ * 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.  All Rights Reserved.
+ * 
+ * Contributor(s): 
+ *		Dave Mackie		dmackie@cisco.com
+ */
+
+#include "mp4.h"
+
+main(int argc, char** argv)
+{
+#if 0
+	MP4FileHandle mp4File = MP4Read(argv[1], MP4_DETAILS_ERROR);
+
+	if (!mp4File) {
+		exit(1);
+	}
+
+	MP4Dump(mp4File, stdout, 0);
+
+	MP4Close(mp4File);
+
+	exit(0);
+#else
+	MP4FileHandle mp4File;
+
+	mp4File = MP4Read(argv[1], MP4_DETAILS_ERROR);
+	MP4SetVerbosity(mp4File, MP4_DETAILS_ALL);
+	if (MP4HaveAtom(mp4File, "moov.mbhd")) {
+	  printf("found moov.mvhd\n");
+	}
+	if (MP4HaveTrackAtom(mp4File, 1, argv[2])) {
+	  printf("found %s\n", argv[2]);
+	} else {
+	  printf("didn't find it\n");
+	}
+	    
+#if 0
+	u_int8_t* data=(u_int8_t*)"this is my tag data";
+	u_int32_t len=strlen((char*)data);
+	MP4SetMetadataFreeForm(mp4File, "mytag1",data,len);
+	MP4SetMetadataFreeForm(mp4File, "my_tag2",data,len);
+#endif
+	MP4Close(mp4File);
+	exit(0);
+#endif
+}
+
diff --git a/test/OLD/mp4clip.cpp b/test/OLD/mp4clip.cpp
new file mode 100644
index 0000000..28a9b1a
--- /dev/null
+++ b/test/OLD/mp4clip.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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-2002.  All Rights Reserved.
+ * 
+ * Contributor(s): 
+ *		Dave Mackie		dmackie@cisco.com
+ */
+
+// N.B. mp4clips clips tracks in an mp4 file
+// without regard to sync samples aka "key frames"
+// as a testing app the burden is on the user to choose
+// an appropriate clip start time
+
+#include "mp4.h"
+#include "mpeg4ip_getopt.h"
+
+char* ProgName;
+
+// forward declaration
+void ClipTrack(
+	MP4FileHandle srcFile, 
+	MP4TrackId trackId, 
+	MP4FileHandle dstFile,
+	float startTime,
+	float duration);
+
+
+int main(int argc, char** argv)
+{
+	char* usageString = 
+		"usage: %s [-t <track-id>] [-v [<level>]] [-s <start>] -l <duration> <file-name>\n";
+	char* srcFileName = NULL;
+	char* dstFileName = NULL;
+	MP4TrackId trackId = MP4_INVALID_TRACK_ID;
+	u_int32_t verbosity = MP4_DETAILS_ERROR;
+	float startTime = 0.0;
+	float duration = 0.0;
+
+	/* begin processing command line */
+	ProgName = argv[0];
+	while (true) {
+		int c = -1;
+		int option_index = 0;
+		static struct option long_options[] = {
+			{ "length", 1, 0, 'l' },
+			{ "start", 1, 0, 's' },
+			{ "track", 1, 0, 't' },
+			{ "verbose", 2, 0, 'v' },
+			{ "version", 0, 0, 'V' },
+			{ NULL, 0, 0, 0 }
+		};
+
+		c = getopt_long_only(argc, argv, "l:s:t:v::V",
+			long_options, &option_index);
+
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'l':
+			if (sscanf(optarg, "%f", &duration) != 1) {
+				fprintf(stderr, 
+					"%s: bad length specified: %s\n",
+					 ProgName, optarg);
+			}
+			break;
+		case 's':
+			if (sscanf(optarg, "%f", &startTime) != 1) {
+				fprintf(stderr, 
+					"%s: bad start time specified: %s\n",
+					 ProgName, optarg);
+			}
+			break;
+		case 't':
+			if (sscanf(optarg, "%u", &trackId) != 1) {
+				fprintf(stderr, 
+					"%s: bad track-id specified: %s\n",
+					 ProgName, optarg);
+				exit(1);
+			}
+			break;
+		case 'v':
+			verbosity |= MP4_DETAILS_READ;
+			if (optarg) {
+				u_int32_t level;
+				if (sscanf(optarg, "%u", &level) == 1) {
+					if (level >= 2) {
+						verbosity |= MP4_DETAILS_TABLE;
+					} 
+					if (level >= 3) {
+						verbosity |= MP4_DETAILS_SAMPLE;
+					} 
+					if (level >= 4) {
+						verbosity = MP4_DETAILS_ALL;
+					}
+				}
+			}
+			break;
+		case '?':
+			fprintf(stderr, usageString, ProgName);
+			exit(0);
+		case 'V':
+		  fprintf(stderr, "%s - %s version %s\n", 
+			  ProgName, MPEG4IP_PACKAGE, MPEG4IP_VERSION);
+		  exit(0);
+		default:
+			fprintf(stderr, "%s: unknown option specified, ignoring: %c\n", 
+				ProgName, c);
+		}
+	}
+
+	/* check that we have at least one non-option argument */
+	if ((argc - optind) < 1) {
+		fprintf(stderr, usageString, ProgName);
+		exit(1);
+	}
+	
+	if (verbosity) {
+		fprintf(stderr, "%s version %s\n", ProgName, MPEG4IP_VERSION);
+	}
+
+	/* point to the specified file name */
+	srcFileName = argv[optind++];
+
+	/* get dest file name */
+	if ((argc - optind) > 0) {
+		dstFileName = argv[optind++];
+	}
+
+	/* warn about extraneous non-option arguments */
+	if (optind < argc) {
+		fprintf(stderr, "%s: unknown options specified, ignoring: ", ProgName);
+		while (optind < argc) {
+			fprintf(stderr, "%s ", argv[optind++]);
+		}
+		fprintf(stderr, "\n");
+	}
+
+	if (duration == 0.0) {
+		fprintf(stderr,
+			"%s: please specify clip length with -l option\n",
+			ProgName);
+	}
+
+	/* end processing of command line */
+
+
+	MP4FileHandle srcFile = 
+		MP4Modify(srcFileName, verbosity);
+
+	if (!srcFile) {
+		exit(1);
+	}
+
+	MP4FileHandle dstFile = 
+		MP4_INVALID_FILE_HANDLE;
+ 
+	if (dstFileName) {
+		dstFile = MP4Create(dstFileName, verbosity);
+	}
+
+	if (trackId == MP4_INVALID_TRACK_ID) {
+		u_int32_t numTracks = MP4GetNumberOfTracks(srcFile);
+
+		for (u_int32_t i = 0; i < numTracks; i++) {
+			trackId = MP4FindTrackId(srcFile, i);
+			ClipTrack(srcFile, trackId, dstFile, startTime, duration);
+		}
+	} else {
+		ClipTrack(srcFile, trackId, dstFile, startTime, duration);
+	}
+
+	MP4Close(srcFile);
+	if (dstFile != MP4_INVALID_FILE_HANDLE) {
+		MP4Close(dstFile);
+	}
+
+	return(0);
+}
+
+void ClipTrack(
+	MP4FileHandle srcFile, 
+	MP4TrackId trackId, 
+	MP4FileHandle dstFile,
+	float startTime,
+	float duration)
+{
+	MP4Timestamp trackStartTime =
+		MP4ConvertToTrackTimestamp(
+			srcFile,
+			trackId,
+			(u_int64_t)(startTime * 1000),
+			MP4_MSECS_TIME_SCALE);
+
+	MP4Duration trackDuration =
+		MP4ConvertToTrackDuration(
+			srcFile,
+			trackId,
+			(u_int64_t)(duration * 1000),
+			MP4_MSECS_TIME_SCALE);
+
+	MP4EditId editId = 
+		MP4AddTrackEdit(
+			srcFile, 
+			trackId,
+			1,
+			trackStartTime,
+			trackDuration);
+
+	if (editId == MP4_INVALID_EDIT_ID) {
+		fprintf(stderr,
+			"%s: can't create track edit\n",
+			ProgName);
+		return;
+	}
+
+	if (dstFile) {
+		MP4CopyTrack(
+			srcFile,
+			trackId,
+			dstFile,
+			true);
+
+		MP4DeleteTrackEdit(
+			srcFile,
+			trackId, 
+			editId);
+	}
+}
+
diff --git a/test/OLD/nullcreate.cpp b/test/OLD/nullcreate.cpp
new file mode 100644
index 0000000..1460651
--- /dev/null
+++ b/test/OLD/nullcreate.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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.  All Rights Reserved.
+ * 
+ * Contributor(s): 
+ *		Dave Mackie		dmackie@cisco.com
+ */
+
+#include "mp4.h"
+#if 0
+#include "mp4util.h"
+#endif
+
+main(int argc, char** argv)
+{
+#if 1
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s <file>\n", argv[0]);
+		exit(1);
+	}
+
+	u_int32_t verbosity = MP4_DETAILS_ALL;
+
+	MP4FileHandle mp4File = MP4Create(argv[1], verbosity);
+
+	if (!mp4File) {
+		exit(1);
+	}
+
+	printf("Created skeleton\n");
+	MP4Dump(mp4File);
+
+	MP4SetODProfileLevel(mp4File, 1);
+	MP4SetSceneProfileLevel(mp4File, 1);
+	MP4SetVideoProfileLevel(mp4File, 1);
+	MP4SetAudioProfileLevel(mp4File, 1);
+	MP4SetGraphicsProfileLevel(mp4File, 1);
+
+	MP4TrackId odTrackId = 
+		MP4AddODTrack(mp4File);
+
+	MP4TrackId bifsTrackId = 
+		MP4AddSceneTrack(mp4File);
+
+	MP4TrackId videoTrackId = 
+#if 0
+		MP4AddVideoTrack(mp4File, 90000, 3000, 320, 240);
+#else
+	MP4AddH264VideoTrack(mp4File, 90000, 3000, 320, 240, 
+			     1, 2, 3, 1);
+	static uint8_t pseq[] = { 0, 1, 2, 3, 4, 5, 6,7, 8, 9 };
+
+	MP4AddH264SequenceParameterSet(mp4File, videoTrackId, pseq, 10);
+	MP4AddH264SequenceParameterSet(mp4File, videoTrackId, pseq, 6);
+	MP4AddH264PictureParameterSet(mp4File, videoTrackId, pseq, 7);
+	MP4AddH264PictureParameterSet(mp4File, videoTrackId, pseq, 8);
+	MP4AddH264PictureParameterSet(mp4File, videoTrackId, pseq, 7);
+
+#endif
+
+	MP4TrackId videoHintTrackId = 
+		MP4AddHintTrack(mp4File, videoTrackId);
+
+	MP4TrackId audioTrackId = 
+		MP4AddAudioTrack(mp4File, 44100, 1152);
+
+	MP4TrackId audioHintTrackId = 
+		MP4AddHintTrack(mp4File, audioTrackId);
+
+	printf("Added tracks\n");
+	MP4Dump(mp4File);
+
+	MP4Close(mp4File);
+
+	//	MP4MakeIsmaCompliant(argv[1], verbosity);
+
+	exit(0);
+#else
+   uint8_t *bin = NULL;
+
+   for (uint32_t ix = 4; ix < 1024; ix++) {
+     printf("pass %d\n", ix);
+     bin = (uint8_t *)malloc(ix);
+     for (uint32_t jx = 0; jx < ix; jx++) {
+       bin[jx] = ((uint32_t)random()) >> 24;
+     }
+     char *test;
+     test = MP4ToBase64(bin, ix);
+     uint8_t *ret;
+     uint32_t retsize;
+     ret = Base64ToBinary(test, strlen(test), &retsize);
+     if (retsize != ix) {
+       printf("return size not same %d %d\n", ix, retsize);
+       exit(0);
+     }
+     if (memcmp(ret, bin, ix) != 0) {
+       printf("memory not same\n");
+       exit(0);
+     }
+     free(test);
+     free(ret);
+     free(bin);
+   }
+   return 0;
+#endif
+}
+
diff --git a/test/OLD/nullvplayer.cpp b/test/OLD/nullvplayer.cpp
new file mode 100644
index 0000000..c80cb63
--- /dev/null
+++ b/test/OLD/nullvplayer.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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.  All Rights Reserved.
+ * 
+ * Contributor(s): 
+ *		Dave Mackie		dmackie@cisco.com
+ */
+
+#include "mp4.h"
+
+main(int argc, char** argv)
+{
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s <file>\n", argv[0]);
+		exit(1);
+	}
+
+	u_int32_t verbosity = MP4_DETAILS_ALL;
+	char* fileName = argv[1];
+
+	// open the mp4 file, and read meta-info
+	MP4FileHandle mp4File = MP4Read(fileName, verbosity);
+
+	u_int8_t profileLevel = MP4GetVideoProfileLevel(mp4File);
+
+	// get a handle on the first video track
+	MP4TrackId trackId = MP4FindTrackId(mp4File, 0, "video");
+
+	// gather the crucial track information 
+
+	u_int32_t timeScale = MP4GetTrackTimeScale(mp4File, trackId);
+
+	// note all times and durations 
+	// are in units of the track time scale
+
+	MP4Duration trackDuration = MP4GetTrackDuration(mp4File, trackId);
+
+	MP4SampleId numSamples = MP4GetTrackNumberOfSamples(mp4File, trackId);
+
+	u_int32_t maxSampleSize = MP4GetTrackMaxSampleSize(mp4File, trackId);
+
+	u_int8_t* pConfig;
+	u_int32_t configSize = 0;
+
+	MP4GetTrackESConfiguration(mp4File, trackId, &pConfig, &configSize);
+
+	// initialize decoder with Elementary Stream (ES) configuration
+
+	// done with our copy of ES configuration
+	free(pConfig);
+
+
+	// now consecutively read and display the track samples
+
+	u_int8_t* pSample = (u_int8_t*)malloc(maxSampleSize);
+	u_int32_t sampleSize;
+	MP4Timestamp sampleTime;
+	MP4Duration sampleDuration;
+	MP4Duration sampleRenderingOffset;
+	bool isSyncSample;
+
+	for (MP4SampleId sampleId = 1; sampleId <= numSamples; sampleId++) {
+
+		// give ReadSample our own buffer, and let it know how big it is
+		sampleSize = maxSampleSize;
+
+		// read next sample from video track
+		MP4ReadSample(mp4File, trackId, sampleId, 
+			&pSample, &sampleSize,
+			&sampleTime, &sampleDuration, &sampleRenderingOffset, 
+			&isSyncSample);
+
+		// convert timestamp and duration from track time to milliseconds
+		u_int64_t myTime = MP4ConvertFromTrackTimestamp(mp4File, trackId, 
+			sampleTime, MP4_MSECS_TIME_SCALE);
+
+		u_int64_t myDuration = MP4ConvertFromTrackDuration(mp4File, trackId,
+			sampleDuration, MP4_MSECS_TIME_SCALE);
+
+		// decode frame and display it
+	}
+
+	// close mp4 file
+	MP4Close(mp4File);
+
+
+	// Note to seek to time 'when' in the track
+	// use MP4GetSampleIdFromTime(MP4FileHandle hFile, 
+	//		MP4Timestamp when, bool wantSyncSample)
+	// 'wantSyncSample' determines if a sync sample is desired or not
+	// e.g.
+	// MP4Timestamp when = 
+	//	MP4ConvertToTrackTimestamp(mp4File, trackId, 30, MP4_SECS_TIME_SCALE);
+	// MP4SampleId newSampleId = MP4GetSampleIdFromTime(mp4File, when, true);
+	// MP4ReadSample(mp4File, trackId, newSampleId, ...);
+	// 
+	// Note that start time for sample may be later than 'when'
+
+	exit(0);
+}
+
diff --git a/test/OLD/urltrack.cpp b/test/OLD/urltrack.cpp
new file mode 100644
index 0000000..a61c480
--- /dev/null
+++ b/test/OLD/urltrack.cpp
@@ -0,0 +1,91 @@
+/*
+ * 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.  All Rights Reserved.
+ * 
+ * Contributor(s): 
+ *		Dave Mackie		dmackie@cisco.com
+ */
+
+#include "mp4.h"
+
+main(int argc, char** argv)
+{
+	if (argc < 2) {
+		fprintf(stderr, "Usage: %s <file>\n", argv[0]);
+		exit(1);
+	}
+
+	u_int32_t verbosity = 0 /* MP4_DETAILS_ALL */;
+
+	MP4FileHandle mp4File = MP4Create(argv[1], verbosity);
+
+	if (!mp4File) {
+		exit(1);
+	}
+
+	MP4TrackId urlTrackId = 
+#if 0
+		MP4AddTrack(mp4File, "URLF");
+#else
+	MP4AddHrefTrack(mp4File, 90000, MP4_INVALID_DURATION);
+#endif
+	printf("urlTrackId %d\n", urlTrackId);
+
+	u_int8_t i;
+	char url[128];
+
+	for (i = 1; i <= 5; i++) {
+		sprintf(url, "http://server.com/foo/bar%u.html", i);
+
+		MP4WriteSample(mp4File, urlTrackId, 
+			(u_int8_t*)url, strlen(url) + 1, (MP4Duration)i);
+	}
+
+	MP4Close(mp4File);
+
+	mp4File = MP4Read(argv[1], verbosity);
+
+	// check that we can find the track again
+#if 0
+	urlTrackId = MP4FindTrackId(mp4File, 0, "URLF");
+#else
+	urlTrackId = MP4FindTrackId(mp4File, 0, MP4_CNTL_TRACK_TYPE);
+#endif
+	printf("urlTrackId %d\n", urlTrackId);
+	
+	for (i = 1; i <= 5; i++) {
+		u_int8_t* pSample = NULL;
+		u_int32_t sampleSize = 0;
+		MP4Duration duration;
+		bool rc;
+
+		rc = MP4ReadSample(mp4File, urlTrackId, i,
+			&pSample, &sampleSize, NULL, &duration);
+
+		if (rc) {
+			printf("Sample %i duration "D64": %s\n", 
+				i, duration, pSample);
+			free(pSample);
+		} else {
+			printf("Couldn't read sample %i\n", i);
+		}
+	}
+
+	MP4Close(mp4File);
+
+	exit(0);
+}
+
diff --git a/testsuite/config/unix.exp b/testsuite/config/unix.exp
new file mode 100644
index 0000000..89aa930
--- /dev/null
+++ b/testsuite/config/unix.exp
@@ -0,0 +1 @@
+load_lib command.exp
diff --git a/testsuite/lib/command.exp b/testsuite/lib/command.exp
new file mode 100644
index 0000000..537550b
--- /dev/null
+++ b/testsuite/lib/command.exp
@@ -0,0 +1,172 @@
+namespace eval ::command {
+    namespace export test_standardUsage
+
+    variable _action  "undefined"
+    variable _subject "undefined"
+    variable _test    "undefined"
+}
+
+###############################################################################
+
+proc ::command::beginAction { args } {
+    variable _section
+
+    set _action  [lindex $args 0]
+    set _subject [lrange $args 1 end]
+
+    clone_output "++ $_action: $_subject"
+}
+
+proc ::command::beginTest { args } {
+    variable _test
+    set _test [lindex $args 0]
+}
+
+###############################################################################
+
+proc ::command::exec { args } {
+    set status [lindex $args 0]
+    set exe    [lindex $args 1]
+    set xargs  [lrange $args 2 end]
+
+    if { [llength $xargs] == 0 } {
+        beginAction "EXECUTE" $exe
+        set rv [catch {::exec $::objdir/$exe} output]
+    } else {
+        beginAction "EXECUTE" $exe [join $xargs]
+        set rv [catch {::exec $::objdir/$exe [join $xargs]} output]
+    }
+
+    set output [split $output "\n"]
+
+    beginTest "exit=$status"
+    if { $rv } {
+        set errg [lindex $::errorCode 0]
+        set errc [lindex $::errorCode 2]
+        if { $errg == "CHILDSTATUS" && $errc == $status } {
+            pass
+            return [list 0 $output]
+        } else {
+            fail "exit=$errc"
+            return [list 1 $output]
+        }
+    } else {
+        if { $status == 0 } {
+            pass
+            return [list 0 $output]
+        } else {
+            fail "exit=0"
+            return [list 1 $output]
+        }
+    }
+}
+
+###############################################################################
+
+proc ::command::fail { args } {
+    variable _test
+
+    set text  [lindex $args 0]
+    set input [lindex $args 1]
+
+    set max [llength $input]
+
+    ::fail $_test
+    if { $max == 0 } {
+        send_user -- "-- $text\n"
+        return
+    }
+
+    if { $max == 1 } {
+        send_user -- "-- $text (1 line of match text follows)\n"
+    } else {
+        send_user -- "-- $text ($max lines of match text follows)\n"
+    }
+
+    for { set i 0 } { $i < $max } { incr i } {
+        set line [lindex $input $i]
+        send_user -- "-- | $line\n"
+    }
+}
+
+proc ::command::pass { args } {
+    variable _test
+    ::pass $_test
+}
+
+###############################################################################
+
+proc ::command::matchListAny { args } {
+    set rx    [lindex $args 0]
+    set lines [lindex $args 1]
+
+    beginTest "match: $rx"
+    set max [llength $lines]
+    for { set i 0 } { $i < $max } { incr i } {
+        set line [lindex $lines $i]
+
+        if { [regexp $rx $line] } {
+            pass
+            return
+        }
+    }
+
+    fail "no match" $lines
+}
+
+proc ::command::matchListLine { args } {
+    set rx    [lindex $args 0]
+    set lines [lindex $args 1]
+    set idx   [lindex $args 2]
+
+    set line [lindex $lines $idx]
+
+    beginTest "match: $rx"
+    if { [regexp $rx $line] } {
+        pass
+    } else {
+        fail "no match" [list $line]
+    }
+}
+
+###############################################################################
+
+proc ::command::test_standardUsage { args } {
+    set exe [lindex $args 0]
+
+    # noargs
+    set rv [exec 1 $exe]
+    matchListLine {^Usage: \S+} [lindex $rv 1] 0
+    matchListLine {^Try.*-h.*--help} [lindex $rv 1] 1
+
+    # -h
+    set rv [exec 0 $exe -h]
+    matchListLine {^Usage: \S+} [lindex $rv 1] 0
+    matchListAny  {\s+--help}    [lindex $rv 1]
+    matchListAny  {\s+--version} [lindex $rv 1]
+
+    # --help
+    set rv [exec 0 $exe --help]
+    matchListLine {^Usage: \S+} [lindex $rv 1] 0
+    matchListAny  {\s+--help}       [lindex $rv 1]
+    matchListAny  {\s+--version}    [lindex $rv 1]
+    matchListAny  {^VERBOSE LEVELS} [lindex $rv 1]
+
+    # --version
+    set rv [exec 0 $exe --version]
+    matchListLine {^\S+\s-\s\S+} [lindex $rv 1] 0
+
+    # --versionx
+    set rv [exec 0 $exe --versionx]
+    matchListAny  {^utility:\s+\S+} [lindex $rv 1]
+    matchListAny  {^product:\s+\S+} [lindex $rv 1]
+    matchListAny  {^version:\s+\S+} [lindex $rv 1]
+
+    # bogus short-option
+    set rv [exec 1 $exe "-*" ]
+    matchListLine {invalid} [lindex $rv 1] 0
+
+    # bogus long-option
+    set rv [exec 1 $exe --bogusLongOption ]
+    matchListLine {unrecognized} [lindex $rv 1] 0
+}
diff --git a/testsuite/main/standardUsage.exp b/testsuite/main/standardUsage.exp
new file mode 100644
index 0000000..11b4627
--- /dev/null
+++ b/testsuite/main/standardUsage.exp
@@ -0,0 +1,4 @@
+::command::test_standardUsage mp4art
+::command::test_standardUsage mp4file
+::command::test_standardUsage mp4subtitle
+::command::test_standardUsage mp4track
diff --git a/util/impl.h b/util/impl.h
new file mode 100644
index 0000000..9daa297
--- /dev/null
+++ b/util/impl.h
@@ -0,0 +1,10 @@
+#ifndef MP4V2_UTIL_IMPL_H
+#define MP4V2_UTIL_IMPL_H
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libutil/util.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif // MP4V2_UTIL_IMPL_H
diff --git a/util/mp4art.cpp b/util/mp4art.cpp
new file mode 100644
index 0000000..add935e
--- /dev/null
+++ b/util/mp4art.cpp
@@ -0,0 +1,438 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2010.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "util/impl.h"
+
+namespace mp4v2 { namespace util {
+    using namespace itmf;
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ArtUtility : public Utility
+{
+private:
+    enum ArtLongCode {
+        LC_ART_ANY = _LC_MAX,
+        LC_ART_INDEX,
+        LC_LIST,
+        LC_ADD,
+        LC_REMOVE,
+        LC_REPLACE,
+        LC_EXTRACT,
+    };
+
+public:
+    ArtUtility( int, char** );
+
+protected:
+    // delegates implementation
+    bool utility_option( int, bool& );
+    bool utility_job( JobContext& );
+
+private:
+    struct ArtType {
+        string         name;
+        string         ext;
+        vector<string> cwarns; // compatibility warnings
+        string         cerror; // compatibility error
+    };
+
+    bool actionList    ( JobContext& );
+    bool actionAdd     ( JobContext& );
+    bool actionRemove  ( JobContext& );
+    bool actionReplace ( JobContext& );
+    bool actionExtract ( JobContext& );
+
+    bool extractSingle( JobContext&, const CoverArtBox::Item&, uint32_t );
+
+private:
+    Group  _actionGroup;
+    Group  _parmGroup;
+
+    bool (ArtUtility::*_action)( JobContext& );
+
+    string   _artImageFile;
+    uint32_t _artFilter;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+ArtUtility::ArtUtility( int argc, char** argv )
+    : Utility      ( "mp4art", argc, argv )
+    , _actionGroup ( "ACTIONS" )
+    , _parmGroup   ( "ACTION PARAMETERS" )
+    , _action      ( NULL )
+    , _artFilter   ( numeric_limits<uint32_t>::max() )
+{
+    // add standard options which make sense for this utility
+    _group.add( STD_OPTIMIZE );
+    _group.add( STD_DRYRUN );
+    _group.add( STD_KEEPGOING );
+    _group.add( STD_OVERWRITE );
+    _group.add( STD_FORCE );
+    _group.add( STD_QUIET );
+    _group.add( STD_DEBUG );
+    _group.add( STD_VERBOSE );
+    _group.add( STD_HELP );
+    _group.add( STD_VERSION );
+    _group.add( STD_VERSIONX );
+
+    _parmGroup.add( "art-any",   false, LC_ART_ANY,   "act on all covr-boxes (default)" );
+    _parmGroup.add( "art-index", true,  LC_ART_INDEX, "act on covr-box index IDX", "IDX" );
+    _groups.push_back( &_parmGroup );
+
+    _actionGroup.add( "list",    false, LC_LIST,    "list all covr-boxes" );
+    _actionGroup.add( "add",     true,  LC_ADD,     "add covr-box from IMG file", "IMG" );
+    _actionGroup.add( "replace", true,  LC_REPLACE, "replace covr-box with IMG file", "IMG" );
+    _actionGroup.add( "remove",  false, LC_REMOVE,  "remove covr-box" );
+    _actionGroup.add( "extract", false, LC_EXTRACT, "extract covr-box" );
+    _groups.push_back( &_actionGroup );
+
+    _usage = "[OPTION]... ACTION file...";
+    _description =
+        // 79-cols, inclusive, max desired width
+        // |----------------------------------------------------------------------------|
+        "\nFor each mp4 (m4a) file specified, perform the specified ACTION. An action"
+        "\nmust be specified. Some options are not applicable for some actions.";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::actionAdd( JobContext& job )
+{
+    File in( _artImageFile, File::MODE_READ );
+    if( in.open() )
+        return herrf( "unable to open %s for read: %s\n", _artImageFile.c_str(), sys::getLastErrorStr() );
+
+    const uint32_t max = numeric_limits<uint32_t>::max();
+    if( in.size > max )
+        return herrf( "file too large: %s (exceeds %u bytes)\n", _artImageFile.c_str(), max );
+
+    CoverArtBox::Item item;
+    item.size     = static_cast<uint32_t>( in.size );
+    item.buffer   = static_cast<uint8_t*>( malloc( item.size ));
+    item.autofree = true;
+
+    File::Size nin;
+    if( in.read( item.buffer, item.size, nin ))
+        return herrf( "read failed: %s\n", _artImageFile.c_str() );
+
+    in.close();
+
+    verbose1f( "adding %s -> %s\n", _artImageFile.c_str(), job.file.c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    if( CoverArtBox::add( job.fileHandle, item ))
+        return herrf( "unable to add covr-box\n" );
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::actionExtract( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    // single-mode
+    if( _artFilter != numeric_limits<uint32_t>::max() ) {
+        CoverArtBox::Item item;
+        if( CoverArtBox::get( job.fileHandle, item, _artFilter ))
+            return herrf( "unable to retrieve covr-box (index=%d): %s\n", _artFilter, job.file.c_str() );
+
+        return extractSingle( job, item, _artFilter );
+    }
+
+    // wildcard-mode
+    CoverArtBox::ItemList items;
+    if( CoverArtBox::list( job.fileHandle, items ))
+        return herrf( "unable to fetch list of covr-box: %s\n", job.file.c_str() );
+
+    bool onesuccess = false;
+    const CoverArtBox::ItemList::size_type max = items.size();
+    for( CoverArtBox::ItemList::size_type i = 0; i < max; i++ ) {
+        bool rv = extractSingle( job, items[i], (uint32_t)i );
+        if( !rv )
+            onesuccess = true;
+        if( !_keepgoing && rv )
+            return FAILURE;
+    }
+
+    return _keepgoing ? onesuccess : SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::actionList( JobContext& job )
+{
+    ostringstream report;
+
+    const int widx = 3;
+    const int wsize = 8;
+    const int wtype = 9;
+    const string sep = "  ";
+
+    if( _jobCount == 0 ) {
+        report << setw(widx) << right << "IDX" << left
+               << sep << setw(wsize) << right << "BYTES" << left
+               << sep << setw(8) << "CRC32"
+               << sep << setw(wtype) << "TYPE"
+               << sep << setw(0) << "FILE"
+               << '\n';
+
+        report << setfill('-') << setw(70) << "" << setfill(' ') << '\n';
+    }
+
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    CoverArtBox::ItemList items;
+    if( CoverArtBox::list( job.fileHandle, items ))
+        return herrf( "unable to get list of covr-box: %s\n", job.file.c_str() );
+
+    int line = 0;
+    const CoverArtBox::ItemList::size_type max = items.size();
+    for( CoverArtBox::ItemList::size_type i = 0; i < max; i++ ) {
+        if( _artFilter != numeric_limits<uint32_t>::max() && _artFilter != i )
+            continue;
+
+        CoverArtBox::Item& item = items[i];
+        const uint32_t crc = crc32( item.buffer, item.size );
+
+        report << setw(widx) << right << i
+               << sep << setw(wsize) << item.size
+               << sep << setw(8) << setfill('0') << hex << crc << setfill(' ') << dec
+               << sep << setw(wtype) << left << enumBasicType.toString( item.type );
+
+        if( line++ == 0 )
+            report << sep << setw(0) << job.file;
+
+        report << '\n';
+    }
+
+    verbose1f( "%s", report.str().c_str() );
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::actionRemove( JobContext& job )
+{
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    if( _artFilter == numeric_limits<uint32_t>::max() )
+        verbose1f( "removing covr-box (all) from %s\n", job.file.c_str() );
+    else
+        verbose1f( "removing covr-box (index=%d) from %s\n", _artFilter, job.file.c_str() );
+
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    if( CoverArtBox::remove( job.fileHandle, _artFilter ))
+        return herrf( "remove failed\n" );
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::actionReplace( JobContext& job )
+{
+    File in( _artImageFile, File::MODE_READ );
+    if( in.open() )
+        return herrf( "unable to open %s for read: %s\n", _artImageFile.c_str(), sys::getLastErrorStr() );
+
+    const uint32_t max = numeric_limits<uint32_t>::max();
+    if( in.size > max )
+        return herrf( "file too large: %s (exceeds %u bytes)\n", _artImageFile.c_str(), max );
+
+    CoverArtBox::Item item;
+    item.size     = static_cast<uint32_t>( in.size );
+    item.buffer   = static_cast<uint8_t*>( malloc( item.size ));
+    item.autofree = true;
+
+    File::Size nin;
+    if( in.read( item.buffer, item.size, nin ))
+        return herrf( "read failed: %s\n", _artImageFile.c_str() );
+
+    in.close();
+
+    if( _artFilter == numeric_limits<uint32_t>::max() )
+        verbose1f( "replacing %s -> %s (all)\n", _artImageFile.c_str(), job.file.c_str() );
+    else
+        verbose1f( "replacing %s -> %s (index=%d)\n", _artImageFile.c_str(), job.file.c_str(), _artFilter );
+
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    if( CoverArtBox::set( job.fileHandle, item, _artFilter ))
+        return herrf( "unable to add covr-box: %s\n", job.file.c_str() );
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::extractSingle( JobContext& job, const CoverArtBox::Item& item, uint32_t index )
+{
+    // compute out filename
+    string out_name = job.file;
+    FileSystem::pathnameStripExtension( out_name );
+
+    ostringstream oss;
+    oss << out_name << ".art[" << index << ']';
+
+    // if implicit we try to determine type by inspecting data
+    BasicType bt = item.type;
+    if( bt == BT_IMPLICIT )
+        bt = computeBasicType( item.buffer, item.size );
+
+    // add file extension appropriate for known covr-box types
+    switch( bt ) {
+        case BT_GIF:    oss << ".gif"; break;
+        case BT_JPEG:   oss << ".jpg"; break;
+        case BT_PNG:    oss << ".png"; break;
+        case BT_BMP:    oss << ".bmp"; break;
+
+        default:
+            oss << ".dat";
+            break;
+    }
+
+    out_name = oss.str();
+    verbose1f( "extracting %s (index=%d) -> %s\n", job.file.c_str(), index, out_name.c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    File out( out_name, File::MODE_CREATE );
+    if( openFileForWriting( out ))
+        return FAILURE;
+
+    File::Size nout;
+    if( out.write( item.buffer, item.size, nout ))
+        return herrf( "write failed: %s\n", out_name.c_str() );
+
+    out.close();
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::utility_job( JobContext& job )
+{
+    if( !_action )
+        return herrf( "no action specified\n" );
+
+    return (this->*_action)( job );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+ArtUtility::utility_option( int code, bool& handled )
+{
+    handled = true;
+
+    switch( code ) {
+        case LC_ART_ANY:
+            _artFilter = numeric_limits<uint32_t>::max();
+            break;
+
+        case LC_ART_INDEX:
+        {
+            istringstream iss( prog::optarg );
+            iss >> _artFilter;
+            if( iss.rdstate() != ios::eofbit )
+                return herrf( "invalid cover-art index: %s\n", prog::optarg );
+            break;
+        }
+
+        case LC_LIST:
+            _action = &ArtUtility::actionList;
+            break;
+
+        case LC_ADD:
+            _action = &ArtUtility::actionAdd;
+            _artImageFile = prog::optarg;
+            if( _artImageFile.empty() )
+                return herrf( "invalid image file: empty-string\n" );
+            break;
+
+        case LC_REMOVE:
+            _action = &ArtUtility::actionRemove;
+            break;
+
+        case LC_REPLACE:
+            _action = &ArtUtility::actionReplace;
+            _artImageFile = prog::optarg;
+            if( _artImageFile.empty() )
+                return herrf( "invalid image file: empty-string\n" );
+            break;
+
+        case LC_EXTRACT:
+            _action = &ArtUtility::actionExtract;
+            break;
+
+        default:
+            handled = false;
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C"
+int main( int argc, char** argv )
+{
+    mp4v2::util::ArtUtility util( argc, argv );
+    return util.process();
+}
diff --git a/util/mp4chaps.cpp b/util/mp4chaps.cpp
new file mode 100644
index 0000000..98400f8
--- /dev/null
+++ b/util/mp4chaps.cpp
@@ -0,0 +1,1158 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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 Ullrich Pollaehne.
+//  Portions created by Kona Blend are Copyright (C) 2008.
+//  Portions created by David Byron are Copyright (C) 2010.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      Ullrich Pollaehne, u.pollaehne@@gmail.com
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+#include "util/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+///
+/// Chapter utility program class.
+///
+/// This class provides an implementation for a QuickTime/Nero chapter utility which
+/// allows to add, delete, convert export or import QuickTime and Nero chapters
+/// in MP4 container files.
+///
+///
+/// @see Utility
+///
+///////////////////////////////////////////////////////////////////////////////
+class ChapterUtility : public Utility
+{
+private:
+    static const double CHAPTERTIMESCALE; //!< the timescale used for chapter tracks (1000)
+
+    enum FileLongCode {
+        LC_CHPT_ANY = _LC_MAX,
+        LC_CHPT_QT,
+        LC_CHPT_NERO,
+        LC_CHPT_COMMON,
+        LC_CHP_LIST,
+        LC_CHP_CONVERT,
+        LC_CHP_EVERY,
+        LC_CHP_EXPORT,
+        LC_CHP_IMPORT,
+        LC_CHP_REMOVE
+    };
+
+    enum ChapterFormat {
+        CHPT_FMT_NATIVE,
+        CHPT_FMT_COMMON
+    };
+
+    enum FormatState {
+        FMT_STATE_INITIAL,
+        FMT_STATE_TIME_LINE,
+        FMT_STATE_TITLE_LINE,
+        FMT_STATE_FINISH
+    };
+
+public:
+    ChapterUtility( int, char** );
+
+protected:
+    // delegates implementation
+    bool utility_option( int, bool& );
+    bool utility_job( JobContext& );
+
+private:
+    bool actionList    ( JobContext& ); 
+    bool actionConvert ( JobContext& );
+    bool actionEvery   ( JobContext& );
+    bool actionExport  ( JobContext& );
+    bool actionImport  ( JobContext& );
+    bool actionRemove  ( JobContext& );
+
+private:
+    Group  _actionGroup;
+    Group  _parmGroup;
+
+    bool        (ChapterUtility::*_action)( JobContext& );
+    void        fixQtScale(MP4FileHandle );
+    MP4TrackId  getReferencingTrack( MP4FileHandle, bool& );
+    string      getChapterTypeName( MP4ChapterType ) const;
+    bool        parseChapterFile( const string, vector<MP4Chapter_t>&, Timecode::Format& );
+    bool        readChapterFile( const string, char**, File::Size& );
+    MP4Duration convertFrameToMillis( MP4Duration, uint32_t );
+
+    MP4ChapterType _ChapterType;
+    ChapterFormat  _ChapterFormat;
+    uint32_t       _ChaptersEvery;
+    string         _ChapterFile;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+const double ChapterUtility::CHAPTERTIMESCALE = 1000.0;
+
+///////////////////////////////////////////////////////////////////////////////
+
+ChapterUtility::ChapterUtility( int argc, char** argv )
+    : Utility        ( "mp4chaps", argc, argv )
+    , _actionGroup   ( "ACTIONS" )
+    , _parmGroup     ( "ACTION PARAMETERS" )
+    , _action        ( NULL )
+    , _ChapterType   ( MP4ChapterTypeAny )
+    , _ChapterFormat ( CHPT_FMT_NATIVE )
+    , _ChaptersEvery ( 0 )
+{
+    // add standard options which make sense for this utility
+    _group.add( STD_OPTIMIZE );
+    _group.add( STD_DRYRUN );
+    _group.add( STD_KEEPGOING );
+    _group.add( STD_OVERWRITE );
+    _group.add( STD_FORCE );
+    _group.add( STD_QUIET );
+    _group.add( STD_DEBUG );
+    _group.add( STD_VERBOSE );
+    _group.add( STD_HELP );
+    _group.add( STD_VERSION );
+    _group.add( STD_VERSIONX );
+
+    _parmGroup.add( 'A', false, "chapter-any",   false, LC_CHPT_ANY,    "act on any chapter type (default)" );
+    _parmGroup.add( 'Q', false, "chapter-qt",    false, LC_CHPT_QT,     "act on QuickTime chapters" );
+    _parmGroup.add( 'N', false, "chapter-nero",  false, LC_CHPT_NERO,   "act on Nero chapters" );
+    _parmGroup.add( 'C', false, "format-common", false, LC_CHPT_COMMON, "export chapters in common format" );
+    _groups.push_back( &_parmGroup );
+
+    _actionGroup.add( 'l', false, "list",    false, LC_CHP_LIST,    "list available chapters" );
+    _actionGroup.add( 'c', false, "convert", false, LC_CHP_CONVERT, "convert available chapters" );
+    _actionGroup.add( 'e', true,  "every",   true,  LC_CHP_EVERY,   "create chapters every NUM seconds", "NUM" );
+    _actionGroup.add( 'x', false, "export",  false, LC_CHP_EXPORT,  "export chapters to mp4file.chapters.txt", "TXT" );
+    _actionGroup.add( 'i', false, "import",  false, LC_CHP_IMPORT,  "import chapters from mp4file.chapters.txt", "TXT" );
+    _actionGroup.add( 'r', false, "remove",  false, LC_CHP_REMOVE,  "remove all chapters" );
+    _groups.push_back( &_actionGroup );
+
+    _usage = "[OPTION]... ACTION [ACTION PARAMETERS] mp4file...";
+    _description =
+        // 79-cols, inclusive, max desired width
+        // |----------------------------------------------------------------------------|
+        "\nFor each mp4 file specified, perform the specified ACTION. An action must be"
+        "\nspecified. Some options are not applicable to some actions.";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Action for listing chapters from <b>job.file</b>
+ *
+ *  
+ *  @param job the job to process
+ *  @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise
+ */
+bool
+ChapterUtility::actionList( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+    {
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+    }
+
+    MP4Chapter_t * chapters = 0;
+    uint32_t chapterCount = 0;
+
+    // get the list of chapters
+    MP4ChapterType chtp = MP4GetChapters(job.fileHandle, &chapters, &chapterCount, _ChapterType);
+    if (0 == chapterCount)
+    {
+        verbose1f( "File \"%s\" does not contain chapters of type %s\n", job.file.c_str(),
+                   getChapterTypeName( _ChapterType ).c_str() );
+        return SUCCESS;
+    }
+
+    // start output (more or less like mp4box does)
+    ostringstream report;
+    report << getChapterTypeName( chtp ) << ' ' << "Chapters of " << '"' << job.file << '"' << endl;
+
+    Timecode duration(0, CHAPTERTIMESCALE);
+    duration.setFormat( Timecode::DECIMAL );
+    for (uint32_t i = 0; i < chapterCount; ++i)
+    {
+        // print the infos
+        report << '\t' << "Chapter #" << setw( 3 ) << setfill( '0' ) << i+1
+               << " - " << duration.svalue << " - " << '"' << chapters[i].title << '"' << endl;
+
+        // add the duration of this chapter to the sum (is the start time of the next chapter)
+        duration += Timecode(chapters[i].duration, CHAPTERTIMESCALE);
+    }
+
+    verbose1f( "%s", report.str().c_str() );
+
+    // free up the memory
+    MP4Free(chapters);
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Action for converting chapters in <b>job.file</b>
+ *
+ *  
+ *  @param job the job to process
+ *  @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise
+ */
+bool
+ChapterUtility::actionConvert( JobContext& job )
+{
+    MP4ChapterType sourceType;
+
+    switch( _ChapterType )
+    {
+    case MP4ChapterTypeNero:
+        sourceType = MP4ChapterTypeQt;
+        break;
+    case MP4ChapterTypeQt:
+        sourceType = MP4ChapterTypeNero;
+        break;
+    default:
+        return herrf( "invalid chapter type \"%s\" define the chapter type to convert to\n",
+                      getChapterTypeName( _ChapterType ).c_str() );
+    }
+
+    ostringstream oss;
+    oss << "converting chapters in file " << '"' << job.file << '"'
+        << " from " << getChapterTypeName( sourceType ) << " to " << getChapterTypeName( _ChapterType ) << endl;
+
+    verbose1f( "%s", oss.str().c_str() );
+    if( dryrunAbort() )
+    {
+        return SUCCESS;
+    }
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+    {
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+    }
+
+    MP4ChapterType chtp = MP4ConvertChapters( job.fileHandle, _ChapterType );
+    if( MP4ChapterTypeNone == chtp )
+    {
+        return herrf( "File %s does not contain chapters of type %s\n", job.file.c_str(),
+                      getChapterTypeName( sourceType ).c_str() );
+    }
+
+    fixQtScale( job.fileHandle );
+    job.optimizeApplicable = true;
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Action for setting chapters every n second in <b>job.file</b>
+ *
+ *  
+ *  @param job the job to process
+ *  @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise
+ */
+bool
+ChapterUtility::actionEvery( JobContext& job )
+{
+    ostringstream oss;
+    oss << "Setting " << getChapterTypeName( _ChapterType ) << " chapters every "
+        << _ChaptersEvery << " seconds in file " << '"' << job.file << '"' << endl;
+
+    verbose1f( "%s", oss.str().c_str() );
+    if( dryrunAbort() )
+    {
+        return SUCCESS;
+    }
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+    {
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+    }
+
+    bool isVideoTrack = false;
+    MP4TrackId refTrackId = getReferencingTrack( job.fileHandle, isVideoTrack );
+    if( !MP4_IS_VALID_TRACK_ID(refTrackId) )
+    {
+        return herrf( "unable to find a video or audio track in file %s\n", job.file.c_str() );
+    }
+
+    Timecode refTrackDuration( MP4GetTrackDuration( job.fileHandle, refTrackId ), MP4GetTrackTimeScale( job.fileHandle, refTrackId ) );
+    refTrackDuration.setScale( CHAPTERTIMESCALE );
+
+    Timecode chapterDuration( _ChaptersEvery * 1000, CHAPTERTIMESCALE );
+    chapterDuration.setFormat( Timecode::DECIMAL );
+    vector<MP4Chapter_t> chapters;
+
+    do
+    {
+        MP4Chapter_t chap;
+        chap.duration = refTrackDuration.duration > chapterDuration.duration ? chapterDuration.duration : refTrackDuration.duration;
+        sprintf(chap.title, "Chapter %lu", (unsigned long)chapters.size()+1);
+
+        chapters.push_back( chap );
+        refTrackDuration -= chapterDuration;
+    }
+    while( refTrackDuration.duration > 0 );
+
+    if( 0 < chapters.size() )
+    {
+        MP4SetChapters(job.fileHandle, &chapters[0], (uint32_t)chapters.size(), _ChapterType);
+    }
+
+    fixQtScale( job.fileHandle );
+    job.optimizeApplicable = true;
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Action for exporting chapters from the <b>job.file</b>
+ *
+ *  
+ *  @param job the job to process
+ *  @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise
+ */
+bool
+ChapterUtility::actionExport( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+    {
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+    }
+
+    // get the list of chapters
+    MP4Chapter_t*  chapters = 0;
+    uint32_t       chapterCount = 0;
+    MP4ChapterType chtp = MP4GetChapters( job.fileHandle, &chapters, &chapterCount, _ChapterType );
+    if (0 == chapterCount)
+    {
+        return herrf( "File \"%s\" does not contain chapters of type %s\n", job.file.c_str(),
+                      getChapterTypeName( chtp ).c_str() );
+    }
+
+    // build the filename
+    string outName = job.file;
+    if( _ChapterFile.empty() )
+    {
+        FileSystem::pathnameStripExtension( outName );
+        outName.append( ".chapters.txt" );
+    }
+    else
+    {
+        outName = _ChapterFile;
+    }
+
+    ostringstream oss;
+    oss << "Exporting " << chapterCount << " " << getChapterTypeName( chtp );
+    oss << " chapters from file " << '"' << job.file << '"' << " into chapter file " << '"' << outName << '"' << endl;
+
+    verbose1f( "%s", oss.str().c_str() );
+    if( dryrunAbort() )
+    {
+        // free up the memory
+        MP4Free(chapters);
+
+        return SUCCESS;
+    }
+
+    // open the file
+    File out( outName, File::MODE_CREATE );
+    if( openFileForWriting( out ) )
+    {
+        // free up the memory
+        MP4Free(chapters);
+
+        return FAILURE;
+    }
+
+    // write the chapters
+#if defined( _WIN32 )
+    static const char* LINEND = "\r\n";
+#else
+    static const char* LINEND = "\n";
+#endif
+    File::Size nout;
+    bool failure = SUCCESS;
+    int width = 2;
+    if( CHPT_FMT_COMMON == _ChapterFormat && (chapterCount / 100) >= 1 )
+    {
+        width = 3;
+    }
+    Timecode duration( 0, CHAPTERTIMESCALE );
+    duration.setFormat( Timecode::DECIMAL );
+    for( uint32_t i = 0; i < chapterCount; ++i )
+    {
+        // print the infos
+        ostringstream oss;
+        switch( _ChapterFormat )
+        {
+            case CHPT_FMT_COMMON:
+                oss << "CHAPTER" << setw( width ) << setfill( '0' ) << i+1 <<     '=' << duration.svalue << LINEND
+                    << "CHAPTER" << setw( width ) << setfill( '0' ) << i+1 << "NAME=" << chapters[i].title << LINEND;
+                break;
+            case CHPT_FMT_NATIVE:
+            default:
+                oss << duration.svalue << ' ' << chapters[i].title << LINEND;
+        }
+
+        string str = oss.str();
+        if( out.write( str.c_str(), str.size(), nout ) )
+        {
+            failure = herrf( "write to %s failed: %s\n", outName.c_str(), sys::getLastErrorStr() );
+            break;
+        }
+
+        // add the duration of this chapter to the sum (the start time of the next chapter)
+        duration += Timecode(chapters[i].duration, CHAPTERTIMESCALE);
+    }
+    out.close();
+    if( failure )
+    {
+        verbose1f( "removing file %s\n", outName.c_str() );
+        ::remove( outName.c_str() );
+    }
+
+    // free up the memory
+    MP4Free(chapters);
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Action for importing chapters into the <b>job.file</b>
+ *
+ *  
+ *  @param job the job to process
+ *  @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise
+ */
+bool
+ChapterUtility::actionImport( JobContext& job )
+{
+    vector<MP4Chapter_t> chapters;
+    Timecode::Format format;
+
+    // create the chapter file name
+    string inName = job.file;
+    if( _ChapterFile.empty() )
+    {
+        FileSystem::pathnameStripExtension( inName );
+        inName.append( ".chapters.txt" );
+    }
+    else
+    {
+        inName = _ChapterFile;
+    }
+
+    if( parseChapterFile( inName, chapters, format ) )
+    {
+        return FAILURE;
+    }
+
+    ostringstream oss;
+    oss << "Importing " << chapters.size() << " " << getChapterTypeName( _ChapterType );
+    oss << " chapters from file " << inName << " into file " << '"' << job.file << '"' << endl;
+
+    verbose1f( "%s", oss.str().c_str() );
+    if( dryrunAbort() )
+    {
+        return SUCCESS;
+    }
+
+    if( 0 == chapters.size() )
+    {
+        return herrf( "No chapters found in file %s\n", inName.c_str() );
+    }
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+    {
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+    }
+
+    bool isVideoTrack = false;
+    MP4TrackId refTrackId = getReferencingTrack( job.fileHandle, isVideoTrack );
+    if( !MP4_IS_VALID_TRACK_ID(refTrackId) )
+    {
+        return herrf( "unable to find a video or audio track in file %s\n", job.file.c_str() );
+    }
+    if( Timecode::FRAME == format && !isVideoTrack )
+    {
+        // we need a video track for this
+        return herrf( "unable to find a video track in file %s but chapter file contains frame timestamps\n", job.file.c_str() );
+    }
+
+    // get duration and recalculate scale
+    Timecode refTrackDuration( MP4GetTrackDuration( job.fileHandle, refTrackId ),
+                               MP4GetTrackTimeScale( job.fileHandle, refTrackId ) );
+    refTrackDuration.setScale( CHAPTERTIMESCALE );
+
+    // check for chapters starting after duration of reftrack
+    for( vector<MP4Chapter_t>::iterator it = chapters.begin(); it != chapters.end(); )
+    {
+        Timecode curr( (*it).duration, CHAPTERTIMESCALE );
+        if( refTrackDuration <= curr )
+        {
+            hwarnf( "Chapter '%s' start: %s, playlength of file: %s, chapter cannot be set\n",
+                    (*it).title, curr.svalue.c_str(), refTrackDuration.svalue.c_str() );
+            it = chapters.erase( it );
+        }
+        else
+        {
+            ++it;
+        }
+    }
+    if( 0 == chapters.size() )
+    {
+        return SUCCESS;
+    }
+
+    // convert start time into duration
+	uint32_t framerate = static_cast<uint32_t>( CHAPTERTIMESCALE );
+    if( Timecode::FRAME == format )
+    {
+        // get the framerate
+        MP4SampleId sampleCount = MP4GetTrackNumberOfSamples( job.fileHandle, refTrackId );
+        Timecode tmpcd( refTrackDuration.svalue, CHAPTERTIMESCALE );
+		framerate = static_cast<uint32_t>( std::ceil( ((double)sampleCount / (double)tmpcd.duration) * CHAPTERTIMESCALE ) );
+    }
+
+    for( vector<MP4Chapter_t>::iterator it = chapters.begin(); it != chapters.end(); ++it )
+    {
+        MP4Duration currDur = (*it).duration;
+        MP4Duration nextDur =  chapters.end() == it+1 ? refTrackDuration.duration : (*(it+1)).duration;
+
+        if( Timecode::FRAME == format )
+        {
+            // convert from frame nr to milliseconds
+            currDur = convertFrameToMillis( (*it).duration, framerate );
+
+            if( chapters.end() != it+1 )
+            {
+                nextDur = convertFrameToMillis( (*(it+1)).duration, framerate );
+            }
+        }
+
+        (*it).duration = nextDur - currDur;
+    }
+
+    // now set the chapters
+    MP4SetChapters( job.fileHandle, &chapters[0], (uint32_t)chapters.size(), _ChapterType );
+
+    fixQtScale( job.fileHandle );
+    job.optimizeApplicable = true;
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Action for removing chapters from the <b>job.file</b>
+ *
+ *  
+ *  @param job the job to process
+ *  @return mp4v2::util::SUCCESS if successful, mp4v2::util::FAILURE otherwise
+ */
+bool
+ChapterUtility::actionRemove( JobContext& job )
+{
+    ostringstream oss;
+    oss << "Deleting " << getChapterTypeName( _ChapterType ) << " chapters from file " << '"' << job.file << '"' << endl;
+
+    verbose1f( "%s", oss.str().c_str() );
+    if( dryrunAbort() )
+    {
+        return SUCCESS;
+    }
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+    {
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+    }
+
+    MP4ChapterType chtp = MP4DeleteChapters( job.fileHandle, _ChapterType );
+    if( MP4ChapterTypeNone == chtp )
+    {
+        return FAILURE;
+    }
+
+    fixQtScale( job.fileHandle );
+    job.optimizeApplicable = true;
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** process positional argument
+ *
+ *  @see Utility::utility_job( JobContext& )
+ */
+bool
+ChapterUtility::utility_job( JobContext& job )
+{
+    if( !_action )
+    {
+        return herrf( "no action specified\n" );
+    }
+
+    return (this->*_action)( job );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** process command-line option
+ *
+ *  @see Utility::utility_option( int, bool& )
+ */
+bool
+ChapterUtility::utility_option( int code, bool& handled )
+{
+    handled = true;
+
+    switch( code ) {
+        case 'A':
+        case LC_CHPT_ANY:
+            _ChapterType = MP4ChapterTypeAny;
+            break;
+
+        case 'Q':
+        case LC_CHPT_QT:
+            _ChapterType = MP4ChapterTypeQt;
+            break;
+
+        case 'N':
+        case LC_CHPT_NERO:
+            _ChapterType = MP4ChapterTypeNero;
+            break;
+
+        case 'C':
+        case LC_CHPT_COMMON:
+            _ChapterFormat = CHPT_FMT_COMMON;
+            break;
+
+        case 'l':
+        case LC_CHP_LIST:
+            _action = &ChapterUtility::actionList;
+            break;
+
+        case 'e':
+        case LC_CHP_EVERY:
+        {
+            istringstream iss( prog::optarg );
+            iss >> _ChaptersEvery;
+            if( iss.rdstate() != ios::eofbit )
+            {
+                return herrf( "invalid number of seconds: %s\n", prog::optarg );
+            }
+            _action = &ChapterUtility::actionEvery;
+            break;
+        }
+
+        case 'x':
+            _action = &ChapterUtility::actionExport;
+            break;
+
+        case LC_CHP_EXPORT:
+            _action = &ChapterUtility::actionExport;
+            /* currently not supported since the chapters of n input files would be written to one chapter file
+            _ChapterFile = prog::optarg;
+            if( _ChapterFile.empty() )
+            {
+                return herrf( "invalid TXT file: empty-string\n" );
+            }
+            */
+            break;
+
+        case 'i':
+            _action = &ChapterUtility::actionImport;
+            break;
+
+        case LC_CHP_IMPORT:
+            _action = &ChapterUtility::actionImport;
+            /* currently not supported since the chapters of n input files would be read from one chapter file
+            _ChapterFile = prog::optarg;
+            if( _ChapterFile.empty() )
+            {
+                return herrf( "invalid TXT file: empty-string\n" );
+            }
+            */
+            break;
+
+        case 'c':
+        case LC_CHP_CONVERT:
+            _action = &ChapterUtility::actionConvert;
+            break;
+
+        case 'r':
+        case LC_CHP_REMOVE:
+            _action = &ChapterUtility::actionRemove;
+            break;
+
+        default:
+            handled = false;
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Fix a QuickTime/iPod issue with long audio files.
+ *
+ *  This function checks if the <b>file</b> is a long audio file (more than
+ *  about 6 1/2 hours) and modifies the timescale if necessary to allow
+ *  playback of the file in QuickTime player and on some iPod models.
+ *
+ *  @param file the opened MP4 file
+ */
+void
+ChapterUtility::fixQtScale(MP4FileHandle file)
+{
+    // get around a QuickTime/iPod issue with storing the number of samples in a signed 32Bit value
+    if( INT_MAX < MP4GetDuration(file))
+    {
+        bool isVideoTrack = false;
+        if( MP4_IS_VALID_TRACK_ID(getReferencingTrack( file, isVideoTrack )) & isVideoTrack )
+        {
+            // if it is a video, everything is different
+            return;
+        }
+
+        // timescale too high, lower it
+        MP4ChangeMovieTimeScale(file, 1000);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Finds a suitable track that can reference a chapter track.
+ *
+ *  This function returns the first video or audio track that is found
+ *  in the <b>file</b>.
+ *  This track ca be used to reference the QuickTime chapter track.
+ *
+ *  @param file the opened MP4 file
+ *  @param isVideoTrack receives true if the found track is video, false otherwise
+ *  @return the <b>MP4TrackId</b> of the found track
+ */
+MP4TrackId
+ChapterUtility::getReferencingTrack( MP4FileHandle file, bool& isVideoTrack )
+{
+    isVideoTrack = false;
+
+    uint32_t trackCount = MP4GetNumberOfTracks( file );
+    if( 0 == trackCount )
+    {
+        return MP4_INVALID_TRACK_ID;
+    }
+
+    MP4TrackId refTrackId = MP4_INVALID_TRACK_ID;
+    for( uint32_t i = 0; i < trackCount; ++i )
+    {
+        MP4TrackId    id = MP4FindTrackId( file, i );
+        const char* type = MP4GetTrackType( file, id );
+        if( MP4_IS_VIDEO_TRACK_TYPE( type ) )
+        {
+            refTrackId = id;
+            isVideoTrack = true;
+            break;
+        }
+        else if( MP4_IS_AUDIO_TRACK_TYPE( type ) )
+        {
+            refTrackId = id;
+            break;
+        }
+    }
+
+    return refTrackId;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Return a human readable representation of a <b>MP4ChapterType</b>.
+ *
+ *  @param chapterType the chapter type
+ *  @return a string representing the chapter type
+ */
+string
+ChapterUtility::getChapterTypeName( MP4ChapterType chapterType) const
+{
+    switch( chapterType )
+    {
+    case MP4ChapterTypeQt:
+        return string( "QuickTime" );
+        break;
+
+    case MP4ChapterTypeNero:
+        return string( "Nero" );
+        break;
+
+    case MP4ChapterTypeAny:
+        return string( "QuickTime and Nero" );
+        break;
+
+    default:
+        return string( "Unknown" );
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Read a file into a buffer.
+ *
+ *  This function reads the file named by <b>filename</b> into a buffer allocated
+ *  by malloc and returns the pointer to this buffer in <b>buffer</b> and the size
+ *  of this buffer in <b>fileSize</b>.
+ *
+ *  @param filename  the name of the file.
+ *  @param buffer    receives a pointer to the created buffer
+ *  @param fileSize  reference to a <b>io::StdioFile::Size</b> that receives the size of the file
+ *  @return true if there was an error, false otherwise
+ */
+bool
+ChapterUtility::readChapterFile( const string filename, char** buffer, File::Size& fileSize )
+{
+    // open the file
+    File in( filename, File::MODE_READ );
+    File::Size nin;
+    if( in.open() ) {
+        return herrf( "opening chapter file '%s' failed: %s\n", filename.c_str(), sys::getLastErrorStr() );
+    }
+
+    // get the file size
+    fileSize = in.size;
+    if( 0 >= fileSize )
+    {
+        in.close();
+        return herrf( "getting size of chapter file '%s' failed: %s\n", filename.c_str(), sys::getLastErrorStr() );
+    }
+
+    // allocate a buffer for the file and read the content
+    char* inBuf = static_cast<char*>( malloc( fileSize+1 ) );
+    if( in.read( inBuf, fileSize, nin ) )
+    {
+        in.close();
+        free(inBuf);
+        return herrf( "reading chapter file '%s' failed: %s\n", filename.c_str(), sys::getLastErrorStr() );
+    }
+    in.close();
+    inBuf[fileSize] = 0;
+
+    *buffer = inBuf;
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Read and parse a chapter file.
+ *
+ *  This function reads and parses a chapter file and returns a vector of
+ *  <b>MP4Chapter_t</b> elements.
+ *
+ *  @param filename the name of the file.
+ *  @param vector   receives a vector of chapters
+ *  @param format   receives the <b>Timecode::Format</b> of the timestamps
+ *  @return true if there was an error, false otherwise
+ */
+bool
+ChapterUtility::parseChapterFile( const string filename, vector<MP4Chapter_t>& chapters, Timecode::Format& format )
+{
+    // get the content
+    char * inBuf;
+    File::Size fileSize;
+    if( readChapterFile( filename, &inBuf, fileSize ) )
+    {
+        return FAILURE;
+    }
+
+    // separate the text lines
+    char* pos = inBuf;
+    while (pos < inBuf + fileSize)
+    {
+        if (*pos == '\n' || *pos == '\r')
+        {
+            *pos = 0;
+            if (pos > inBuf)
+            {
+                // remove trailing whitespace
+                char* tmp = pos-1;
+                while ((*tmp == ' ' || *tmp == '\t') && tmp > inBuf)
+                {
+                    *tmp = 0;
+                    tmp--;
+                }
+            }
+        }
+        pos++;
+    }
+    pos = inBuf;
+
+    // check for a BOM
+    char bom[5] = {0};
+    int bomLen = 0;
+    const unsigned char* uPos = reinterpret_cast<unsigned char*>( pos );
+    if( 0xEF == *uPos && 0xBB == *(uPos+1) && 0xBF == *(uPos+2) )
+    {
+        // UTF-8 (we do not need the BOM)
+        pos += 3;
+    }
+    else if(   ( 0xFE == *uPos && 0xFF == *(uPos+1) )   // UTF-16 big endian
+            || ( 0xFF == *uPos && 0xFE == *(uPos+1) ) ) // UTF-16 little endian
+    {
+        // store the BOM to prepend the title strings
+        bom[0] = *pos++;
+        bom[1] = *pos++;
+        bomLen = 2;
+        return herrf( "chapter file '%s' has UTF-16 encoding which is not supported (only UTF-8 is allowed)\n",
+                      filename.c_str() );
+    }
+    else if(   ( 0x0 == *uPos && 0x0 == *(uPos+1) && 0xFE == *(uPos+2) && 0xFF == *(uPos+3) )   // UTF-32 big endian
+            || ( 0xFF == *uPos && *(uPos+1) == 0xFE && *(uPos+2) == 0x0 && 0x0 == *(uPos+3) ) ) // UTF-32 little endian
+    {
+        // store the BOM to prepend the title strings
+        bom[0] = *pos++;
+        bom[1] = *pos++;
+        bom[2] = *pos++;
+        bom[3] = *pos++;
+        bomLen = 4;
+        return herrf( "chapter file '%s' has UTF-32 encoding which is not supported (only UTF-8 is allowed)\n",
+                      filename.c_str() );
+    }
+
+    // parse the lines
+    bool failure = false;
+    uint32_t currentChapter = 0;
+    FormatState formatState = FMT_STATE_INITIAL;
+    char* titleStart = 0;
+    uint32_t titleLen = 0;
+    char* timeStart = 0;
+    while( pos < inBuf + fileSize )
+    {
+        if( 0 == *pos || ' ' == *pos || '\t' == *pos )
+        {
+            // uninteresting chars
+            pos++;
+            continue;
+        }
+        else if( '#' == *pos )
+        {
+            // comment line
+            pos += strlen( pos );
+            continue;
+        }
+        else if( isdigit( *pos ) )
+        {
+            // mp4chaps native format: hh:mm:ss.sss <title>
+
+            timeStart = pos;
+
+            // read the title if there is one
+            titleStart = strchr( timeStart, ' ' );
+            if( NULL == titleStart )
+            {
+                titleStart = strchr( timeStart, '\t' );
+            }
+
+            if( NULL != titleStart )
+            {
+                *titleStart = 0;
+                pos = ++titleStart;
+
+                while( ' ' == *titleStart || '\t' == *titleStart )
+                {
+                    titleStart++;
+                }
+
+                titleLen = (uint32_t)strlen( titleStart );
+
+                // advance to the end of the line
+                pos = titleStart + 1 + titleLen;
+            }
+            else
+            {
+                // advance to the end of the line
+                pos += strlen( pos );
+            }
+
+            formatState = FMT_STATE_FINISH;
+        }
+#if defined( _MSC_VER )
+        else if( 0 == strnicmp( pos, "CHAPTER", 7 ) )
+#else
+        else if( 0 == strncasecmp( pos, "CHAPTER", 7 ) )
+#endif
+        {
+            // common format: CHAPTERxx=hh:mm:ss.sss\nCHAPTERxxNAME=<title>
+
+            char* equalsPos = strchr( pos+7, '=' );
+            if( NULL == equalsPos )
+            {
+                herrf( "Unable to parse line \"%s\"\n", pos );
+                failure = true;
+                break;
+            }
+
+            *equalsPos = 0;
+
+            char* tlwr = pos;
+            while( equalsPos != tlwr )
+            {
+                *tlwr = tolower( *tlwr );
+                tlwr++;
+            }
+
+            if( NULL != strstr( pos, "name" ) )
+            {
+                // mark the chapter title
+                uint32_t chNr = 0;
+                sscanf( pos, "chapter%dname", &chNr );
+                if( chNr != currentChapter )
+                {
+                    // different chapter number => different chapter definition pair
+                    if( FMT_STATE_INITIAL != formatState )
+                    {
+                        herrf( "Chapter lines are not consecutive before line \"%s\"\n", pos );
+                        failure = true;
+                        break;
+                    }
+
+                    currentChapter = chNr;
+                }
+                formatState = FMT_STATE_TIME_LINE == formatState ? FMT_STATE_FINISH
+                                                                 : FMT_STATE_TITLE_LINE;
+
+                titleStart = equalsPos + 1;
+                titleLen = (uint32_t)strlen( titleStart );
+
+                // advance to the end of the line
+                pos = titleStart + titleLen;
+            }
+            else
+            {
+                // mark the chapter start time
+                uint32_t chNr = 0;
+                sscanf( pos, "chapter%d", &chNr );
+                if( chNr != currentChapter )
+                {
+                    // different chapter number => different chapter definition pair
+                    if( FMT_STATE_INITIAL != formatState )
+                    {
+                        herrf( "Chapter lines are not consecutive at line \"%s\"\n", pos );
+                        failure = true;
+                        break;
+                    }
+
+                    currentChapter = chNr;
+                }
+                formatState = FMT_STATE_TITLE_LINE == formatState ? FMT_STATE_FINISH 
+                                                                  : FMT_STATE_TIME_LINE;
+
+                timeStart = equalsPos + 1;
+
+                // advance to the end of the line
+                pos = timeStart + strlen( timeStart );
+            }
+        }
+
+        if( FMT_STATE_FINISH == formatState )
+        {
+            // now we have title and start time
+            MP4Chapter_t chap;
+
+            strncpy( chap.title, titleStart, min( titleLen, (uint32_t)MP4V2_CHAPTER_TITLE_MAX ) );
+            chap.title[titleLen] = 0;
+
+            Timecode tc( 0, CHAPTERTIMESCALE );
+            string tm( timeStart );
+            if( tc.parse( tm ) )
+            {
+                herrf( "Unable to parse time code from \"%s\"\n", tm.c_str() );
+                failure = true;
+                break;
+            }
+            chap.duration = tc.duration;
+            format = tc.format;
+
+            // ad the chapter to the list
+            chapters.push_back( chap );
+
+            // re-initialize
+            formatState = FMT_STATE_INITIAL;
+            titleStart = timeStart = NULL;
+            titleLen = 0;
+        }
+    }
+    free( inBuf );
+    if( failure )
+    {
+        return failure;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Convert from frame to millisecond timestamp.
+ *
+ *  This function converts a timestamp from hh:mm:ss:ff to hh:mm:ss.sss
+ *
+ *  @param duration  the timestamp in hours:minutes:seconds:frames.
+ *  @param framerate the frames per second
+ *  @return the timestamp in milliseconds
+ */
+MP4Duration
+ChapterUtility::convertFrameToMillis( MP4Duration duration, uint32_t framerate )
+{
+    Timecode tc( duration, CHAPTERTIMESCALE );
+    if( framerate < tc.subseconds )
+    {
+        uint64_t seconds = tc.subseconds / framerate;
+        tc.setSeconds( tc.seconds + seconds );
+        tc.setSubseconds( (tc.subseconds - (seconds * framerate)) * framerate );
+    }
+    else
+    {
+        tc.setSubseconds( tc.subseconds * framerate );
+    }
+
+    return tc.duration;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C"
+int main( int argc, char** argv )
+{
+    mp4v2::util::ChapterUtility util( argc, argv );
+    return util.process();
+}
diff --git a/util/mp4extract.cpp b/util/mp4extract.cpp
new file mode 100644
index 0000000..1e3c232
--- /dev/null
+++ b/util/mp4extract.cpp
@@ -0,0 +1,252 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+// N.B. mp4extract just extracts tracks/samples from an mp4 file
+// For many track types this is insufficient to reconsruct a valid
+// elementary stream (ES). Use "mp4creator -extract=<trackId>" if
+// you need the ES reconstructed.
+
+#include "util/impl.h"
+
+using namespace mp4v2::util;
+
+char* ProgName;
+char* Mp4PathName;
+char* Mp4FileName;
+
+// forward declaration
+void ExtractTrack( MP4FileHandle mp4File, MP4TrackId trackId,
+                   bool sampleMode, MP4SampleId sampleId, char* dstFileName = NULL );
+
+extern "C" int main( int argc, char** argv )
+{
+    const char* const usageString =
+        "[-l] [-t <track-id>] [-s <sample-id>] [-v [<level>]] <file-name>";
+    bool doList = false;
+    bool doSamples = false;
+    MP4TrackId trackId = MP4_INVALID_TRACK_ID;
+    MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID;
+    char* dstFileName = NULL;
+    MP4LogLevel verbosity = MP4_LOG_ERROR;
+
+    /* begin processing command line */
+    ProgName = argv[0];
+    while ( true ) {
+        int c = -1;
+        int option_index = 0;
+        static const prog::Option long_options[] = {
+            { "list",    prog::Option::NO_ARG,       0, 'l' },
+            { "track",   prog::Option::REQUIRED_ARG, 0, 't' },
+            { "sample",  prog::Option::OPTIONAL_ARG, 0, 's' },
+            { "verbose", prog::Option::OPTIONAL_ARG, 0, 'v' },
+            { "version", prog::Option::NO_ARG,       0, 'V' },
+            { NULL, prog::Option::NO_ARG, 0, 0 }
+        };
+
+        c = prog::getOptionSingle( argc, argv, "lt:s::v::V", long_options, &option_index );
+
+        if ( c == -1 )
+            break;
+
+        switch ( c ) {
+            case 'l':
+                doList = true;
+                break;
+            case 's':
+                doSamples = true;
+                if ( prog::optarg ) {
+                    if ( sscanf( prog::optarg, "%u", &sampleId ) != 1 ) {
+                        fprintf( stderr,
+                                 "%s: bad sample-id specified: %s\n",
+                                 ProgName, prog::optarg );
+                    }
+                }
+                break;
+            case 't':
+                if ( sscanf( prog::optarg, "%u", &trackId ) != 1 ) {
+                    fprintf( stderr,
+                             "%s: bad track-id specified: %s\n",
+                             ProgName, prog::optarg );
+                    exit( 1 );
+                }
+                break;
+            case 'v':
+                verbosity = MP4_LOG_VERBOSE1;
+                if ( prog::optarg ) {
+                    uint32_t level;
+                    if ( sscanf( prog::optarg, "%u", &level ) == 1 ) {
+                        if ( level >= 2 ) {
+                            verbosity = MP4_LOG_VERBOSE2;
+                        }
+                        if ( level >= 3 ) {
+                            verbosity = MP4_LOG_VERBOSE3;
+                        }
+                        if ( level >= 4 ) {
+                            verbosity = MP4_LOG_VERBOSE4;
+                        }
+                    }
+                }
+                break;
+            case '?':
+                fprintf( stderr, "usage: %s %s\n", ProgName, usageString );
+                exit( 0 );
+            case 'V':
+                fprintf( stderr, "%s - %s\n", ProgName, MP4V2_PROJECT_name_formal );
+                exit( 0 );
+            default:
+                fprintf( stderr, "%s: unknown option specified, ignoring: %c\n",
+                         ProgName, c );
+        }
+    }
+
+    /* check that we have at least one non-option argument */
+    if ( ( argc - prog::optind ) < 1 ) {
+        fprintf( stderr, "usage: %s %s\n", ProgName, usageString );
+        exit( 1 );
+    }
+
+    MP4LogSetLevel(verbosity);
+    if ( verbosity ) {
+        fprintf( stderr, "%s version %s\n", ProgName, MP4V2_PROJECT_version );
+    }
+
+    /* point to the specified file names */
+    Mp4PathName = argv[prog::optind++];
+
+    /* get dest file name for a single track */
+    if ( trackId && ( argc - prog::optind ) > 0 ) {
+        dstFileName = argv[prog::optind++];
+    }
+
+    char* lastSlash = strrchr( Mp4PathName, '/' );
+    if ( lastSlash ) {
+        Mp4FileName = lastSlash + 1;
+    }
+    else {
+        Mp4FileName = Mp4PathName;
+    }
+
+    /* warn about extraneous non-option arguments */
+    if ( prog::optind < argc ) {
+        fprintf( stderr, "%s: unknown options specified, ignoring: ", ProgName );
+        while ( prog::optind < argc ) {
+            fprintf( stderr, "%s ", argv[prog::optind++] );
+        }
+        fprintf( stderr, "\n" );
+    }
+
+    /* end processing of command line */
+
+
+    MP4FileHandle mp4File = MP4Read( Mp4PathName );
+
+    if ( !mp4File ) {
+        exit( 1 );
+    }
+
+    if ( doList ) {
+        MP4Info( mp4File );
+        exit( 0 );
+    }
+
+    if ( trackId == 0 ) {
+        uint32_t numTracks = MP4GetNumberOfTracks( mp4File );
+
+        for ( uint32_t i = 0; i < numTracks; i++ ) {
+            trackId = MP4FindTrackId( mp4File, i );
+            ExtractTrack( mp4File, trackId, doSamples, sampleId );
+        }
+    }
+    else {
+        ExtractTrack( mp4File, trackId, doSamples, sampleId, dstFileName );
+    }
+
+    MP4Close( mp4File );
+
+    return( 0 );
+}
+
+void ExtractTrack( MP4FileHandle mp4File, MP4TrackId trackId,
+                   bool sampleMode, MP4SampleId sampleId, char* dstFileName )
+{
+    static string outName;
+    File out;
+
+    if( !sampleMode ) {
+        if( !dstFileName ) {
+            stringstream ss;
+            ss << Mp4FileName << ".t" << trackId;
+            outName = ss.str();
+        } else {
+            outName = dstFileName;
+        }
+
+        if( out.open( outName.c_str(), File::MODE_CREATE )) {
+            fprintf( stderr, "%s: can't open %s: %s\n", ProgName, outName.c_str(), sys::getLastErrorStr() );
+            return;
+        }
+    }
+
+    MP4SampleId numSamples;
+
+    if ( sampleMode && sampleId != MP4_INVALID_SAMPLE_ID ) {
+        numSamples = sampleId;
+    }
+    else {
+        sampleId = 1;
+        numSamples = MP4GetTrackNumberOfSamples( mp4File, trackId );
+    }
+
+    for ( ; sampleId <= numSamples; sampleId++ ) {
+        // signals to ReadSample() that it should malloc a buffer for us
+        uint8_t* pSample = NULL;
+        uint32_t sampleSize = 0;
+
+        if( !MP4ReadSample( mp4File, trackId, sampleId, &pSample, &sampleSize )) {
+            fprintf( stderr, "%s: read sample %u for %s failed\n", ProgName, sampleId, outName.c_str() );
+            break;
+        }
+
+        if ( sampleMode ) {
+            stringstream ss;
+            ss << Mp4FileName << ".t" << trackId << ".s" << sampleId;
+            outName = ss.str();
+
+            if( out.open( outName.c_str(), File::MODE_CREATE )) {
+                fprintf( stderr, "%s: can't open %s: %s\n", ProgName, outName.c_str(), sys::getLastErrorStr() );
+                break;
+            }
+        }
+
+        File::Size nout;
+        if( out.write( pSample, sampleSize, nout )) {
+            fprintf( stderr, "%s: write to %s failed: %s\n", ProgName, outName.c_str(), sys::getLastErrorStr() );
+            break;
+        }
+
+        free( pSample );
+
+        if( sampleMode )
+            out.close();
+    }
+
+    out.close();
+}
diff --git a/util/mp4file.cpp b/util/mp4file.cpp
new file mode 100644
index 0000000..c27844b
--- /dev/null
+++ b/util/mp4file.cpp
@@ -0,0 +1,224 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2010.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "util/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FileUtility : public Utility
+{
+private:
+    enum FileLongCode {
+        LC_LIST = _LC_MAX,
+        LC_OPTIMIZE,
+        LC_DUMP,
+    };
+
+public:
+    FileUtility( int, char** );
+
+protected:
+    // delegates implementation
+    bool utility_option( int, bool& );
+    bool utility_job( JobContext& );
+
+private:
+    bool actionList     ( JobContext& );
+    bool actionOptimize ( JobContext& );
+    bool actionDump     ( JobContext& );
+
+private:
+    Group _actionGroup;
+
+    bool (FileUtility::*_action)( JobContext& );
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+FileUtility::FileUtility( int argc, char** argv )
+    : Utility      ( "mp4file", argc, argv )
+    , _actionGroup ( "ACTIONS" )
+    , _action      ( NULL )
+{
+    // add standard options which make sense for this utility
+    _group.add( STD_DRYRUN );
+    _group.add( STD_KEEPGOING );
+    _group.add( STD_QUIET );
+    _group.add( STD_DEBUG );
+    _group.add( STD_VERBOSE );
+    _group.add( STD_HELP );
+    _group.add( STD_VERSION );
+    _group.add( STD_VERSIONX );
+
+    _actionGroup.add( "list",     false, LC_LIST,     "list (summary information)" );
+    _actionGroup.add( "optimize", false, LC_OPTIMIZE, "optimize mp4 structure" );
+    _actionGroup.add( "dump",     false, LC_DUMP,     "dump mp4 structure in human-readable format" );
+    _groups.push_back( &_actionGroup );
+
+    _usage = "[OPTION]... ACTION file...";
+    _description =
+        // 79-cols, inclusive, max desired width
+        // |----------------------------------------------------------------------------|
+        "\nFor each mp4 file specified, perform the specified ACTION. An action must be"
+        "\nspecified. Some options are not applicable to some actions.";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileUtility::actionDump( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    if( !MP4Dump( job.fileHandle, _debugImplicits ))
+        return herrf( "dump failed: %s\n", job.file.c_str() );
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileUtility::actionList( JobContext& job )
+{
+    ostringstream report;
+
+    const int wbrand = 5;
+    const int wcompat = 18;
+    const int wsizing = 6;
+    const string sep = "  ";
+
+    if( _jobCount == 0 ) {
+        report << setw(wbrand) << left << "BRAND" 
+               << sep << setw(wcompat) << left << "COMPAT" 
+               << sep << setw(wsizing) << left << "SIZING" 
+               << sep << setw(0) << "FILE"
+               << '\n';
+
+        report << setfill('-') << setw(70) << "" << setfill(' ') << '\n';
+    }
+
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    FileSummaryInfo info;
+    if( fileFetchSummaryInfo( job.fileHandle, info ))
+        return herrf( "unable to fetch file summary info" );
+
+    string compat;
+    {
+        const FileSummaryInfo::BrandSet::iterator ie = info.compatible_brands.end();
+        int count = 0;
+        for( FileSummaryInfo::BrandSet::iterator it = info.compatible_brands.begin(); it != ie; it++, count++ ) {
+            if( count > 0 )
+                compat += ',';
+            compat += *it;
+        }
+    }
+
+    const bool sizing = info.nlargesize | info.nversion1 | info.nspecial;
+
+    report << setw(wbrand) << left << info.major_brand
+           << sep << setw(wcompat) << left << compat
+           << sep << setw(wsizing) << left << (sizing ? "64-bit" : "32-bit")
+           << sep << job.file
+           << '\n';
+
+    verbose1f( "%s", report.str().c_str() );
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileUtility::actionOptimize( JobContext& job )
+{
+    verbose1f( "optimizing %s\n", job.file.c_str() );
+
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    if( !MP4Optimize( job.file.c_str(), NULL ))
+        return herrf( "optimize failed: %s\n", job.file.c_str() );
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileUtility::utility_job( JobContext& job )
+{
+    if( !_action )
+        return herrf( "no action specified\n" );
+
+    return (this->*_action)( job );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+FileUtility::utility_option( int code, bool& handled )
+{
+    handled = true;
+
+    switch( code ) {
+        case LC_LIST:
+            _action = &FileUtility::actionList;
+            break;
+
+        case LC_OPTIMIZE:
+            _action = &FileUtility::actionOptimize;
+            break;
+
+        case LC_DUMP:
+            _action = &FileUtility::actionDump;
+            break;
+
+        default:
+            handled = false;
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C"
+int main( int argc, char** argv )
+{
+    mp4v2::util::FileUtility util( argc, argv );
+    return util.process();
+}
diff --git a/util/mp4info.cpp b/util/mp4info.cpp
new file mode 100644
index 0000000..4c1c766
--- /dev/null
+++ b/util/mp4info.cpp
@@ -0,0 +1,249 @@
+/*
+ * 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-2002.  All Rights Reserved.
+ *
+ * Portions created by Rouven Wessling are
+ * Copyright (C) 2008-2009. All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ *      Rouven Wessling mp4v2@rouvenwessling.de
+ */
+
+#include "util/impl.h"
+
+using namespace mp4v2::util;
+
+extern "C" int main( int argc, char** argv )
+{
+    const char* const usageString =
+        "<file-name>";
+
+    /* begin processing command line */
+    char* ProgName = argv[0];
+    while ( true ) {
+        int c = -1;
+        int option_index = 0;
+        static const prog::Option long_options[] = {
+            { "version", prog::Option::NO_ARG, 0, 'V' },
+            { NULL, prog::Option::NO_ARG, 0, 0 }
+        };
+
+        c = prog::getOptionSingle( argc, argv, "V", long_options, &option_index );
+
+        if ( c == -1 )
+            break;
+
+        switch ( c ) {
+            case '?':
+                fprintf( stderr, "usage: %s %s\n", ProgName, usageString );
+                exit( 0 );
+            case 'V':
+                fprintf( stderr, "%s - %s\n", ProgName, MP4V2_PROJECT_name_formal );
+                exit( 0 );
+            default:
+                fprintf( stderr, "%s: unknown option specified, ignoring: %c\n",
+                         ProgName, c );
+        }
+    }
+
+    /* check that we have at least one non-option argument */
+    if ( ( argc - prog::optind ) < 1 ) {
+        fprintf( stderr, "usage: %s %s\n", ProgName, usageString );
+        exit( 1 );
+    }
+
+    /* end processing of command line */
+    printf( "%s version %s\n", ProgName, MP4V2_PROJECT_version );
+
+    while ( prog::optind < argc ) {
+        char *mp4FileName = argv[prog::optind++];
+
+        printf( "%s:\n", mp4FileName );
+
+        char* info = MP4FileInfo( mp4FileName );
+
+        if ( !info ) {
+            fprintf( stderr,
+                     "%s: can't open %s\n",
+                     ProgName, mp4FileName );
+            continue;
+        }
+
+        fputs( info, stdout );
+        MP4FileHandle mp4file = MP4Read( mp4FileName ); //, MP4_DETAILS_ERROR);
+        if ( mp4file != MP4_INVALID_FILE_HANDLE ) {
+            const MP4Tags* tags = MP4TagsAlloc();
+            MP4TagsFetch( tags, mp4file );
+            if ( tags->name ) {
+                fprintf( stdout, " Name: %s\n", tags->name );
+            }
+            if ( tags->sortName ) {
+                fprintf( stdout, " Sort Name: %s\n", tags->sortName );
+            }
+            if ( tags->artist ) {
+                fprintf( stdout, " Artist: %s\n", tags->artist );
+            }
+            if ( tags->sortArtist ) {
+                fprintf( stdout, " Sort Artist: %s\n", tags->sortArtist );
+            }
+            if ( tags->composer ) {
+                fprintf( stdout, " Composer: %s\n", tags->composer );
+            }
+            if ( tags->sortComposer ) {
+                fprintf( stdout, " Sort Composer: %s\n", tags->sortComposer );
+            }
+            if ( tags->encodingTool ) {
+                fprintf( stdout, " Encoded with: %s\n", tags->encodingTool );
+            }
+            if ( tags->encodedBy ) {
+                fprintf( stdout, " Encoded by: %s\n", tags->encodedBy );
+            }
+            if ( tags->releaseDate ) {
+                fprintf( stdout, " Release Date: %s\n", tags->releaseDate );
+            }
+            if ( tags->album ) {
+                fprintf( stdout, " Album: %s\n", tags->album );
+            }
+            if ( tags->sortAlbum ) {
+                fprintf( stdout, " Sort Album: %s\n", tags->sortAlbum );
+            }
+            if ( tags->track ) {
+                fprintf( stdout, " Track: %u of %u\n", tags->track->index, tags->track->total );
+            }
+            if ( tags->disk ) {
+                fprintf( stdout, " Disk: %u of %u\n", tags->disk->index, tags->disk->total );
+            }
+            if ( tags->genre ) {
+                fprintf( stdout, " Genre: %s\n", tags->genre );
+            }
+            if ( tags->genreType ) {
+                string s = itmf::enumGenreType.toString(static_cast<itmf::GenreType>(*tags->genreType ), true);
+                fprintf( stdout, " GenreType: %u, %s\n", *tags->genreType, s.c_str() );
+            }
+            if ( tags->grouping ) {
+                fprintf( stdout, " Grouping: %s\n", tags->grouping );
+            }
+            if ( tags->tempo ) {
+                fprintf( stdout, " BPM: %u\n", *tags->tempo );
+            }
+            if ( tags->comments ) {
+                fprintf( stdout, " Comments: %s\n", tags->comments );
+            }
+            if ( tags->compilation ) {
+                fprintf( stdout, " Part of Compilation: %s\n", *tags->compilation ? "yes" : "no" );
+            }
+            if ( tags->gapless ) {
+                fprintf( stdout, " Part of Gapless Album: %s\n", *tags->gapless ? "yes" : "no" );
+            }
+            if ( tags->artworkCount ) {
+                fprintf( stdout, " Cover Art pieces: %u\n", tags->artworkCount );
+            }
+            if ( tags->albumArtist ) {
+                fprintf( stdout, " Album Artist: %s\n", tags->albumArtist );
+            }
+            if ( tags->sortAlbumArtist ) {
+                fprintf( stdout, " Sort Album Artist: %s\n", tags->sortAlbumArtist );
+            }
+            if ( tags->copyright ) {
+                fprintf( stdout, " Copyright: %s\n", tags->copyright );
+            }
+            if ( tags->contentRating ) {
+                string s = itmf::enumContentRating.toString( static_cast<itmf::ContentRating>( *tags->contentRating ), true );
+                fprintf( stdout, " Content Rating: %s\n", s.c_str() );
+            }
+            if ( tags->hdVideo ) {
+                fprintf( stdout, " HD Video: %s\n", *tags->hdVideo ? "yes" : "no");
+            }
+            if ( tags->mediaType ) {
+                string s = itmf::enumStikType.toString( static_cast<itmf::StikType>( *tags->mediaType ), true );
+                fprintf( stdout, " Media Type: %s\n", s.c_str() );
+            }
+            if ( tags->tvShow ) {
+                fprintf( stdout, " TV Show: %s\n", tags->tvShow );
+            }
+            if ( tags->sortTVShow ) {
+                fprintf( stdout, " Sort TV Show: %s\n", tags->sortTVShow );
+            }
+            if ( tags->tvNetwork ) {
+                fprintf( stdout, " TV Network: %s\n", tags->tvNetwork );
+            }
+            if ( tags->tvEpisodeID ) {
+                fprintf( stdout, " TV Episode Number: %s\n", tags->tvEpisodeID );
+            }
+            if ( tags->description ) {
+                fprintf( stdout, " Short Description: %s\n", tags->description );
+            }
+            if ( tags->longDescription ) {
+                fprintf( stdout, " Long Description: %s\n", tags->longDescription );
+            }
+            if ( tags->lyrics ) {
+                fprintf( stdout, " Lyrics: \n %s\n", tags->lyrics );
+            }
+            if ( tags->tvEpisode ) {
+                fprintf( stdout, " TV Episode: %u\n", *tags->tvEpisode );
+            }
+            if ( tags->tvSeason ) {
+                fprintf( stdout, " TV Season: %u\n", *tags->tvSeason );
+            }
+            if ( tags->podcast) {
+                fprintf( stdout, " Podcast: %s\n", *tags->podcast ? "yes" : "no" );
+            }
+            if ( tags->keywords ) {
+                fprintf( stdout, " Keywords: %s\n", tags->keywords );
+            }
+            if ( tags->category ) {
+                fprintf( stdout, " Category: %s\n", tags->category );
+            }
+            if ( tags->contentID ) {
+                fprintf( stdout, " Content ID: %u\n", *tags->contentID );
+            }
+            if ( tags->artistID ) {
+                fprintf( stdout, " Artist ID: %u\n", *tags->artistID );
+            }
+            if ( tags->playlistID ) {
+                fprintf( stdout, " Playlist ID: %llu\n", *tags->playlistID );
+            }
+            if ( tags->genreID ) {
+                fprintf( stdout, " Genre ID: %u\n", *tags->genreID );
+            }
+            if ( tags->composerID ) {
+                fprintf( stdout, " Composer ID: %u\n", *tags->composerID );
+            }
+            if ( tags->xid ) {
+                fprintf( stdout, " xid: %s\n", tags->xid );
+            }
+            if ( tags->iTunesAccount ) {
+                fprintf( stdout, " iTunes Account: %s\n", tags->iTunesAccount );
+            }
+            if ( tags->iTunesAccountType ) {
+                string s = itmf::enumAccountType.toString( static_cast<itmf::AccountType>( *tags->iTunesAccountType ), true );
+                fprintf( stdout, " iTunes Account Type: %s\n", s.c_str() );
+            }
+            if ( tags->purchaseDate ) {
+                fprintf( stdout, " Purchase Date: %s\n", tags->purchaseDate );
+            }
+            if ( tags->iTunesCountry ) {
+                string s = itmf::enumCountryCode.toString( static_cast<itmf::CountryCode>( *tags->iTunesCountry ), true );
+                fprintf( stdout, " iTunes Store Country: %s\n", s.c_str() );
+            }
+            MP4TagsFree( tags );
+            MP4Close( mp4file );
+        }
+        free( info );
+    }
+    return( 0 );
+}
diff --git a/util/mp4subtitle.cpp b/util/mp4subtitle.cpp
new file mode 100644
index 0000000..7462153
--- /dev/null
+++ b/util/mp4subtitle.cpp
@@ -0,0 +1,209 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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
+//      Edward Groenendaal, egroenen@@cisco.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "util/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SubtitleUtility : public Utility
+{
+private:
+    enum SubtitleLongCode {
+        LC_LIST = _LC_MAX,
+        LC_EXPORT,
+        LC_IMPORT,
+        LC_REMOVE,
+    };
+
+public:
+    SubtitleUtility( int, char** );
+
+protected:
+    // delegates implementation
+    bool utility_option( int, bool& );
+    bool utility_job( JobContext& );
+
+private:
+    bool actionList   ( JobContext& );
+    bool actionExport ( JobContext& );
+    bool actionImport ( JobContext& );
+    bool actionRemove ( JobContext& );
+
+private:
+    Group  _actionGroup;
+
+    bool (SubtitleUtility::*_action)( JobContext& );
+
+    string _stTextFile;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SubtitleUtility::SubtitleUtility( int argc, char** argv )
+    : Utility      ( "mp4subtitle", argc, argv )
+    , _actionGroup ( "ACTIONS" )
+    , _action      ( NULL )
+{
+    // add standard options which make sense for this utility
+    _group.add( STD_OPTIMIZE );
+    _group.add( STD_DRYRUN );
+    _group.add( STD_KEEPGOING );
+    _group.add( STD_OVERWRITE );
+    _group.add( STD_FORCE );
+    _group.add( STD_QUIET );
+    _group.add( STD_DEBUG );
+    _group.add( STD_VERBOSE );
+    _group.add( STD_HELP );
+    _group.add( STD_VERSION );
+    _group.add( STD_VERSIONX );
+
+    _actionGroup.add( "list",   false, LC_LIST,   "list available subtitles" );
+    _actionGroup.add( "export", true,  LC_EXPORT, "export subtitles to TXT", "TXT" );
+    _actionGroup.add( "import", true,  LC_IMPORT, "import subtitles from TXT", "TXT" );
+    _actionGroup.add( "remove", false, LC_REMOVE, "remove all subtitles" );
+    _groups.push_back( &_actionGroup );
+
+    _usage = "[OPTION]... ACTION file...";
+    _description =
+        // 79-cols, inclusive, max desired width
+        // |----------------------------------------------------------------------------|
+        "\nFor each mp4 file specified, perform the specified ACTION. An action must be"
+        "\nspecified. Some options are not applicable to some actions.";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+SubtitleUtility::actionExport( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    verbose1f( "NOT IMPLEMENTED\n" );
+    return FAILURE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+SubtitleUtility::actionImport( JobContext& job )
+{
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    verbose1f( "NOT IMPLEMENTED\n" );
+    return FAILURE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+SubtitleUtility::actionList( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    verbose1f( "NOT IMPLEMENTED\n" );
+    return FAILURE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+SubtitleUtility::actionRemove( JobContext& job )
+{
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    verbose1f( "NOT IMPLEMENTED" );
+    return FAILURE;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+SubtitleUtility::utility_job( JobContext& job )
+{
+    if( !_action )
+        return herrf( "no action specified\n" );
+
+    return (this->*_action)( job );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+SubtitleUtility::utility_option( int code, bool& handled )
+{
+    handled = true;
+
+    switch( code ) {
+        case LC_LIST:
+            _action = &SubtitleUtility::actionList;
+            break;
+
+        case LC_EXPORT:
+            _action = &SubtitleUtility::actionExport;
+            _stTextFile = prog::optarg;
+            if( _stTextFile.empty() )
+                return herrf( "invalid TXT file: empty-string\n" );
+            break;
+
+        case LC_IMPORT:
+            _action = &SubtitleUtility::actionImport;
+            _stTextFile = prog::optarg;
+            if( _stTextFile.empty() )
+                return herrf( "invalid TXT file: empty-string\n" );
+            break;
+
+        case LC_REMOVE:
+            _action = &SubtitleUtility::actionRemove;
+            break;
+
+        default:
+            handled = false;
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C"
+int main( int argc, char** argv )
+{
+    mp4v2::util::SubtitleUtility util( argc, argv );
+    return util.process();
+}
diff --git a/util/mp4tags.cpp b/util/mp4tags.cpp
new file mode 100644
index 0000000..80e89d4
--- /dev/null
+++ b/util/mp4tags.cpp
@@ -0,0 +1,600 @@
+/* mp4tags -- tool to set iTunes-compatible metadata tags
+ *
+ * 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.
+ *
+ * Contributed to MPEG4IP
+ * by Christopher League <league@contrapunctus.net>
+ */
+
+#include "util/impl.h"
+
+using namespace mp4v2::util;
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* One-letter options -- if you want to rearrange these, change them
+   here, immediately below in OPT_STRING, and in the help text. */
+#define OPT_HELP         0x01ff
+#define OPT_VERSION      0x02ff
+#define OPT_REMOVE       'r'
+#define OPT_ALBUM        'A'
+#define OPT_ARTIST       'a'
+#define OPT_TEMPO        'b'
+#define OPT_COMMENT      'c'
+#define OPT_COPYRIGHT    'C'
+#define OPT_DISK         'd'
+#define OPT_DISKS        'D'
+#define OPT_ENCODEDBY    'e'
+#define OPT_TOOL         'E'
+#define OPT_GENRE        'g'
+#define OPT_GROUPING     'G'
+#define OPT_HD           'H'
+#define OPT_MEDIA_TYPE   'i'
+#define OPT_CONTENTID    'I'
+#define OPT_LONGDESC     'l'
+#define OPT_GENREID      'j'
+#define OPT_LYRICS       'L'
+#define OPT_DESCRIPTION  'm'
+#define OPT_TVEPISODE    'M'
+#define OPT_TVSEASON     'n'
+#define OPT_TVNETWORK    'N'
+#define OPT_TVEPISODEID  'o'
+#define OPT_CATEGORY     'O'
+#define OPT_PLAYLISTID   'p'
+#define OPT_PICTURE      'P'
+#define OPT_PODCAST      'B'
+#define OPT_ALBUM_ARTIST 'R'
+#define OPT_NAME         's'
+#define OPT_TVSHOW       'S'
+#define OPT_TRACK        't'
+#define OPT_TRACKS       'T'
+#define OPT_XID          'x'
+#define OPT_RATING       'X'
+#define OPT_COMPOSER     'w'
+#define OPT_RELEASEDATE  'y'
+#define OPT_ARTISTID     'z'
+#define OPT_COMPOSERID   'Z'
+
+#define OPT_STRING  "r:A:a:b:c:C:d:D:e:E:g:G:H:i:I:j:l:L:m:M:n:N:o:O:p:P:B:R:s:S:t:T:x:X:w:y:z:Z:"
+
+#define ELEMENT_OF(x,i) x[int(i)]
+
+static const char* const help_text =
+    "OPTION... FILE...\n"
+    "Adds or modifies iTunes-compatible tags on MP4 files.\n"
+    "\n"
+    "      -help            Display this help text and exit\n"
+    "      -version         Display version information and exit\n"
+    "  -A, -album       STR  Set the album title\n"
+    "  -a, -artist      STR  Set the artist information\n"
+    "  -b, -tempo       NUM  Set the tempo (beats per minute)\n"
+    "  -c, -comment     STR  Set a general comment\n"
+    "  -C, -copyright   STR  Set the copyright information\n"
+    "  -d, -disk        NUM  Set the disk number\n"
+    "  -D, -disks       NUM  Set the number of disks\n"
+    "  -e, -encodedby   STR  Set the name of the person or company who encoded the file\n"
+    "  -E, -tool        STR  Set the software used for encoding\n"
+    "  -g, -genre       STR  Set the genre name\n"
+    "  -G, -grouping    STR  Set the grouping name\n"
+    "  -H, -hdvideo     NUM  Set the HD flag (1\\0)\n"
+    "  -i, -type        STR  Set the Media Type(tvshow, movie, music, ...)\n"
+    "  -I, -contentid   NUM  Set the content ID\n"
+    "  -j, -genreid     NUM  Set the genre ID\n"
+    "  -l, -longdesc    STR  Set the long description\n"
+    "  -L, -lyrics      NUM  Set the lyrics\n"
+    "  -m, -description STR  Set the short description\n"
+    "  -M, -episode     NUM  Set the episode number\n"
+    "  -n, -season      NUM  Set the season number\n"
+    "  -N, -network     STR  Set the TV network\n"
+    "  -o, -episodeid   STR  Set the TV episode ID\n"
+	"  -O, -category    STR  Set the category\n"
+    "  -p, -playlistid  NUM  Set the playlist ID\n"
+    "  -P, -picture     PTH  Set the picture as a .png\n"
+    "  -B, -podcast     NUM  Set the podcast flag.\n"
+    "  -R, -albumartist STR  Set the album artist\n"
+    "  -s, -song        STR  Set the song title\n"
+    "  -S  -show        STR  Set the TV show\n"
+    "  -t, -track       NUM  Set the track number\n"
+    "  -T, -tracks      NUM  Set the number of tracks\n"
+    "  -x, -xid         STR  Set the globally-unique xid (vendor:scheme:id)\n"
+	"  -X, -rating      STR  Set the Rating(none, clean, explicit)\n"
+    "  -w, -writer      STR  Set the composer information\n"
+    "  -y, -year        NUM  Set the release date\n"
+    "  -z, -artistid    NUM  Set the artist ID\n"
+    "  -Z, -composerid  NUM  Set the composer ID\n"
+    "  -r, -remove      STR  Remove tags by code (e.g. \"-r cs\"\n"
+    "                        removes the comment and song tags)";
+
+extern "C" int
+    main( int argc, char** argv )
+{
+    const prog::Option long_options[] = {
+        { "help",        prog::Option::NO_ARG,       0, OPT_HELP         },
+        { "version",     prog::Option::NO_ARG,       0, OPT_VERSION      },
+        { "album",       prog::Option::REQUIRED_ARG, 0, OPT_ALBUM        },
+        { "artist",      prog::Option::REQUIRED_ARG, 0, OPT_ARTIST       },
+        { "comment",     prog::Option::REQUIRED_ARG, 0, OPT_COMMENT      },
+        { "copyright",   prog::Option::REQUIRED_ARG, 0, OPT_COPYRIGHT    },
+        { "disk",        prog::Option::REQUIRED_ARG, 0, OPT_DISK         },
+        { "disks",       prog::Option::REQUIRED_ARG, 0, OPT_DISKS        },
+        { "encodedby",   prog::Option::REQUIRED_ARG, 0, OPT_ENCODEDBY    },
+        { "tool",        prog::Option::REQUIRED_ARG, 0, OPT_TOOL         },
+        { "genre",       prog::Option::REQUIRED_ARG, 0, OPT_GENRE        },
+        { "grouping",    prog::Option::REQUIRED_ARG, 0, OPT_GROUPING     },
+        { "hdvideo",     prog::Option::REQUIRED_ARG, 0, OPT_HD           },
+        { "type",        prog::Option::REQUIRED_ARG, 0, OPT_MEDIA_TYPE   },
+        { "contentid",   prog::Option::REQUIRED_ARG, 0, OPT_CONTENTID    },
+        { "longdesc",    prog::Option::REQUIRED_ARG, 0, OPT_LONGDESC     },
+        { "genreid",     prog::Option::REQUIRED_ARG, 0, OPT_GENREID      },
+        { "lyrics",      prog::Option::REQUIRED_ARG, 0, OPT_LYRICS       },
+        { "description", prog::Option::REQUIRED_ARG, 0, OPT_DESCRIPTION  },
+        { "episode",     prog::Option::REQUIRED_ARG, 0, OPT_TVEPISODE    },
+        { "season",      prog::Option::REQUIRED_ARG, 0, OPT_TVSEASON     },
+        { "network",     prog::Option::REQUIRED_ARG, 0, OPT_TVNETWORK    },
+        { "episodeid",   prog::Option::REQUIRED_ARG, 0, OPT_TVEPISODEID  },
+        { "playlistid",  prog::Option::REQUIRED_ARG, 0, OPT_PLAYLISTID   },
+        { "picture",     prog::Option::REQUIRED_ARG, 0, OPT_PICTURE      },
+        { "podcast",     prog::Option::REQUIRED_ARG, 0, OPT_PODCAST      },
+        { "song",        prog::Option::REQUIRED_ARG, 0, OPT_NAME         },
+        { "show",        prog::Option::REQUIRED_ARG, 0, OPT_TVSHOW       },
+        { "tempo",       prog::Option::REQUIRED_ARG, 0, OPT_TEMPO        },
+        { "track",       prog::Option::REQUIRED_ARG, 0, OPT_TRACK        },
+        { "tracks",      prog::Option::REQUIRED_ARG, 0, OPT_TRACKS       },
+        { "xid",         prog::Option::REQUIRED_ARG, 0, OPT_XID          },
+        { "writer",      prog::Option::REQUIRED_ARG, 0, OPT_COMPOSER     },
+        { "year",        prog::Option::REQUIRED_ARG, 0, OPT_RELEASEDATE  },
+        { "artistid",    prog::Option::REQUIRED_ARG, 0, OPT_ARTISTID     },
+        { "composerid",  prog::Option::REQUIRED_ARG, 0, OPT_COMPOSERID   },
+        { "remove",      prog::Option::REQUIRED_ARG, 0, OPT_REMOVE       },
+        { "albumartist", prog::Option::REQUIRED_ARG, 0, OPT_ALBUM_ARTIST },
+        { "category",    prog::Option::REQUIRED_ARG, 0, OPT_CATEGORY },
+        { "rating",      prog::Option::REQUIRED_ARG, 0, OPT_RATING },
+        { NULL, prog::Option::NO_ARG, 0, 0 }
+    };
+
+    /* Sparse arrays of tag data: some space is wasted, but it's more
+       convenient to say tags[OPT_SONG] than to enumerate all the
+       metadata types (again) as a struct. */
+    const char *tags[UCHAR_MAX];
+    uint64_t nums[UCHAR_MAX];
+
+    memset( tags, 0, sizeof( tags ) );
+    memset( nums, 0, sizeof( nums ) );
+
+    /* Any modifications requested? */
+    int mods = 0;
+
+    /* Option-processing loop. */
+    int c = prog::getOptionSingle( argc, argv, OPT_STRING, long_options, NULL );
+    while ( c != -1 ) {
+        int r = 2;
+        switch ( c ) {
+                /* getopt() returns '?' if there was an error.  It already
+                   printed the error message, so just return. */
+            case '?':
+                return 1;
+
+                /* Help and version requests handled here. */
+            case OPT_HELP:
+                fprintf( stderr, "usage %s %s\n", argv[0], help_text );
+                return 0;
+            case OPT_VERSION:
+                fprintf( stderr, "%s - %s\n", argv[0], MP4V2_PROJECT_name_formal );
+                return 0;
+
+                /* Integer arguments: convert them using sscanf(). */
+            case OPT_TEMPO:
+            case OPT_DISK:
+            case OPT_DISKS:
+            case OPT_HD:
+            case OPT_CONTENTID:
+            case OPT_GENREID:
+            case OPT_TVEPISODE:
+            case OPT_TVSEASON:
+            case OPT_PLAYLISTID:
+            case OPT_TRACK:
+            case OPT_TRACKS:
+            case OPT_ARTISTID:
+            case OPT_COMPOSERID:
+            case OPT_PODCAST:
+                if ( c == OPT_PLAYLISTID ) {
+                    r = sscanf( prog::optarg, "%llu", &nums[c] );
+                } else {
+                    unsigned int n;
+                    r = sscanf( prog::optarg, "%u", &n );
+                    if ( r >= 1 )
+                    {
+                        nums[c] = static_cast<uint64_t>( n );
+                    }
+                }
+                if ( r < 1 ) {
+                    fprintf( stderr, "%s: option requires integer argument -- %c\n",
+                             argv[0], c );
+                    return 2;
+                }
+                /* Break not, lest ye be broken.  :) */
+                /* All arguments: all valid options end up here, and we just
+                   stuff the string pointer into the tags[] array. */
+            default:
+                tags[c] = prog::optarg;
+                mods++;
+        } /* end switch */
+
+        c = prog::getOptionSingle( argc, argv, OPT_STRING, long_options, NULL );
+    } /* end while */
+
+    /* Check that we have at least one non-option argument */
+    if ( ( argc - prog::optind ) < 1 ) {
+        fprintf( stderr,
+                 "%s: You must specify at least one MP4 file.\n",
+                 argv[0] );
+        fprintf( stderr, "usage %s %s\n", argv[0], help_text );
+        return 3;
+    }
+
+    /* Check that we have at least one requested modification.  Probably
+       it's useful instead to print the metadata if no modifications are
+       requested? */
+    if ( !mods ) {
+        fprintf( stderr,
+                 "%s: You must specify at least one tag modification.\n",
+                 argv[0] );
+        fprintf( stderr, "usage %s %s\n", argv[0], help_text );
+        return 4;
+    }
+
+    /* Loop through the non-option arguments, and modify the tags as
+       requested. */
+    while ( prog::optind < argc ) {
+        char *mp4 = argv[prog::optind++];
+
+        MP4FileHandle h = MP4Modify( mp4 );
+        if ( h == MP4_INVALID_FILE_HANDLE ) {
+            fprintf( stderr, "Could not open '%s'... aborting\n", mp4 );
+            return 5;
+        }
+        /* Read out the existing metadata */
+        const MP4Tags* mdata = MP4TagsAlloc();
+        MP4TagsFetch( mdata, h );
+
+        /* Remove any tags */
+        if ( ELEMENT_OF(tags,OPT_REMOVE) ) {
+            for ( const char *p = ELEMENT_OF(tags,OPT_REMOVE); *p; p++ ) {
+                switch ( *p ) {
+                    case OPT_ALBUM:
+                        MP4TagsSetAlbum( mdata, NULL );
+                        break;
+                    case OPT_ARTIST:
+                        MP4TagsSetArtist( mdata, NULL );
+                        break;
+                    case OPT_TEMPO:
+                        MP4TagsSetTempo( mdata, NULL );
+                        break;
+                    case OPT_COMMENT:
+                        MP4TagsSetComments( mdata, NULL );
+                        break;
+                    case OPT_COPYRIGHT:
+                        MP4TagsSetCopyright( mdata, NULL );
+                        break;
+                    case OPT_DISK:
+                        MP4TagsSetDisk( mdata, NULL );
+                        break;
+                    case OPT_DISKS:
+                        MP4TagsSetDisk( mdata, NULL );
+                        break;
+                    case OPT_ENCODEDBY:
+                        MP4TagsSetEncodedBy( mdata, NULL );
+                        break;
+                    case OPT_TOOL:
+                        MP4TagsSetEncodingTool( mdata, NULL );
+                        break;
+                    case OPT_GENRE:
+                        MP4TagsSetGenre( mdata, NULL );
+                        break;
+                    case OPT_GROUPING:
+                        MP4TagsSetGrouping( mdata, NULL );
+                        break;
+                    case OPT_HD:
+                        MP4TagsSetHDVideo( mdata, NULL );
+                        break;
+                    case OPT_MEDIA_TYPE:
+                        MP4TagsSetMediaType( mdata, NULL );
+                        break;
+                    case OPT_CONTENTID:
+                        MP4TagsSetContentID( mdata, NULL );
+                        break;
+                    case OPT_LONGDESC:
+                        MP4TagsSetLongDescription( mdata, NULL );
+                        break;
+                    case OPT_GENREID:
+                        MP4TagsSetGenreID( mdata, NULL );
+                        break;
+                    case OPT_LYRICS:
+                        MP4TagsSetLyrics( mdata, NULL );
+                        break;
+                    case OPT_DESCRIPTION:
+                        MP4TagsSetDescription( mdata, NULL );
+                        break;
+                    case OPT_TVEPISODE:
+                        MP4TagsSetTVEpisode( mdata, NULL );
+                        break;
+                    case OPT_TVSEASON:
+                        MP4TagsSetTVSeason( mdata, NULL );
+                        break;
+                    case OPT_TVNETWORK:
+                        MP4TagsSetTVNetwork( mdata, NULL );
+                        break;
+                    case OPT_TVEPISODEID:
+                        MP4TagsSetTVEpisodeID( mdata, NULL );
+                        break;
+                    case OPT_PLAYLISTID:
+                        MP4TagsSetPlaylistID( mdata, NULL );
+                        break;
+                    case OPT_PICTURE:
+                        if( mdata->artworkCount )
+                            MP4TagsRemoveArtwork( mdata, 0 );
+                        break;
+                    case OPT_ALBUM_ARTIST:
+                        MP4TagsSetAlbumArtist( mdata, NULL );
+                        break ;
+                    case OPT_NAME:
+                        MP4TagsSetName( mdata, NULL );
+                        break;
+                    case OPT_TVSHOW:
+                        MP4TagsSetTVShow( mdata, NULL );
+                        break;
+                    case OPT_TRACK:
+                        MP4TagsSetTrack( mdata, NULL );
+                        break;
+                    case OPT_TRACKS:
+                        MP4TagsSetTrack( mdata, NULL );
+                        break;
+                    case OPT_XID:
+                        MP4TagsSetXID( mdata, NULL );
+                        break;
+                    case OPT_COMPOSER:
+                        MP4TagsSetComposer( mdata, NULL );
+                        break;
+                    case OPT_RELEASEDATE:
+                        MP4TagsSetReleaseDate( mdata, NULL );
+                        break;
+                    case OPT_ARTISTID:
+                        MP4TagsSetArtistID( mdata, NULL );
+                        break;
+                    case OPT_COMPOSERID:
+                        MP4TagsSetComposerID( mdata, NULL );
+                        break;
+                    case OPT_PODCAST:
+                        MP4TagsSetPodcast(mdata, NULL);
+                        break;
+                    case OPT_CATEGORY:
+                        MP4TagsSetCategory(mdata, NULL);
+                        break;
+                    case OPT_RATING:
+                        MP4TagsSetContentRating(mdata, NULL);
+                        break;
+                }
+            }
+        }
+
+        /* Track/disk numbers need to be set all at once, but we'd like to
+           allow users to just specify -T 12 to indicate that all existing
+           track numbers are out of 12.  This means we need to look up the
+           current info if it is not being set. */
+
+        if ( ELEMENT_OF(tags,OPT_TRACK) || ELEMENT_OF(tags,OPT_TRACKS) ) {
+            MP4TagTrack tt;
+            tt.index = 0;
+            tt.total = 0;
+
+            if( mdata->track ) {
+                tt.index = mdata->track->index;
+                tt.total = mdata->track->total;
+            }
+
+            if( ELEMENT_OF(tags,OPT_TRACK) )
+                tt.index = static_cast<uint16_t>( ELEMENT_OF(nums,OPT_TRACK) );
+            if( ELEMENT_OF(tags,OPT_TRACKS) )
+                tt.total = static_cast<uint16_t>( ELEMENT_OF(nums,OPT_TRACKS) );
+
+            MP4TagsSetTrack( mdata, &tt );
+        }
+
+        if ( ELEMENT_OF(tags,OPT_DISK) || ELEMENT_OF(tags,OPT_DISKS) ) {
+            MP4TagDisk td;
+            td.index = 0;
+            td.total = 0;
+
+            if( mdata->disk ) {
+                td.index = mdata->disk->index;
+                td.total = mdata->disk->total;
+            }
+
+            if( ELEMENT_OF(tags,OPT_DISK) )
+                td.index = static_cast<uint16_t>( ELEMENT_OF(nums,OPT_DISK) );
+            if( ELEMENT_OF(tags,OPT_DISKS) )
+                td.total = static_cast<uint16_t>( ELEMENT_OF(nums,OPT_DISKS) );
+
+            MP4TagsSetDisk( mdata, &td );
+        }
+
+        /* Set the other relevant attributes */
+        for ( int i = 0;  i < UCHAR_MAX;  i++ ) {
+            if ( tags[i] ) {
+                switch ( i ) {
+                    case OPT_ALBUM:
+                        MP4TagsSetAlbum( mdata, tags[i] );
+                        break;
+                    case OPT_ARTIST:
+                        MP4TagsSetArtist( mdata, tags[i] );
+                        break;
+                    case OPT_TEMPO:
+                    {
+                        uint16_t value = static_cast<uint16_t>( nums[i] );
+                        MP4TagsSetTempo( mdata, &value );
+                        break;
+                    }
+                    case OPT_COMMENT:
+                        MP4TagsSetComments( mdata, tags[i] );
+                        break;
+                    case OPT_COPYRIGHT:
+                        MP4TagsSetCopyright( mdata, tags[i] );
+                        break;
+                    case OPT_ENCODEDBY:
+                        MP4TagsSetEncodedBy( mdata, tags[i] );
+                        break;
+                    case OPT_TOOL:
+                        MP4TagsSetEncodingTool( mdata, tags[i] );
+                        break;
+                    case OPT_GENRE:
+                        MP4TagsSetGenre( mdata, tags[i] );
+                        break;
+                    case OPT_GROUPING:
+                        MP4TagsSetGrouping( mdata, tags[i] );
+                        break;
+                    case OPT_HD:
+                    {
+                        uint8_t value = static_cast<uint8_t>( nums[i] );
+                        MP4TagsSetHDVideo( mdata, &value );
+                        break;
+                    }
+                    case OPT_MEDIA_TYPE:
+                    {
+                        uint8_t st = static_cast<uint8_t>( itmf::enumStikType.toType( tags[i] ) ) ;
+                        MP4TagsSetMediaType( mdata, &st );
+                        break;
+                    }
+                    case OPT_CONTENTID:
+                    {
+                        uint32_t value = static_cast<uint32_t>( nums[i] );
+                        MP4TagsSetContentID( mdata, &value );
+                        break;
+                    }
+                    case OPT_LONGDESC:
+                        MP4TagsSetLongDescription( mdata, tags[i] );
+                        break;
+                    case OPT_GENREID:
+                    {
+                        uint32_t value = static_cast<uint32_t>( nums[i] );
+                        MP4TagsSetGenreID( mdata, &value );
+                        break;
+                    }
+                    case OPT_LYRICS:
+                        MP4TagsSetLyrics( mdata, tags[i] );
+                        break;
+                    case OPT_DESCRIPTION:
+                        MP4TagsSetDescription( mdata, tags[i] );
+                        break;
+                    case OPT_TVEPISODE:
+                    {
+                        uint32_t value = static_cast<uint32_t>( nums[i] );
+                        MP4TagsSetTVEpisode( mdata, &value );
+                        break;
+                    }
+                    case OPT_TVSEASON:
+                    {
+                        uint32_t value = static_cast<uint32_t>( nums[i] );
+                        MP4TagsSetTVSeason( mdata, &value );
+                        break;
+                    }
+                    case OPT_TVNETWORK:
+                        MP4TagsSetTVNetwork( mdata, tags[i] );
+                        break;
+                    case OPT_TVEPISODEID:
+                        MP4TagsSetTVEpisodeID( mdata, tags[i] );
+                        break;
+                    case OPT_PLAYLISTID:
+                    {
+                        uint64_t value = static_cast<uint64_t>( nums[i] );
+                        MP4TagsSetPlaylistID( mdata, &value );
+                        break;
+                    }
+                    case OPT_PICTURE:
+                    {
+                        File in( tags[i], File::MODE_READ );
+                        if( !in.open() ) {
+                            MP4TagArtwork art;
+                            art.size = (uint32_t)in.size;
+                            art.data = malloc( art.size );
+                            art.type = MP4_ART_UNDEFINED;
+
+                            File::Size nin;
+                            if( !in.read( art.data, art.size, nin ) && nin == art.size ) {
+                                if( mdata->artworkCount )
+                                    MP4TagsRemoveArtwork( mdata, 0 );
+                                MP4TagsAddArtwork( mdata, &art ); 
+                            }
+
+                            free( art.data );
+                            in.close();
+                        }
+                        else {
+                            fprintf( stderr, "Art file %s not found\n", tags[i] );
+                        }
+                        break;
+                    }
+                    case OPT_ALBUM_ARTIST:
+                        MP4TagsSetAlbumArtist( mdata, tags[i] );
+                        break;
+                    case OPT_NAME:
+                        MP4TagsSetName( mdata, tags[i] );
+                        break;
+                    case OPT_TVSHOW:
+                        MP4TagsSetTVShow( mdata, tags[i] );
+                        break;
+                    case OPT_XID:
+                        MP4TagsSetXID( mdata, tags[i] );
+                        break;
+                    case OPT_COMPOSER:
+                        MP4TagsSetComposer( mdata, tags[i] );
+                        break;
+                    case OPT_RELEASEDATE:
+                        MP4TagsSetReleaseDate( mdata, tags[i] );
+                        break;
+                    case OPT_ARTISTID:
+                    {
+                        uint32_t value = static_cast<uint32_t>( nums[i] );
+                        MP4TagsSetArtistID( mdata, &value );
+                        break;
+                    }
+                    case OPT_COMPOSERID:
+                    {
+                        uint32_t value = static_cast<uint32_t>( nums[i] );
+                        MP4TagsSetComposerID( mdata, &value );
+                        break;
+                    }
+                    case OPT_PODCAST:
+                    {
+                        uint8_t value = static_cast<uint8_t>( nums[i] );
+                        MP4TagsSetPodcast(mdata, &value);
+                        break;
+                    }
+                    case OPT_CATEGORY:
+                    {
+                        MP4TagsSetCategory(mdata, tags[i]);
+                        break;
+                    }
+                    case OPT_RATING:
+                    {
+                        uint8_t rating = static_cast<uint8_t>( itmf::enumContentRating.toType( tags[i] ) ) ;
+                        MP4TagsSetContentRating(mdata, &rating);
+                        break;
+                    }
+                }
+            }
+        }
+        /* Write out all tag modifications, free and close */
+        MP4TagsStore( mdata, h );
+        MP4TagsFree( mdata );
+        MP4Close( h );
+    } /* end while optind < argc */
+    return 0;
+}
diff --git a/util/mp4track.cpp b/util/mp4track.cpp
new file mode 100644
index 0000000..d550506
--- /dev/null
+++ b/util/mp4track.cpp
@@ -0,0 +1,997 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+//  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.
+//  Portions created by David Byron are Copyright (C) 2010.
+//  All Rights Reserved.
+//
+//  Contributors:
+//      Kona Blend, kona8lend@@gmail.com
+//      David Byron, dbyron@dbyron.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "util/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class TrackUtility : public Utility
+{
+private:
+    enum TrackLongAction {
+        LC_TRACK_WILDCARD = _LC_MAX,
+        LC_TRACK_ID,
+        LC_TRACK_INDEX,
+
+        LC_SAMPLE_WILDCARD,
+        LC_SAMPLE_ID,
+        LC_SAMPLE_INDEX,
+
+        LC_LIST,
+
+        LC_ENABLED,
+        LC_INMOVIE,
+        LC_INPREVIEW,
+        LC_LAYER,
+        LC_ALTGROUP,
+        LC_VOLUME,
+        LC_WIDTH,
+        LC_HEIGHT,
+        LC_LANGUAGE,
+        LC_HDLRNAME,
+        LC_UDTANAME,
+        LC_UDTANAME_R,
+
+        LC_COLR_PARMS,
+        LC_COLR_PARM_HD,
+        LC_COLR_PARM_SD,
+
+        LC_COLR_LIST,
+        LC_COLR_ADD,
+        LC_COLR_SET,
+        LC_COLR_REMOVE,
+
+        LC_PASP_PARMS,
+
+        LC_PASP_LIST,
+        LC_PASP_ADD,
+        LC_PASP_SET,
+        LC_PASP_REMOVE,
+    };
+
+public:
+    TrackUtility( int, char** );
+
+protected:
+    // delegates implementation
+    bool utility_option( int, bool& );
+    bool utility_job( JobContext& );
+
+private:
+    bool actionList( JobContext& );
+    bool actionListSingle( JobContext&, uint16_t );
+
+    bool actionColorParameterList   ( JobContext& );
+    bool actionColorParameterAdd    ( JobContext& );
+    bool actionColorParameterSet    ( JobContext& );
+    bool actionColorParameterRemove ( JobContext& );
+
+    bool actionPictureAspectRatioList   ( JobContext& );
+    bool actionPictureAspectRatioAdd    ( JobContext& );
+    bool actionPictureAspectRatioSet    ( JobContext& );
+    bool actionPictureAspectRatioRemove ( JobContext& );
+
+    bool actionTrackModifierSet    ( JobContext& );
+    bool actionTrackModifierRemove ( JobContext& );
+
+private:
+    enum TrackMode {
+        TM_UNDEFINED,
+        TM_INDEX,
+        TM_ID,
+        TM_WILDCARD,
+    };
+
+    enum SampleMode {
+        SM_UNDEFINED,
+        SM_INDEX,
+        SM_ID,
+        SM_WILDCARD,
+    };
+
+    Group _actionGroup;
+    Group _parmGroup;
+
+    bool (TrackUtility::*_action)( JobContext& );
+
+    TrackMode _trackMode;
+    uint16_t  _trackIndex;
+    uint32_t  _trackId;
+
+    SampleMode _sampleMode;
+    uint16_t   _sampleIndex;
+    uint32_t   _sampleId;
+
+    qtff::ColorParameterBox::Item     _colorParameterItem;
+    qtff::PictureAspectRatioBox::Item _pictureAspectRatioItem;
+
+    void (TrackModifier::*_actionTrackModifierSet_function)( const string& );
+    string _actionTrackModifierSet_name;
+    string _actionTrackModifierSet_value;
+
+    void (TrackModifier::*_actionTrackModifierRemove_function)();
+    string _actionTrackModifierRemove_name;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+string toStringTrackType( string );
+
+///////////////////////////////////////////////////////////////////////////////
+
+TrackUtility::TrackUtility( int argc, char** argv )
+    : Utility      ( "mp4track", argc, argv )
+    , _actionGroup ( "ACTIONS" )
+    , _parmGroup   ( "PARAMETERS" )
+    , _action      ( NULL )
+    , _trackMode   ( TM_UNDEFINED )
+    , _trackIndex  ( 0 )
+    , _trackId     ( MP4_INVALID_TRACK_ID )
+    , _sampleMode  ( SM_UNDEFINED )
+    , _sampleIndex ( 0 )
+    , _sampleId    ( MP4_INVALID_SAMPLE_ID )
+{
+    // add standard options which make sense for this utility
+    _group.add( STD_OPTIMIZE );
+    _group.add( STD_DRYRUN );
+    _group.add( STD_KEEPGOING );
+    _group.add( STD_OVERWRITE );
+    _group.add( STD_FORCE );
+    _group.add( STD_QUIET );
+    _group.add( STD_DEBUG );
+    _group.add( STD_VERBOSE );
+    _group.add( STD_HELP );
+    _group.add( STD_VERSION );
+    _group.add( STD_VERSIONX );
+
+    _parmGroup.add( "track-any",    false, LC_TRACK_WILDCARD,  "act on any/all tracks" );
+    _parmGroup.add( "track-index",  true,  LC_TRACK_INDEX,     "act on track index IDX", "IDX" );
+    _parmGroup.add( "track-id",     true,  LC_TRACK_ID,        "act on track id ID", "ID" );
+/*
+    _parmGroup.add( "sample-any",   false, LC_SAMPLE_WILDCARD, "act on any sample (default)" );
+    _parmGroup.add( "sample-index", true,  LC_SAMPLE_INDEX,    "act on sample index IDX" );
+    _parmGroup.add( "sample-id",    true,  LC_SAMPLE_ID,       "act on sample id ID" );
+*/
+    _parmGroup.add( "colr-parms",   true,  LC_COLR_PARMS,      "where CSV is IDX1,IDX2,IDX3", "CSV" );
+    _parmGroup.add( "colr-parm-hd", false, LC_COLR_PARM_HD,    "equivalent to --colr-parms=1,1,1" );
+    _parmGroup.add( "colr-parm-sd", false, LC_COLR_PARM_SD,    "equivalent to --colr-parms=6,1,6" );
+    _parmGroup.add( "pasp-parms",   true,  LC_PASP_PARMS,      "where CSV is hSPACING,vSPACING", "CSV" );
+    _groups.push_back( &_parmGroup );
+
+    _actionGroup.add( "list", false, LC_LIST, "list all tracks in mp4" );
+
+    _actionGroup.add( "enabled",         true,  LC_ENABLED,    "set trak.tkhd.flags (enabled bit)", "BOOL" );
+    _actionGroup.add( "inmovie",         true,  LC_INMOVIE,    "set trak.tkhd.flags (inMovie bit)", "BOOL" );
+    _actionGroup.add( "inpreview",       true,  LC_INPREVIEW,  "set trak.tkhd.flags (inPreview bit)", "BOOL" );
+    _actionGroup.add( "layer",           true,  LC_LAYER,      "set trak.tkhd.layer", "NUM" );
+    _actionGroup.add( "altgroup",        true,  LC_ALTGROUP,   "set trak.tkhd.alternate_group", "NUM" );
+    _actionGroup.add( "volume",          true,  LC_VOLUME,     "set trak.tkhd.volume", "FLOAT" );
+    _actionGroup.add( "width",           true,  LC_WIDTH,      "set trak.tkhd.width", "FLOAT" );
+    _actionGroup.add( "height",          true,  LC_HEIGHT,     "set trak.tkhd.height", "FLOAT" );
+    _actionGroup.add( "language",        true,  LC_LANGUAGE,   "set trak.mdia.mdhd.language", "CODE" );
+    _actionGroup.add( "hdlrname",        true,  LC_HDLRNAME,   "set trak.mdia.hdlr.name", "STR" );
+    _actionGroup.add( "udtaname",        true,  LC_UDTANAME,   "set trak.udta.name.value", "STR" );
+    _actionGroup.add( "udtaname-remove", false, LC_UDTANAME_R, "remove trak.udta.name atom" );
+
+    _actionGroup.add( "colr-list",   false, LC_COLR_LIST,   "list all colr-boxes in mp4" );
+    _actionGroup.add( "colr-add",    false, LC_COLR_ADD,    "add colr-box to a video track" );
+    _actionGroup.add( "colr-set",    false, LC_COLR_SET,    "set colr-box parms" );
+    _actionGroup.add( "colr-remove", false, LC_COLR_REMOVE, "remove colr-box from track" );
+    _actionGroup.add( "pasp-list",   false, LC_PASP_LIST,   "list all pasp-boxes in mp4" );
+    _actionGroup.add( "pasp-add",    false, LC_PASP_ADD,    "add pasp-box to a video track" );
+    _actionGroup.add( "pasp-set",    false, LC_PASP_SET,    "set pasp-box parms" );
+    _actionGroup.add( "pasp-remove", false, LC_PASP_REMOVE, "remove pasp-box from track" );
+
+    _groups.push_back( &_actionGroup );
+
+    _usage = "[OPTION]... [PARAMETERS]... ACTION file...";
+    _description =
+        // 79-cols, inclusive, max desired width
+        // |----------------------------------------------------------------------------|
+        "\nFor each mp4 file specified, perform the specified ACTION. An action must be"
+        "\nspecified. Some options are not applicable to some actions.";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionColorParameterAdd( JobContext& job )
+{
+    ostringstream oss;
+    oss << "adding colr-box(" << _colorParameterItem.convertToCSV() << ") -> " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            return herrf( "track not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        default:
+        case TM_INDEX:
+            if( qtff::ColorParameterBox::add( job.fileHandle, _trackIndex, _colorParameterItem ))
+                return herrf( "unable to add colr-box\n" );
+            break;
+
+        case TM_ID:
+            if( qtff::ColorParameterBox::add( job.fileHandle, _trackId, _colorParameterItem ))
+                return herrf( "unable to add colr-box\n" );
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionColorParameterList( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    ostringstream report;
+
+    const int widx = 3;
+    const int wid = 3;
+    const int wtype = 8;
+    const int wparm = 6;
+    const string sep = "  ";
+
+    if( _jobCount == 0 ) {
+        report << setw(widx) << right << "IDX"
+               << sep << setw(wid) << "ID"
+               << sep << setw(wtype) << left << "TYPE"
+               << sep << setw(wparm) << right << "PRIMRY"
+               << sep << setw(wparm) << right << "XFERFN"
+               << sep << setw(wparm) << right << "MATRIX"
+               << sep << setw(0) << "FILE"
+               << '\n';
+
+        report << setfill('-') << setw(70) << "" << setfill(' ') << '\n';
+    }
+
+    qtff::ColorParameterBox::ItemList itemList;
+    if( qtff::ColorParameterBox::list( job.fileHandle, itemList ))
+        return herrf( "unable to fetch list of colr-boxes" );
+
+    const qtff::ColorParameterBox::ItemList::size_type max = itemList.size();
+    for( qtff::ColorParameterBox::ItemList::size_type i = 0; i < max; i++ ) {
+        const qtff::ColorParameterBox::IndexedItem& xitem = itemList[i];
+
+        const char* type = MP4GetTrackType( job.fileHandle, xitem.trackId );
+        if( !type)
+            type = "unknown";
+
+        report << right << setw(widx) << xitem.trackIndex
+               << sep << setw(wid) << xitem.trackId
+               << sep << setw(wtype) << left << toStringTrackType( type )
+               << sep << setw(wparm) << right << xitem.item.primariesIndex
+               << sep << setw(wparm) << right << xitem.item.transferFunctionIndex
+               << sep << setw(wparm) << right << xitem.item.matrixIndex;
+
+        if( i == 0 )
+            report << sep << setw(0) << job.file;
+
+        report << '\n';
+    }
+
+    verbose1f( "%s", report.str().c_str() );
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionColorParameterRemove( JobContext& job )
+{
+    ostringstream oss;
+    oss << "removing colr-box from " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        case TM_WILDCARD:
+            oss << " (all tracks)";
+            break;
+
+        default:
+            return herrf( "track(s) not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            if( qtff::ColorParameterBox::remove( job.fileHandle, _trackIndex ))
+                return herrf( "unable to remove colr-box\n" );
+            break;
+
+        case TM_ID:
+            if( qtff::ColorParameterBox::remove( job.fileHandle, _trackId ))
+                return herrf( "unable to remove colr-box\n" );
+            break;
+
+        default:
+        case TM_WILDCARD:
+        {
+            qtff::ColorParameterBox::ItemList itemList;
+            if( qtff::ColorParameterBox::list( job.fileHandle, itemList ))
+                return herrf( "unable to fetch list of colr-boxes" );
+
+            _trackMode = TM_INDEX;
+            const qtff::ColorParameterBox::ItemList::size_type max = itemList.size();
+            for( qtff::ColorParameterBox::ItemList::size_type i = 0; i < max; i++ ) {
+                const qtff::ColorParameterBox::IndexedItem& xitem = itemList[i];
+                _trackIndex = xitem.trackIndex;
+                actionColorParameterRemove( job );
+            }
+            break;
+        }
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionColorParameterSet( JobContext& job )
+{
+    ostringstream oss;
+    oss << "setting colr-box(" << _colorParameterItem.convertToCSV() << ") -> " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            return herrf( "track not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        default:
+        case TM_INDEX:
+            if( qtff::ColorParameterBox::set( job.fileHandle, _trackIndex, _colorParameterItem ))
+                return herrf( "unable to set colr-box\n" );
+            break;
+
+        case TM_ID:
+            if( qtff::ColorParameterBox::set( job.fileHandle, _trackId, _colorParameterItem ))
+                return herrf( "unable to set colr-box\n" );
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionList( JobContext& job )
+{
+    if( _jobTotal > 1 )
+        verbose1f( "file %u of %u: %s\n", _jobCount+1, _jobTotal, job.file.c_str() );
+
+    ostringstream report;
+
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            return actionListSingle( job, _trackIndex );
+
+        case TM_ID:
+            return actionListSingle( job, MP4FindTrackIndex( job.fileHandle, _trackId ));
+
+        case TM_WILDCARD:
+        default:
+        {
+            bool result = SUCCESS;
+            const uint16_t trackc = static_cast<uint16_t>( MP4GetNumberOfTracks( job.fileHandle ));
+            for( uint16_t i = 0; i < trackc; i++ ) {
+                if( actionListSingle( job, i ))
+                    result = FAILURE;
+            }
+            return result;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionListSingle( JobContext& job, uint16_t index )
+{
+    TrackModifier tm( job.fileHandle, index );
+
+    ostringstream report;
+    tm.dump( report, ( _jobTotal > 1 ? "  " : "" ));
+
+    verbose1f( "%s", report.str().c_str() );
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionPictureAspectRatioAdd( JobContext& job )
+{
+    ostringstream oss;
+    oss << "adding pasp-box(" << _pictureAspectRatioItem.convertToCSV() << ") -> " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            return herrf( "track not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        default:
+        case TM_INDEX:
+            if( qtff::PictureAspectRatioBox::add( job.fileHandle, _trackIndex, _pictureAspectRatioItem ))
+                return herrf( "unable to add pasp-box\n" );
+            break;
+
+        case TM_ID:
+            if( qtff::PictureAspectRatioBox::add( job.fileHandle, _trackId, _pictureAspectRatioItem ))
+                return herrf( "unable to add pasp-box\n" );
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionPictureAspectRatioList( JobContext& job )
+{
+    job.fileHandle = MP4Read( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for read: %s\n", job.file.c_str() );
+
+    ostringstream report;
+
+    const int widx = 3;
+    const int wid = 3;
+    const int wtype = 8;
+    const int wparm = 6;
+    const string sep = "  ";
+
+    if( _jobCount == 0 ) {
+        report << setw(widx) << right << "IDX"
+               << sep << setw(wid) << "ID"
+               << sep << setw(wtype) << left << "TYPE"
+               << sep << setw(wparm) << right << "hSPACE"
+               << sep << setw(wparm) << right << "vSPACE"
+               << sep << setw(0) << "FILE"
+               << '\n';
+
+        report << setfill('-') << setw(70) << "" << setfill(' ') << '\n';
+    }
+
+    qtff::PictureAspectRatioBox::ItemList itemList;
+    if( qtff::PictureAspectRatioBox::list( job.fileHandle, itemList ))
+        return herrf( "unable to fetch list of pasp-boxes" );
+
+    const qtff::PictureAspectRatioBox::ItemList::size_type max = itemList.size();
+    for( qtff::PictureAspectRatioBox::ItemList::size_type i = 0; i < max; i++ ) {
+        const qtff::PictureAspectRatioBox::IndexedItem& xitem = itemList[i];
+
+        const char* type = MP4GetTrackType( job.fileHandle, xitem.trackId );
+        if( !type)
+            type = "unknown";
+
+        report << right << setw(widx) << xitem.trackIndex
+               << sep << setw(wid) << xitem.trackId
+               << sep << setw(wtype) << left << toStringTrackType( type )
+               << sep << setw(wparm) << right << xitem.item.hSpacing
+               << sep << setw(wparm) << right << xitem.item.vSpacing;
+
+        if( i == 0 )
+            report << sep << setw(0) << job.file;
+
+        report << '\n';
+    }
+
+    verbose1f( "%s", report.str().c_str() );
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionPictureAspectRatioRemove( JobContext& job )
+{
+    ostringstream oss;
+    oss << "removing pasp-box from " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            oss << " (all tracks)";
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            if( qtff::PictureAspectRatioBox::remove( job.fileHandle, _trackIndex ))
+                return herrf( "unable to remove pasp-box\n" );
+            break;
+
+        case TM_ID:
+            if( qtff::PictureAspectRatioBox::remove( job.fileHandle, _trackId ))
+                return herrf( "unable to remove pasp-box\n" );
+            break;
+
+        default:
+        case TM_WILDCARD:
+        {
+            qtff::PictureAspectRatioBox::ItemList itemList;
+            if( qtff::PictureAspectRatioBox::list( job.fileHandle, itemList ))
+                return herrf( "unable to fetch list of pasp-boxes" );
+
+            _trackMode = TM_INDEX;
+            const qtff::PictureAspectRatioBox::ItemList::size_type max = itemList.size();
+            for( qtff::PictureAspectRatioBox::ItemList::size_type i = 0; i < max; i++ ) {
+                const qtff::PictureAspectRatioBox::IndexedItem& xitem = itemList[i];
+                _trackIndex = xitem.trackIndex;
+                actionPictureAspectRatioRemove( job );
+            }
+            break;
+        }
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionPictureAspectRatioSet( JobContext& job )
+{
+    ostringstream oss;
+    oss << "setting pasp-box(" << _pictureAspectRatioItem.convertToCSV() << ") -> " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            return herrf( "track not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    switch( _trackMode ) {
+        default:
+        case TM_INDEX:
+            if( qtff::PictureAspectRatioBox::set( job.fileHandle, _trackIndex, _pictureAspectRatioItem ))
+                return herrf( "unable to set pasp-box\n" );
+            break;
+
+        case TM_ID:
+            if( qtff::PictureAspectRatioBox::set( job.fileHandle, _trackId, _pictureAspectRatioItem ))
+                return herrf( "unable to set pasp-box\n" );
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionTrackModifierRemove( JobContext& job )
+{
+    ostringstream oss;
+    oss << "removing " << _actionTrackModifierRemove_name << " -> " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            return herrf( "track not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    if( _trackMode == TM_ID )
+        _trackIndex = MP4FindTrackIndex( job.fileHandle, _trackId );
+
+    TrackModifier tm( job.fileHandle, _trackIndex );
+    (tm.*_actionTrackModifierRemove_function)();
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::actionTrackModifierSet( JobContext& job )
+{
+    ostringstream oss;
+    oss << "setting " << _actionTrackModifierSet_name << "=" << _actionTrackModifierSet_value << " -> " << job.file;
+
+    switch( _trackMode ) {
+        case TM_INDEX:
+            oss << " (track index=" << _trackIndex << ')';
+            break;
+
+        case TM_ID:
+            oss << " (track id=" << _trackId << ')';
+            break;
+
+        default:
+        case TM_WILDCARD:
+            return herrf( "track not specified\n" );
+    }
+
+    verbose1f( "%s\n", oss.str().c_str() );
+    if( dryrunAbort() )
+        return SUCCESS;
+
+    job.fileHandle = MP4Modify( job.file.c_str() );
+    if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
+        return herrf( "unable to open for write: %s\n", job.file.c_str() );
+
+    if( _trackMode == TM_ID )
+        _trackIndex = MP4FindTrackIndex( job.fileHandle, _trackId );
+
+    TrackModifier tm( job.fileHandle, _trackIndex );
+    (tm.*_actionTrackModifierSet_function)( _actionTrackModifierSet_value );
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::utility_job( JobContext& job )
+{
+    if( !_action )
+        return herrf( "no action specified\n" );
+
+    return (this->*_action)( job );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+TrackUtility::utility_option( int code, bool& handled )
+{
+    handled = true;
+
+    switch( code ) {
+        case LC_TRACK_WILDCARD:
+            _trackMode = TM_WILDCARD;
+            break;
+
+        case LC_TRACK_INDEX:
+        {
+            _trackMode = TM_INDEX;
+            istringstream iss( prog::optarg );
+            iss >> _trackIndex;
+            if( iss.rdstate() != ios::eofbit )
+                return herrf( "invalid track index: %s\n", prog::optarg );
+            break;
+        }
+
+        case LC_TRACK_ID:
+        {
+            _trackMode = TM_ID;
+            istringstream iss( prog::optarg );
+            iss >> _trackId;
+            if( iss.rdstate() != ios::eofbit )
+                return herrf( "invalid track id: %s\n", prog::optarg );
+            break;
+        }
+
+        case LC_LIST:
+            _action = &TrackUtility::actionList;
+            break;
+
+        case LC_COLR_PARMS:
+            _colorParameterItem.convertFromCSV( prog::optarg );
+            break;
+
+        case LC_COLR_PARM_HD:
+            _colorParameterItem.primariesIndex        = 1;
+            _colorParameterItem.transferFunctionIndex = 1;
+            _colorParameterItem.matrixIndex           = 1;
+            break;
+
+        case LC_COLR_PARM_SD:
+            _colorParameterItem.primariesIndex        = 6;
+            _colorParameterItem.transferFunctionIndex = 1;
+            _colorParameterItem.matrixIndex           = 6;
+            break;
+
+        case LC_COLR_LIST:
+            _action = &TrackUtility::actionColorParameterList;
+            break;
+
+        case LC_ENABLED:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setEnabled;
+            _actionTrackModifierSet_name     = "enabled";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_INMOVIE:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setInMovie;
+            _actionTrackModifierSet_name     = "inMovie";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_INPREVIEW:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setInPreview;
+            _actionTrackModifierSet_name     = "inPreview";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_LAYER:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setLayer;
+            _actionTrackModifierSet_name     = "layer";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_ALTGROUP:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setAlternateGroup;
+            _actionTrackModifierSet_name     = "alternateGroup";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_VOLUME:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setVolume;
+            _actionTrackModifierSet_name     = "volume";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_WIDTH:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setWidth;
+            _actionTrackModifierSet_name     = "width";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_HEIGHT:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setHeight;
+            _actionTrackModifierSet_name     = "height";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_LANGUAGE:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setLanguage;
+            _actionTrackModifierSet_name     = "language";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_HDLRNAME:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setHandlerName;
+            _actionTrackModifierSet_name     = "handlerName";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_UDTANAME:
+            _action = &TrackUtility::actionTrackModifierSet;
+            _actionTrackModifierSet_function = &TrackModifier::setUserDataName;
+            _actionTrackModifierSet_name     = "userDataName";
+            _actionTrackModifierSet_value    = prog::optarg;
+            break;
+
+        case LC_UDTANAME_R:
+            _action = &TrackUtility::actionTrackModifierRemove;
+            _actionTrackModifierRemove_function = &TrackModifier::removeUserDataName;
+            _actionTrackModifierRemove_name     = "userDataName";
+            break;
+
+        case LC_COLR_ADD:
+            _action = &TrackUtility::actionColorParameterAdd;
+            break;
+
+        case LC_COLR_SET:
+            _action = &TrackUtility::actionColorParameterSet;
+            break;
+
+        case LC_COLR_REMOVE:
+            _action = &TrackUtility::actionColorParameterRemove;
+            break;
+
+        case LC_PASP_PARMS:
+            _pictureAspectRatioItem.convertFromCSV( prog::optarg );
+            break;
+
+        case LC_PASP_LIST:
+            _action = &TrackUtility::actionPictureAspectRatioList;
+            break;
+
+        case LC_PASP_ADD:
+            _action = &TrackUtility::actionPictureAspectRatioAdd;
+            break;
+
+        case LC_PASP_SET:
+            _action = &TrackUtility::actionPictureAspectRatioSet;
+            break;
+
+        case LC_PASP_REMOVE:
+            _action = &TrackUtility::actionPictureAspectRatioRemove;
+            break;
+
+        default:
+            handled = false;
+            break;
+    }
+
+    return SUCCESS;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string
+toStringTrackType( string code )
+{
+    if( !code.compare( "vide" ))    // 14496-12
+        return "video";
+    if( !code.compare( "soun" ))    // 14496-12
+        return "audio";
+    if( !code.compare( "hint" ))    // 14496-12
+        return "hint";
+
+    if( !code.compare( "text" ))    // QTFF
+        return "text";
+    if( !code.compare( "tmcd" ))    // QTFF
+        return "timecode";
+
+    if( !code.compare( "subt" ))    // QTFF
+        return "subtitle";
+
+    return string( "(" ) + code + ")";
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C"
+int main( int argc, char** argv )
+{
+    mp4v2::util::TrackUtility util( argc, argv );
+    return util.process();
+}
diff --git a/util/mp4trackdump.cpp b/util/mp4trackdump.cpp
new file mode 100644
index 0000000..ab24d36
--- /dev/null
+++ b/util/mp4trackdump.cpp
@@ -0,0 +1,242 @@
+/*
+ * 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.  All Rights Reserved.
+ *
+ * Contributor(s):
+ *      Dave Mackie     dmackie@cisco.com
+ */
+
+// N.B. mp4extract just extracts tracks/samples from an mp4 file
+// For many track types this is insufficient to reconsruct a valid
+// elementary stream (ES). Use "mp4creator -extract=<trackId>" if
+// you need the ES reconstructed.
+
+#include "util/impl.h"
+
+using namespace mp4v2::util;
+
+char* ProgName;
+char* Mp4PathName;
+char* Mp4FileName;
+
+static void DumpTrack ( MP4FileHandle mp4file, MP4TrackId tid )
+{
+    uint32_t numSamples;
+    MP4SampleId sid;
+    MP4Duration time;
+    uint32_t timescale;
+    uint64_t msectime;
+
+    uint64_t sectime, mintime, hrtime;
+
+    numSamples = MP4GetTrackNumberOfSamples( mp4file, tid );
+    timescale = MP4GetTrackTimeScale( mp4file, tid );
+    printf( "mp4file %s, track %d, samples %d, timescale %d\n",
+            Mp4FileName, tid, numSamples, timescale );
+
+    for ( sid = 1; sid <= numSamples; sid++ ) {
+        time = MP4GetSampleTime( mp4file, tid, sid );
+        msectime = time;
+        msectime *= UINT64_C( 1000 );
+        msectime /= timescale;
+        if ( msectime == 0 ) {
+            hrtime = mintime = sectime = UINT64_C( 0 );
+        }
+        else {
+            hrtime = msectime / UINT64_C( 3600000 ); // 3600 * 1000
+            msectime -= hrtime * UINT64_C( 3600000 );// 3600 * 1000
+            mintime = msectime / UINT64_C( 60000 );// 60 * 1000
+            msectime -= ( mintime * UINT64_C( 60000 ) );// 60 * 1000
+            sectime = msectime / UINT64_C( 1000 );
+            msectime -= sectime * UINT64_C( 1000 );
+        }
+
+        printf( "sampleId %6d, size %5u duration %8" PRIu64 " time %8" PRIu64 " %02" PRIu64 ":%02" PRIu64 ":%02" PRIu64 ".%03" PRIu64 " %c\n",
+                sid,  MP4GetSampleSize( mp4file, tid, sid ),
+                MP4GetSampleDuration( mp4file, tid, sid ),
+                time, hrtime, mintime, sectime, msectime,
+                MP4GetSampleSync( mp4file, tid, sid ) == 1 ? 'S' : ' ' );
+    }
+}
+
+extern "C" int main( int argc, char** argv )
+{
+    const char* const usageString =
+        "[-l] [-t <track-id>] [-s <sample-id>] [-v [<level>]] <file-name>";
+    MP4TrackId trackId = MP4_INVALID_TRACK_ID;
+    MP4SampleId sampleId = MP4_INVALID_SAMPLE_ID;
+    MP4LogLevel verbosity = MP4_LOG_ERROR;
+
+    /* begin processing command line */
+    ProgName = argv[0];
+    while ( true ) {
+        int c = -1;
+        int option_index = 0;
+        static const prog::Option long_options[] = {
+            { "track",   prog::Option::REQUIRED_ARG, 0, 't' },
+            { "sample",  prog::Option::REQUIRED_ARG, 0, 's' },
+            { "verbose", prog::Option::OPTIONAL_ARG, 0, 'v' },
+            { "version", prog::Option::NO_ARG,       0, 'V' },
+            { NULL, prog::Option::NO_ARG, 0, 0 }
+        };
+
+        c = prog::getOptionSingle( argc, argv, "t:v::V", long_options, &option_index );
+
+        if ( c == -1 )
+            break;
+
+        switch ( c ) {
+            case 's':
+                if ( sscanf( prog::optarg, "%u", &sampleId ) != 1 ) {
+                    fprintf( stderr, "%s: bad sample-id specified: %s\n",
+                             ProgName, prog::optarg );
+                    exit( 1 );
+                }
+                break;
+            case 't':
+                if ( sscanf( prog::optarg, "%u", &trackId ) != 1 ) {
+                    fprintf( stderr,
+                             "%s: bad track-id specified: %s\n",
+                             ProgName, prog::optarg );
+                    exit( 1 );
+                }
+                break;
+            case 'v':
+                verbosity = MP4_LOG_VERBOSE1;
+                if ( prog::optarg ) {
+                    uint32_t level;
+                    if ( sscanf( prog::optarg, "%u", &level ) == 1 ) {
+                        if ( level >= 2 ) {
+                            verbosity = MP4_LOG_VERBOSE2;
+                        }
+                        if ( level >= 3 ) {
+                            verbosity = MP4_LOG_VERBOSE3;
+                        }
+                        if ( level >= 4 ) {
+                            verbosity = MP4_LOG_VERBOSE4;
+                        }
+                    }
+                }
+                break;
+            case '?':
+                fprintf( stderr, "usage: %s %s\n", ProgName, usageString );
+                exit( 0 );
+            case 'V':
+                fprintf( stderr, "%s - %s\n",
+                         ProgName, MP4V2_PROJECT_name_formal );
+                exit( 0 );
+            default:
+                fprintf( stderr, "%s: unknown option specified, ignoring: %c\n",
+                         ProgName, c );
+        }
+    }
+
+    /* check that we have at least one non-option argument */
+    if ( ( argc - prog::optind ) < 1 ) {
+        fprintf( stderr, "usage: %s %s\n", ProgName, usageString );
+        exit( 1 );
+    }
+
+    MP4LogSetLevel(verbosity);
+    if ( verbosity ) {
+        fprintf( stderr, "%s version %s\n", ProgName, MP4V2_PROJECT_version );
+    }
+
+    /* point to the specified file names */
+    Mp4PathName = argv[prog::optind++];
+
+    char* lastSlash = strrchr( Mp4PathName, '/' );
+    if ( lastSlash ) {
+        Mp4FileName = lastSlash + 1;
+    }
+    else {
+        Mp4FileName = Mp4PathName;
+    }
+
+    /* warn about extraneous non-option arguments */
+    if ( prog::optind < argc ) {
+        fprintf( stderr, "%s: unknown options specified, ignoring: ", ProgName );
+        while ( prog::optind < argc ) {
+            fprintf( stderr, "%s ", argv[prog::optind++] );
+        }
+        fprintf( stderr, "\n" );
+    }
+
+    /* end processing of command line */
+
+
+    MP4FileHandle mp4File = MP4Read( Mp4PathName );
+
+    if ( !mp4File ) {
+        exit( 1 );
+    }
+
+    if ( sampleId != MP4_INVALID_SAMPLE_ID ) {
+        if ( trackId == 0 ) {
+            fprintf( stderr, "%s: Must specify track for sample\n", ProgName );
+            return -1;
+        }
+        if ( sampleId > MP4GetTrackNumberOfSamples( mp4File, trackId ) ) {
+            fprintf( stderr, "%s: Sample number %u is past end %u\n",
+                     ProgName, sampleId, MP4GetTrackNumberOfSamples( mp4File, trackId ) );
+            return -1;
+        }
+        uint32_t sample_size = MP4GetTrackMaxSampleSize( mp4File, trackId );
+        uint8_t *sample = ( uint8_t * )malloc( sample_size );
+        MP4Timestamp sampleTime;
+        MP4Duration sampleDuration, sampleRenderingOffset;
+        uint32_t this_size = sample_size;
+        bool isSyncSample;
+        bool ret = MP4ReadSample( mp4File,
+                                  trackId,
+                                  sampleId,
+                                  &sample,
+                                  &this_size,
+                                  &sampleTime,
+                                  &sampleDuration,
+                                  &sampleRenderingOffset,
+                                  &isSyncSample );
+        if ( ret == false ) {
+            fprintf( stderr, "Sample read error\n" );
+            return -1;
+        }
+        printf( "Track %u, Sample %u, Length %u\n",
+                trackId, sampleId, this_size );
+
+        for ( uint32_t ix = 0; ix < this_size; ix++ ) {
+            if ( ( ix % 16 ) == 0 ) printf( "\n%04u ", ix );
+            printf( "%02x ", sample[ix] );
+        }
+        printf( "\n" );
+    }
+    else {
+        if ( trackId == 0 ) {
+            uint32_t numTracks = MP4GetNumberOfTracks( mp4File );
+
+            for ( uint32_t i = 0; i < numTracks; i++ ) {
+                trackId = MP4FindTrackId( mp4File, i );
+                DumpTrack( mp4File, trackId );
+            }
+        }
+        else {
+            DumpTrack( mp4File, trackId );
+        }
+    }
+
+    MP4Close( mp4File );
+
+    return( 0 );
+}
diff --git a/vstudio9.0/include/mp4v2/project.h b/vstudio9.0/include/mp4v2/project.h
new file mode 100644
index 0000000..a5a97bd
--- /dev/null
+++ b/vstudio9.0/include/mp4v2/project.h
@@ -0,0 +1,30 @@
+#ifndef MP4V2_PROJECT_H
+#define MP4V2_PROJECT_H
+
+/*****************************************************************************/
+
+#define MP4V2_PROJECT_name            "MP4v2"
+#define MP4V2_PROJECT_name_lower      "mp4v2"
+#define MP4V2_PROJECT_name_upper      "MP4V2"
+#define MP4V2_PROJECT_name_formal     "MP4v2 2.0-r477"
+#define MP4V2_PROJECT_url_website     "http://code.google.com/p/mp4v2"
+#define MP4V2_PROJECT_url_downloads   "http://code.google.com/p/mp4v2/downloads/list"
+#define MP4V2_PROJECT_url_discussion  "http://groups.google.com/group/mp4v2"
+#define MP4V2_PROJECT_irc             "irc://irc.freenode.net/handbrake"
+#define MP4V2_PROJECT_bugreport       "<eddyg@myreflection.org>"
+#define MP4V2_PROJECT_version         "2.0-r477"
+#define MP4V2_PROJECT_version_hex     0x00020000
+#define MP4V2_PROJECT_version_major   2
+#define MP4V2_PROJECT_version_minor   0
+#define MP4V2_PROJECT_version_point   0
+#define MP4V2_PROJECT_repo_url        "https://mp4v2.googlecode.com/svn/trunk"
+#define MP4V2_PROJECT_repo_root       "https://mp4v2.googlecode.com/svn"
+#define MP4V2_PROJECT_repo_uuid       "6e6572fa-98a6-11dd-ad9f-f77439c74b79"
+#define MP4V2_PROJECT_repo_rev        477
+#define MP4V2_PROJECT_repo_date       "2011-06-25 15:50:07 -0700 (Sat, 25 Jun 2011)"
+#define MP4V2_PROJECT_repo_type       "developer"
+#define MP4V2_PROJECT_build           "Sat Jun 25 15:52:52 PDT 2011"
+
+/*****************************************************************************/
+
+#endif /* MP4V2_PROJECT_H */
diff --git a/vstudio9.0/libmp4v2/Version.rc b/vstudio9.0/libmp4v2/Version.rc
new file mode 100644
index 0000000..70bcdc8
--- /dev/null
+++ b/vstudio9.0/libmp4v2/Version.rc
@@ -0,0 +1,41 @@
+#include <winver.h>
+#include <mp4v2\project.h>
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MP4V2_PROJECT_version_major, MP4V2_PROJECT_version_minor, MP4V2_PROJECT_version_point, MP4V2_PROJECT_repo_rev
+ PRODUCTVERSION MP4V2_PROJECT_version_major, MP4V2_PROJECT_version_minor, MP4V2_PROJECT_version_point, MP4V2_PROJECT_repo_rev
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", MP4V2_PROJECT_name
+            VALUE "FileDescription", MP4V2_PROJECT_name_formal
+            VALUE "FileVersion", MP4V2_PROJECT_version
+            VALUE "InternalName", MP4V2_PROJECT_name_formal
+            VALUE "LegalCopyright", MP4V2_PROJECT_name
+            VALUE "OriginalFilename", "libmp4v2.dll"
+            VALUE "ProductName", MP4V2_PROJECT_name
+            VALUE "ProductVersion", MP4V2_PROJECT_version
+            VALUE "Url", MP4V2_PROJECT_url_website
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
diff --git a/vstudio9.0/libmp4v2/libmp4v2.vcproj b/vstudio9.0/libmp4v2/libmp4v2.vcproj
new file mode 100644
index 0000000..ec2faf2
--- /dev/null
+++ b/vstudio9.0/libmp4v2/libmp4v2.vcproj
@@ -0,0 +1,1688 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="libmp4v2"
+	ProjectGUID="{BDB97A37-90B8-4906-BCAB-663D983E33E3}"
+	RootNamespace="libmp4v2"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			WholeProgramOptimization="0"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			WholeProgramOptimization="0"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="2"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="4"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="MP4V2_EXPORTS;_CRT_SECURE_NO_WARNINGS"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				AdditionalIncludeDirectories="..\include"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLibrarianTool"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="include"
+			>
+			<File
+				RelativePath="..\..\include\mp4v2\chapter.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\file.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\file_prop.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\general.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\isma.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\itmf_generic.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\itmf_tags.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\mp4v2.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\platform.h"
+				>
+			</File>
+			<File
+				RelativePath="..\include\mp4v2\project.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\sample.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\streaming.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\track.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\include\mp4v2\track_prop.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="impl"
+			>
+			<File
+				RelativePath="..\..\src\3gp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_ac3.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_amr.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_avc1.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_avcC.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_chpl.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_colr.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_d263.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_dac3.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_damr.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_dref.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_elst.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_enca.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_encv.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_free.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_ftab.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_ftyp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_gmin.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_hdlr.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_hinf.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_hnti.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_href.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_mdat.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_mdhd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_meta.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_mp4s.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_mp4v.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_mvhd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_nmhd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_ohdr.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_pasp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_root.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_rtp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_s263.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_sdp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_sdtp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_smi.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_sound.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_standard.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_stbl.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_stdp.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_stsc.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_stsd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_stsz.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_stz2.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_text.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_tfhd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_tkhd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_treftype.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_trun.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_tx3g.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_udta.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_url.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_urn.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_uuid.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_video.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atom_vmhd.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\atoms.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\cmeta.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\descriptors.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\descriptors.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\enum.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\enum.tcc"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\exception.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\exception.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\impl.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\isma.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\log.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\log.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4array.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4atom.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4atom.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4container.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4container.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4descriptor.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4descriptor.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4file.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4file.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4file_io.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4info.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4property.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4property.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4track.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4track.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4util.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\mp4util.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\ocidescriptors.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\ocidescriptors.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\odcommands.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\odcommands.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\qosqualifiers.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\qosqualifiers.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\rtphint.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\rtphint.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\src.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\text.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\text.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\src\util.h"
+				>
+			</File>
+			<Filter
+				Name="bmff"
+				>
+				<File
+					RelativePath="..\..\src\bmff\bmff.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\bmff\impl.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\bmff\typebmff.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\bmff\typebmff.h"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="itmf"
+				>
+				<File
+					RelativePath="..\..\src\itmf\CoverArtBox.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\CoverArtBox.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\generic.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\generic.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\impl.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\itmf.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\Tags.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\Tags.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\type.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\itmf\type.h"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="qtff"
+				>
+				<File
+					RelativePath="..\..\src\qtff\coding.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\coding.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\ColorParameterBox.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\ColorParameterBox.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\impl.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\PictureAspectRatioBox.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\PictureAspectRatioBox.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\src\qtff\qtff.h"
+					>
+				</File>
+			</Filter>
+		</Filter>
+		<Filter
+			Name="platform"
+			>
+			<File
+				RelativePath="..\..\libplatform\endian.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\impl.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\platform.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\platform_base.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\platform_win32.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\platform_win32.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\platform_win32_impl.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libplatform\warning.h"
+				>
+			</File>
+			<Filter
+				Name="io"
+				>
+				<File
+					RelativePath="..\..\libplatform\io\File.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\io\File.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\io\File_win32.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\io\FileSystem.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\io\FileSystem.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\io\FileSystem_win32.cpp"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="number"
+				>
+				<File
+					RelativePath="..\..\libplatform\number\random.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\number\random_win32.cpp"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="process"
+				>
+				<File
+					RelativePath="..\..\libplatform\process\process.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\process\process_win32.cpp"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="prog"
+				>
+				<File
+					RelativePath="..\..\libplatform\prog\option.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\prog\option.h"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="sys"
+				>
+				<File
+					RelativePath="..\..\libplatform\sys\error.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\sys\error.h"
+					>
+				</File>
+			</Filter>
+			<Filter
+				Name="time"
+				>
+				<File
+					RelativePath="..\..\libplatform\time\time.cpp"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\time\time.h"
+					>
+				</File>
+				<File
+					RelativePath="..\..\libplatform\time\time_win32.cpp"
+					>
+				</File>
+			</Filter>
+		</Filter>
+		<Filter
+			Name="util"
+			>
+			<File
+				RelativePath="..\..\libutil\crc.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\crc.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\Database.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\Database.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\impl.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\other.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\other.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\Timecode.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\Timecode.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\TrackModifier.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\TrackModifier.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\util.h"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\Utility.cpp"
+				>
+			</File>
+			<File
+				RelativePath="..\..\libutil\Utility.h"
+				>
+			</File>
+		</Filter>
+		<Filter
+			Name="resources"
+			>
+			<File
+				RelativePath=".\Version.rc"
+				>
+				<FileConfiguration
+					Name="Debug Static (MTd)|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release Static (MT)|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug Static (MDd)|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release Static (MD)|Win32"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug Static (MTd)|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release Static (MT)|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Debug Static (MDd)|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+				<FileConfiguration
+					Name="Release Static (MD)|x64"
+					ExcludedFromBuild="true"
+					>
+					<Tool
+						Name="VCResourceCompilerTool"
+					/>
+				</FileConfiguration>
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4art/mp4art.vcproj b/vstudio9.0/mp4art/mp4art.vcproj
new file mode 100644
index 0000000..0005ef5
--- /dev/null
+++ b/vstudio9.0/mp4art/mp4art.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4art"
+	ProjectGUID="{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}"
+	RootNamespace="mp4art"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4art.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4chaps/mp4chaps.vcproj b/vstudio9.0/mp4chaps/mp4chaps.vcproj
new file mode 100644
index 0000000..3f9d92b
--- /dev/null
+++ b/vstudio9.0/mp4chaps/mp4chaps.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4chaps"
+	ProjectGUID="{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}"
+	RootNamespace="mp4chaps"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4chaps.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4extract/mp4extract.vcproj b/vstudio9.0/mp4extract/mp4extract.vcproj
new file mode 100644
index 0000000..af3c956
--- /dev/null
+++ b/vstudio9.0/mp4extract/mp4extract.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4extract"
+	ProjectGUID="{7141244A-EB2F-4CAB-8025-BA1316A3C306}"
+	RootNamespace="mp4extract"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4extract.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4file/mp4file.vcproj b/vstudio9.0/mp4file/mp4file.vcproj
new file mode 100644
index 0000000..f9f16a3
--- /dev/null
+++ b/vstudio9.0/mp4file/mp4file.vcproj
@@ -0,0 +1,909 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4file"
+	ProjectGUID="{C97FBF72-8249-4782-A34A-BEA222E9A257}"
+	RootNamespace="mp4file"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SetChecksum="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				GenerateDebugInformation="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4file.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4info/mp4info.vcproj b/vstudio9.0/mp4info/mp4info.vcproj
new file mode 100644
index 0000000..048736a
--- /dev/null
+++ b/vstudio9.0/mp4info/mp4info.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4info"
+	ProjectGUID="{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}"
+	RootNamespace="mp4info"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4info.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4subtitle/mp4subtitle.vcproj b/vstudio9.0/mp4subtitle/mp4subtitle.vcproj
new file mode 100644
index 0000000..5a83ac9
--- /dev/null
+++ b/vstudio9.0/mp4subtitle/mp4subtitle.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4subtitle"
+	ProjectGUID="{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}"
+	RootNamespace="mp4subtitle"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4subtitle.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4tags/mp4tags.vcproj b/vstudio9.0/mp4tags/mp4tags.vcproj
new file mode 100644
index 0000000..928d6b8
--- /dev/null
+++ b/vstudio9.0/mp4tags/mp4tags.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4tags"
+	ProjectGUID="{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}"
+	RootNamespace="mp4tags"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4tags.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4track/mp4track.vcproj b/vstudio9.0/mp4track/mp4track.vcproj
new file mode 100644
index 0000000..72538fa
--- /dev/null
+++ b/vstudio9.0/mp4track/mp4track.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4track"
+	ProjectGUID="{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}"
+	RootNamespace="mp4track"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4track.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4trackdump/mp4trackdump.vcproj b/vstudio9.0/mp4trackdump/mp4trackdump.vcproj
new file mode 100644
index 0000000..9dbb91b
--- /dev/null
+++ b/vstudio9.0/mp4trackdump/mp4trackdump.vcproj
@@ -0,0 +1,947 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="9.00"
+	Name="mp4trackdump"
+	ProjectGUID="{0A901CF8-28F1-489A-968A-8D3A782730D9}"
+	RootNamespace="mp4trackdump"
+	Keyword="Win32Proj"
+	TargetFrameworkVersion="196613"
+	>
+	<Platforms>
+		<Platform
+			Name="Win32"
+		/>
+		<Platform
+			Name="x64"
+		/>
+	</Platforms>
+	<ToolFiles>
+	</ToolFiles>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				SetChecksum="true"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MT)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="0"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MTd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="1"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Debug Static (MDd)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				MinimalRebuild="true"
+				BasicRuntimeChecks="3"
+				RuntimeLibrary="3"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="2"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|Win32"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="1"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+		<Configuration
+			Name="Release Static (MD)|x64"
+			OutputDirectory="..\..\bin\Windows-$(PlatformName)\$(ConfigurationName)"
+			IntermediateDirectory="..\..\obj\Windows-$(PlatformName)\$(ConfigurationName)\$(ProjectName)"
+			ConfigurationType="1"
+			CharacterSet="1"
+			WholeProgramOptimization="1"
+			>
+			<Tool
+				Name="VCPreBuildEventTool"
+			/>
+			<Tool
+				Name="VCCustomBuildTool"
+			/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"
+			/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"
+			/>
+			<Tool
+				Name="VCMIDLTool"
+				TargetEnvironment="3"
+			/>
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="1"
+				InlineFunctionExpansion="2"
+				EnableIntrinsicFunctions="true"
+				FavorSizeOrSpeed="2"
+				AdditionalIncludeDirectories="&quot;$(SolutionDir)\..\include&quot;;&quot;$(SolutionDir)\include&quot;;&quot;$(SolutionDir)\..&quot;"
+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;MP4V2_USE_STATIC_LIB"
+				RuntimeLibrary="2"
+				EnableFunctionLevelLinking="true"
+				RuntimeTypeInfo="false"
+				UsePrecompiledHeader="0"
+				WarningLevel="3"
+				DebugInformationFormat="3"
+			/>
+			<Tool
+				Name="VCManagedResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCResourceCompilerTool"
+			/>
+			<Tool
+				Name="VCPreLinkEventTool"
+			/>
+			<Tool
+				Name="VCLinkerTool"
+				LinkIncremental="1"
+				GenerateDebugInformation="true"
+				SubSystem="1"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"
+				TargetMachine="17"
+			/>
+			<Tool
+				Name="VCALinkTool"
+			/>
+			<Tool
+				Name="VCManifestTool"
+			/>
+			<Tool
+				Name="VCXDCMakeTool"
+			/>
+			<Tool
+				Name="VCBscMakeTool"
+			/>
+			<Tool
+				Name="VCFxCopTool"
+			/>
+			<Tool
+				Name="VCAppVerifierTool"
+			/>
+			<Tool
+				Name="VCPostBuildEventTool"
+			/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<File
+			RelativePath="..\..\util\mp4trackdump.cpp"
+			>
+		</File>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/vstudio9.0/mp4v2.sln b/vstudio9.0/mp4v2.sln
new file mode 100644
index 0000000..74ca0e8
--- /dev/null
+++ b/vstudio9.0/mp4v2.sln
@@ -0,0 +1,318 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4v2", "libmp4v2\libmp4v2.vcproj", "{BDB97A37-90B8-4906-BCAB-663D983E33E3}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "util", "util", "{A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4info", "mp4info\mp4info.vcproj", "{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4art", "mp4art\mp4art.vcproj", "{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4extract", "mp4extract\mp4extract.vcproj", "{7141244A-EB2F-4CAB-8025-BA1316A3C306}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4tags", "mp4tags\mp4tags.vcproj", "{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4trackdump", "mp4trackdump\mp4trackdump.vcproj", "{0A901CF8-28F1-489A-968A-8D3A782730D9}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4chaps", "mp4chaps\mp4chaps.vcproj", "{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4file", "mp4file\mp4file.vcproj", "{C97FBF72-8249-4782-A34A-BEA222E9A257}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4track", "mp4track\mp4track.vcproj", "{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mp4subtitle", "mp4subtitle\mp4subtitle.vcproj", "{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}"
+	ProjectSection(ProjectDependencies) = postProject
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3} = {BDB97A37-90B8-4906-BCAB-663D983E33E3}
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug Static (MDd)|Win32 = Debug Static (MDd)|Win32
+		Debug Static (MDd)|x64 = Debug Static (MDd)|x64
+		Debug Static (MTd)|Win32 = Debug Static (MTd)|Win32
+		Debug Static (MTd)|x64 = Debug Static (MTd)|x64
+		Debug|Win32 = Debug|Win32
+		Debug|x64 = Debug|x64
+		Release Static (MD)|Win32 = Release Static (MD)|Win32
+		Release Static (MD)|x64 = Release Static (MD)|x64
+		Release Static (MT)|Win32 = Release Static (MT)|Win32
+		Release Static (MT)|x64 = Release Static (MT)|x64
+		Release|Win32 = Release|Win32
+		Release|x64 = Release|x64
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug|Win32.Build.0 = Debug|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug|x64.ActiveCfg = Debug|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Debug|x64.Build.0 = Debug|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release|Win32.ActiveCfg = Release|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release|Win32.Build.0 = Release|Win32
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release|x64.ActiveCfg = Release|x64
+		{BDB97A37-90B8-4906-BCAB-663D983E33E3}.Release|x64.Build.0 = Release|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug|Win32.ActiveCfg = Debug|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug|Win32.Build.0 = Debug|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug|x64.ActiveCfg = Debug|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Debug|x64.Build.0 = Debug|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release|Win32.ActiveCfg = Release|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release|Win32.Build.0 = Release|Win32
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release|x64.ActiveCfg = Release|x64
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2}.Release|x64.Build.0 = Release|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug|Win32.ActiveCfg = Debug|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug|Win32.Build.0 = Debug|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug|x64.ActiveCfg = Debug|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Debug|x64.Build.0 = Debug|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release|Win32.ActiveCfg = Release|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release|Win32.Build.0 = Release|Win32
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release|x64.ActiveCfg = Release|x64
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389}.Release|x64.Build.0 = Release|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug|Win32.ActiveCfg = Debug|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug|Win32.Build.0 = Debug|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug|x64.ActiveCfg = Debug|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Debug|x64.Build.0 = Debug|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release|Win32.ActiveCfg = Release|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release|Win32.Build.0 = Release|Win32
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release|x64.ActiveCfg = Release|x64
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306}.Release|x64.Build.0 = Release|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug|Win32.ActiveCfg = Debug|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug|Win32.Build.0 = Debug|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug|x64.ActiveCfg = Debug|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Debug|x64.Build.0 = Debug|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release|Win32.ActiveCfg = Release|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release|Win32.Build.0 = Release|Win32
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release|x64.ActiveCfg = Release|x64
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061}.Release|x64.Build.0 = Release|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug|Win32.ActiveCfg = Debug|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug|Win32.Build.0 = Debug|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug|x64.ActiveCfg = Debug|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Debug|x64.Build.0 = Debug|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release|Win32.ActiveCfg = Release|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release|Win32.Build.0 = Release|Win32
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release|x64.ActiveCfg = Release|x64
+		{0A901CF8-28F1-489A-968A-8D3A782730D9}.Release|x64.Build.0 = Release|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug|Win32.ActiveCfg = Debug|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug|Win32.Build.0 = Debug|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug|x64.ActiveCfg = Debug|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Debug|x64.Build.0 = Debug|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release|Win32.ActiveCfg = Release|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release|Win32.Build.0 = Release|Win32
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release|x64.ActiveCfg = Release|x64
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1}.Release|x64.Build.0 = Release|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug|Win32.ActiveCfg = Debug|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug|Win32.Build.0 = Debug|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug|x64.ActiveCfg = Debug|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Debug|x64.Build.0 = Debug|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release|Win32.ActiveCfg = Release|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release|Win32.Build.0 = Release|Win32
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release|x64.ActiveCfg = Release|x64
+		{C97FBF72-8249-4782-A34A-BEA222E9A257}.Release|x64.Build.0 = Release|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MDd)|x64.Build.0 = Debug Static (MDd)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug Static (MTd)|x64.Build.0 = Debug Static (MTd)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug|Win32.ActiveCfg = Debug|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug|Win32.Build.0 = Debug|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug|x64.ActiveCfg = Debug|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Debug|x64.Build.0 = Debug|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MD)|x64.Build.0 = Release Static (MD)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release Static (MT)|x64.Build.0 = Release Static (MT)|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release|Win32.ActiveCfg = Release|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release|Win32.Build.0 = Release|Win32
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release|x64.ActiveCfg = Release|x64
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3}.Release|x64.Build.0 = Release|x64
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug Static (MDd)|Win32.ActiveCfg = Debug Static (MDd)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug Static (MDd)|Win32.Build.0 = Debug Static (MDd)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug Static (MDd)|x64.ActiveCfg = Debug Static (MDd)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug Static (MTd)|Win32.ActiveCfg = Debug Static (MTd)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug Static (MTd)|Win32.Build.0 = Debug Static (MTd)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug Static (MTd)|x64.ActiveCfg = Debug Static (MTd)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug|Win32.ActiveCfg = Debug|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug|Win32.Build.0 = Debug|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Debug|x64.ActiveCfg = Debug|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release Static (MD)|Win32.ActiveCfg = Release Static (MD)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release Static (MD)|Win32.Build.0 = Release Static (MD)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release Static (MD)|x64.ActiveCfg = Release Static (MD)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release Static (MT)|Win32.ActiveCfg = Release Static (MT)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release Static (MT)|Win32.Build.0 = Release Static (MT)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release Static (MT)|x64.ActiveCfg = Release Static (MT)|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release|Win32.ActiveCfg = Release|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release|Win32.Build.0 = Release|Win32
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604}.Release|x64.ActiveCfg = Release|Win32
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(NestedProjects) = preSolution
+		{FBFE9653-0F23-4F83-B4C3-08D1DB1449B2} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{E6E4F05D-BEDE-4FB4-9C68-ADA2605E1389} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{7141244A-EB2F-4CAB-8025-BA1316A3C306} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{3D0B35CE-7DA0-4339-B799-8C0FE9C78061} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{0A901CF8-28F1-489A-968A-8D3A782730D9} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{94C763EE-E990-4D5B-BF9D-148FF55DE1C1} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{C97FBF72-8249-4782-A34A-BEA222E9A257} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{EC1C77A7-E226-4B23-B12E-B94016DFB9D3} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+		{B2B082CA-748D-48A2-9AF1-4BD4F54E5604} = {A7FD8B52-7D83-474D-8CE3-BBB9B72384EE}
+	EndGlobalSection
+EndGlobal