Tag Parser  6.2.2
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
mp4atom.cpp
Go to the documentation of this file.
1 #include "./mp4track.h"
2 #include "./mp4tag.h"
3 #include "./mp4atom.h"
4 #include "./mp4ids.h"
5 #include "./mp4container.h"
6 
7 #include "../exceptions.h"
8 
9 #include <c++utilities/conversion/stringbuilder.h>
10 #include <c++utilities/io/binaryreader.h>
11 #include <c++utilities/io/binarywriter.h>
12 
13 #include <sstream>
14 
15 using namespace std;
16 using namespace IoUtilities;
17 using namespace ConversionUtilities;
18 
19 namespace Media {
20 
29 Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset) :
30  GenericFileElement<Mp4Atom>(container, startOffset)
31 {}
32 
36 Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset, uint64 maxSize) :
37  GenericFileElement<Mp4Atom>(container, startOffset, maxSize)
38 {}
39 
43 Mp4Atom::Mp4Atom(GenericFileElement::implementationType &parent, uint64 startOffset) :
44  GenericFileElement<Mp4Atom>(parent, startOffset)
45 {}
46 
50 string Mp4Atom::parsingContext() const
51 {
52  return "parsing " % idToString() % " atom at " + startOffset();
53 }
54 
59 {
60  invalidateStatus();
61  static const string context("parsing MP4 atom");
62  if(maxTotalSize() < minimumElementSize()) {
63  addNotification(NotificationType::Critical, "Atom is smaller than 8 byte and hence invalid. The remaining size within the parent atom is " % numberToString(maxTotalSize()) + ".", context);
64  throw TruncatedDataException();
65  }
66  stream().seekg(startOffset());
67  m_dataSize = reader().readUInt32BE();
68  if(m_dataSize == 0) {
69  // atom size extends to rest of the file/enclosing container
70  m_dataSize = maxTotalSize();
71  }
72  if(!m_dataSize) {
73  addNotification(NotificationType::Critical, "No data found (only null bytes).", context);
74  throw NoDataFoundException();
75  }
76  if(m_dataSize < 8 && m_dataSize != 1) {
77  addNotification(NotificationType::Critical, "Atom is smaller than 8 byte and hence invalid.", context);
78  throw TruncatedDataException();
79  }
80  m_id = reader().readUInt32BE();
81  m_idLength = 4;
82  if(m_dataSize == 1) { // atom denotes 64-bit size
83  m_dataSize = reader().readUInt64BE();
84  m_sizeLength = 12; // 4 bytes indicate long size denotation + 8 bytes for actual size denotation
85  if(dataSize() < 16 && m_dataSize != 1) {
86  addNotification(NotificationType::Critical, "Atom denoting 64-bit size is smaller than 16 byte and hence invalid.", parsingContext());
87  throw TruncatedDataException();
88  }
89  } else {
90  m_sizeLength = 4;
91  }
92  if(maxTotalSize() < m_dataSize) { // currently m_dataSize holds data size plus header size!
93  addNotification(NotificationType::Warning, "The atom seems to be truncated; unable to parse siblings of that ", parsingContext());
94  m_dataSize = maxTotalSize(); // using max size instead
95  }
96  // currently m_dataSize holds data size plus header size!
97  m_dataSize -= headerSize();
98  Mp4Atom *child = nullptr;
99  if(uint64 firstChildOffset = this->firstChildOffset()) {
100  if(firstChildOffset + minimumElementSize() <= totalSize()) {
101  child = new Mp4Atom(static_cast<Mp4Atom &>(*this), startOffset() + firstChildOffset);
102  }
103  }
104  m_firstChild.reset(child);
105  Mp4Atom *sibling = nullptr;
106  if(totalSize() < maxTotalSize()) {
107  if(parent()) {
108  sibling = new Mp4Atom(*(parent()), startOffset() + totalSize());
109  } else {
110  sibling = new Mp4Atom(container(), startOffset() + totalSize(), maxTotalSize() - totalSize());
111  }
112  }
113  m_nextSibling.reset(sibling);
114 }
115 
125 void Mp4Atom::seekBackAndWriteAtomSize(std::ostream &stream, const ostream::pos_type &startOffset)
126 {
127  ostream::pos_type currentOffset = stream.tellp();
128  stream.seekp(startOffset);
129  BinaryWriter writer(&stream);
130  writer.writeUInt32BE(currentOffset - startOffset);
131  stream.seekp(currentOffset);
132 }
133 
143 void Mp4Atom::seekBackAndWriteAtomSize64(std::ostream &stream, const ostream::pos_type &startOffset)
144 {
145  ostream::pos_type currentOffset = stream.tellp();
146  stream.seekp(startOffset);
147  BinaryWriter writer(&stream);
148  writer.writeUInt32BE(1);
149  stream.seekp(4, ios_base::cur);
150  writer.writeUInt64BE(currentOffset - startOffset);
151  stream.seekp(currentOffset);
152 }
153 
157 void Mp4Atom::makeHeader(uint64 size, uint32 id, BinaryWriter &writer)
158 {
159  if(size < 0xFFFFFFFF) {
160  writer.writeUInt32BE(static_cast<uint32>(size));
161  writer.writeUInt32BE(id);
162  } else {
163  writer.writeUInt32BE(1);
164  writer.writeUInt32BE(id);
165  writer.writeUInt64BE(size);
166  }
167 }
168 
176 bool Mp4Atom::isParent() const
177 {
178  using namespace Mp4AtomIds;
179  // some atom ids are known to be parents
180  switch(id()) {
181  case Movie: case Track: case Media: case MediaInformation: case DataInformation:
182  case SampleTable: case UserData: case Meta: case ItunesList: case MovieFragment:
188  return true;
189  default:
190  if(parent()) {
191  // some atom ids are known to contain parents
192  switch(parent()->id()) {
193  case ItunesList:
194  return true;
195  default: ;
196  }
197  }
198  }
199  return false;
200 }
201 
208 bool Mp4Atom::isPadding() const
209 {
210  using namespace Mp4AtomIds;
211  switch(id()) {
212  case Free: case Skip:
213  return true;
214  default:
215  return false;
216  }
217 }
218 
228 {
229  using namespace Mp4AtomIds;
230  using namespace FourccIds;
231  if(isParent()) {
232  switch(id()) {
233  case Meta: return headerSize() + 0x4u;
234  case DataReference: return headerSize() + 0x8u;
235  default: return headerSize();
236  }
237  } else {
238  switch(id()) {
239  case SampleDescription: return headerSize() + 0x08u;
240  default: return 0x00u;
241  }
242  }
243 }
244 
245 }
void internalParse()
Parses the MP4 atom.
Definition: mp4atom.cpp:58
FileElementTraits< ImplementationType >::implementationType implementationType
Specifies the type of the actual implementation.
STL namespace.
uint64 firstChildOffset() const
Returns the offset of the first child (relative to the start offset of this atom).
Definition: mp4atom.cpp:227
bool isPadding() const
Returns an indication whether the atom is a padding element.
Definition: mp4atom.cpp:208
bool isParent() const
Returns an indication whether the atom is a parent element.
Definition: mp4atom.cpp:176
Contains utility classes helping to read and write streams.
static void seekBackAndWriteAtomSize(std::ostream &stream, const std::ostream::pos_type &startOffset)
This function helps to write the atom size after writing an atom to a stream.
Definition: mp4atom.cpp:125
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:35
The Mp4Atom class helps to parse MP4 files.
Definition: mp4atom.h:57
FileElementTraits< ImplementationType >::containerType containerType
Specifies the type of the corresponding container.
static void seekBackAndWriteAtomSize64(std::ostream &stream, const std::ostream::pos_type &startOffset)
This function helps to write the atom size after writing an atom to a stream.
Definition: mp4atom.cpp:143
The exception that is thrown when the data to be parsed holds no parsable information.
Definition: exceptions.h:19
Contains all classes and functions of the TagInfo library.
Definition: exceptions.h:9
static void makeHeader(uint64 size, uint32 id, IoUtilities::BinaryWriter &writer)
Writes an MP4 atom header to the specified stream.
Definition: mp4atom.cpp:157
Mp4Atom(containerType &container, uint64 startOffset)