2015-09-06 19:57:33 +02:00
# include "./mp4track.h"
# include "./mp4tag.h"
# include "./mp4atom.h"
# include "./mp4ids.h"
# include "./mp4container.h"
2015-09-06 15:42:18 +02:00
2015-09-06 19:57:33 +02:00
# include "../exceptions.h"
2015-04-22 19:22:01 +02:00
# include <c++utilities/io/binaryreader.h>
# include <c++utilities/io/binarywriter.h>
# include <sstream>
using namespace std ;
using namespace IoUtilities ;
using namespace ConversionUtilities ;
namespace Media {
/*!
* \ class Media : : Mp4Atom
* \ brief The Mp4Atom class helps to parse MP4 files .
*/
/*!
* \ brief Constructs a new top level atom with the specified \ a container at the specified \ a startOffset .
*/
Mp4Atom : : Mp4Atom ( GenericFileElement : : containerType & container , uint64 startOffset ) :
GenericFileElement < Mp4Atom > ( container , startOffset )
{ }
2015-06-07 00:18:28 +02:00
/*!
* \ brief Constructs a new top level atom with the specified \ a container at the specified \ a startOffset .
*/
Mp4Atom : : Mp4Atom ( GenericFileElement : : containerType & container , uint64 startOffset , uint64 maxSize ) :
GenericFileElement < Mp4Atom > ( container , startOffset , maxSize )
{ }
2015-04-22 19:22:01 +02:00
/*!
* \ brief Constructs a new sub level atom with the specified \ a parent at the specified \ a startOffset .
*/
Mp4Atom : : Mp4Atom ( GenericFileElement : : implementationType & parent , uint64 startOffset ) :
GenericFileElement < Mp4Atom > ( parent , startOffset )
{ }
/*!
* \ brief Returns the parsing context .
*/
string Mp4Atom : : parsingContext ( ) const
{
return " parsing " + idToString ( ) + " atom at " + numberToString ( startOffset ( ) ) ;
}
/*!
* \ brief Parses the MP4 atom .
*/
void Mp4Atom : : internalParse ( )
{
invalidateStatus ( ) ;
static const string context ( " parsing MP4 atom " ) ;
2015-07-13 00:57:38 +02:00
if ( maxTotalSize ( ) < minimumElementSize ( ) ) {
2015-04-22 19:22:01 +02:00
addNotification ( NotificationType : : Critical , " Atom is smaller then 8 byte and hence invalid. The maximum size within the parent atom is " + numberToString ( maxTotalSize ( ) ) + " . " , context ) ;
throw TruncatedDataException ( ) ;
}
stream ( ) . seekg ( startOffset ( ) ) ;
m_dataSize = reader ( ) . readUInt32BE ( ) ;
if ( m_dataSize = = 0 ) {
// atom size extends to rest of the file/enclosing container
m_dataSize = maxTotalSize ( ) ;
}
if ( ! m_dataSize ) {
addNotification ( NotificationType : : Critical , " No data found (only null bytes). " , context ) ;
throw NoDataFoundException ( ) ;
}
if ( m_dataSize < 8 & & m_dataSize ! = 1 ) {
addNotification ( NotificationType : : Critical , " Atom is smaller then 8 byte and hence invalid. " , context ) ;
throw TruncatedDataException ( ) ;
}
m_id = reader ( ) . readUInt32BE ( ) ;
m_idLength = 4 ;
2015-11-07 15:23:36 +01:00
if ( m_dataSize = = 1 ) { // atom denotes 64-bit size
2015-04-22 19:22:01 +02:00
m_dataSize = reader ( ) . readUInt64BE ( ) ;
m_sizeLength = 12 ; // 4 bytes indicate long size denotation + 8 bytes for actual size denotation
if ( dataSize ( ) < 16 & & m_dataSize ! = 1 ) {
addNotification ( NotificationType : : Critical , " Atom denoting 64-bit size is smaller then 16 byte and hence invalid. " , parsingContext ( ) ) ;
throw TruncatedDataException ( ) ;
}
} else {
m_sizeLength = 4 ;
}
if ( maxTotalSize ( ) < m_dataSize ) { // currently m_dataSize holds data size plus header size!
addNotification ( NotificationType : : Warning , " The atom seems to be truncated; unable to parse siblings of that " , parsingContext ( ) ) ;
m_dataSize = maxTotalSize ( ) ; // using max size instead
}
// currently m_dataSize holds data size plus header size!
m_dataSize - = headerSize ( ) ;
Mp4Atom * child = nullptr ;
if ( uint64 firstChildOffset = this - > firstChildOffset ( ) ) {
2015-07-13 00:57:38 +02:00
if ( firstChildOffset + minimumElementSize ( ) < = totalSize ( ) ) {
2015-04-22 19:22:01 +02:00
child = new Mp4Atom ( static_cast < Mp4Atom & > ( * this ) , startOffset ( ) + firstChildOffset ) ;
}
}
m_firstChild . reset ( child ) ;
Mp4Atom * sibling = nullptr ;
if ( totalSize ( ) < maxTotalSize ( ) ) {
if ( parent ( ) ) {
sibling = new Mp4Atom ( * ( parent ( ) ) , startOffset ( ) + totalSize ( ) ) ;
} else {
2015-06-07 00:18:28 +02:00
sibling = new Mp4Atom ( container ( ) , startOffset ( ) + totalSize ( ) , maxTotalSize ( ) - totalSize ( ) ) ;
2015-04-22 19:22:01 +02:00
}
}
m_nextSibling . reset ( sibling ) ;
}
/*!
* \ brief This function helps to write the atom size after writing an atom to a stream .
* \ param stream Specifies the stream .
* \ param startOffset Specifies the start offset of the atom .
*
* This function seeks back to the start offset and writes the difference between the
* previous offset and the start offset as 32 - bit unsigned integer to the \ a stream .
* Then it seeks back to the previous offset .
*/
2015-11-07 15:23:36 +01:00
void Mp4Atom : : seekBackAndWriteAtomSize ( std : : ostream & stream , const ostream : : pos_type & startOffset )
2015-04-22 19:22:01 +02:00
{
ostream : : pos_type currentOffset = stream . tellp ( ) ;
stream . seekp ( startOffset ) ;
BinaryWriter writer ( & stream ) ;
2015-11-07 15:23:36 +01:00
writer . writeUInt32BE ( currentOffset - startOffset ) ;
stream . seekp ( currentOffset ) ;
}
/*!
* \ brief This function helps to write the atom size after writing an atom to a stream .
* \ param stream Specifies the stream .
* \ param startOffset Specifies the start offset of the atom .
*
* This function seeks back to the start offset and writes the difference between the
* previous offset and the start offset as 64 - bit unsigned integer to the \ a stream .
* Then it seeks back to the previous offset .
*/
void Mp4Atom : : seekBackAndWriteAtomSize64 ( std : : ostream & stream , const ostream : : pos_type & startOffset )
{
ostream : : pos_type currentOffset = stream . tellp ( ) ;
stream . seekp ( startOffset ) ;
BinaryWriter writer ( & stream ) ;
writer . writeUInt32BE ( 1 ) ;
stream . seekp ( 4 , ios_base : : cur ) ;
writer . writeUInt64BE ( currentOffset - startOffset ) ;
2015-04-22 19:22:01 +02:00
stream . seekp ( currentOffset ) ;
}
2015-12-21 00:04:56 +01:00
/*!
* \ brief Writes an MP4 atom header to the specified \ a stream .
*/
void Mp4Atom : : makeHeader ( uint64 size , uint32 id , BinaryWriter & writer )
{
if ( size < 0xFFFFFFFF ) {
writer . writeUInt32BE ( static_cast < uint32 > ( size ) ) ;
writer . writeUInt32BE ( id ) ;
} else {
writer . writeUInt32BE ( 1 ) ;
writer . writeUInt32BE ( id ) ;
writer . writeUInt64BE ( size ) ;
}
}
2015-04-22 19:22:01 +02:00
/*!
* \ brief Returns an indication whether the atom is a parent element .
*
* \ remarks This information is not read from the atom header . Some
* atoms are simply known to be parents whereas all other
* are considered as non - parents .
*/
bool Mp4Atom : : isParent ( ) const
{
using namespace Mp4AtomIds ;
// some atom ids are known to be parents
switch ( id ( ) ) {
case Movie : case Track : case Media : case MediaInformation : case DataInformation :
case SampleTable : case UserData : case Meta : case ItunesList : case MovieFragment :
2015-07-07 03:01:48 +02:00
case TrackFragment : case MovieExtends : case DataReference : case Mp4AtomIds : : AvcConfiguration :
case FourccIds : : Mpeg4Audio : case FourccIds : : AmrNarrowband : case FourccIds : : Amr :
case FourccIds : : Drms : case FourccIds : : Alac : case FourccIds : : WindowsMediaAudio :
case FourccIds : : Ac3 : case FourccIds : : EAc3 : case FourccIds : : DolbyMpl :
case FourccIds : : Dts : case FourccIds : : DtsH : case FourccIds : : DtsE :
2015-04-22 19:22:01 +02:00
return true ;
default :
if ( parent ( ) ) {
// some atom ids are known to contain parents
switch ( parent ( ) - > id ( ) ) {
case ItunesList :
return true ;
default : ;
}
}
}
return false ;
}
/*!
* \ brief Returns an indication whether the atom is a padding element .
*
* \ remarks This information is not read from the atom header . Atoms with
* the IDs " free " and " skip " are considered as padding .
*/
bool Mp4Atom : : isPadding ( ) const
{
using namespace Mp4AtomIds ;
switch ( id ( ) ) {
case Free : case Skip :
return true ;
default :
return false ;
}
}
/*!
* \ brief Returns the offset of the first child ( relative to the start offset of this atom ) .
*
* \ remarks This information is not read from the atom header . The offsets are known
* for specific atoms .
2015-07-07 03:01:48 +02:00
* \ remarks This method returns zero for non - parent atoms which have no childs .
* \ remarks Childs with variable offset such as the " esds " - atom must be denoted !
2015-04-22 19:22:01 +02:00
*/
uint64 Mp4Atom : : firstChildOffset ( ) const
{
using namespace Mp4AtomIds ;
2015-06-12 02:35:50 +02:00
using namespace FourccIds ;
2015-04-22 19:22:01 +02:00
if ( isParent ( ) ) {
switch ( id ( ) ) {
2015-07-07 03:01:48 +02:00
case Meta : return headerSize ( ) + 0x4u ;
case DataReference : return headerSize ( ) + 0x8u ;
2015-04-22 19:22:01 +02:00
default : return headerSize ( ) ;
}
} else {
switch ( id ( ) ) {
case SampleDescription : return headerSize ( ) + 0x08u ;
default : return 0x00u ;
}
}
}
}