Tag Parser  6.5.0
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 "../mediafileinfo.h"
8 #include "../exceptions.h"
9 
10 #include <c++utilities/conversion/stringbuilder.h>
11 #include <c++utilities/io/binaryreader.h>
12 #include <c++utilities/io/binarywriter.h>
13 
14 #include <sstream>
15 
16 using namespace std;
17 using namespace IoUtilities;
18 using namespace ConversionUtilities;
19 
20 namespace Media {
21 
30 Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset) :
31  GenericFileElement<Mp4Atom>(container, startOffset)
32 {}
33 
37 Mp4Atom::Mp4Atom(GenericFileElement::containerType &container, uint64 startOffset, uint64 maxSize) :
38  GenericFileElement<Mp4Atom>(container, startOffset, maxSize)
39 {}
40 
44 Mp4Atom::Mp4Atom(GenericFileElement::implementationType &parent, uint64 startOffset) :
45  GenericFileElement<Mp4Atom>(parent, startOffset)
46 {}
47 
51 string Mp4Atom::parsingContext() const
52 {
53  return "parsing " % idToString() % " atom at " + startOffset();
54 }
55 
60 {
61  invalidateStatus();
62  static const string context("parsing MP4 atom");
63  if(maxTotalSize() < minimumElementSize()) {
64  addNotification(NotificationType::Critical, "Atom is smaller than 8 byte and hence invalid. The remaining size within the parent atom is " % numberToString(maxTotalSize()) + ".", context);
65  throw TruncatedDataException();
66  }
67  stream().seekg(startOffset());
68  m_dataSize = reader().readUInt32BE();
69  if(m_dataSize == 0) {
70  // atom size extends to rest of the file/enclosing container
71  m_dataSize = maxTotalSize();
72  }
73  if(!m_dataSize) {
74  addNotification(NotificationType::Critical, "No data found (only null bytes).", context);
75  throw NoDataFoundException();
76  }
77  if(m_dataSize < 8 && m_dataSize != 1) {
78  addNotification(NotificationType::Critical, "Atom is smaller than 8 byte and hence invalid.", context);
79  throw TruncatedDataException();
80  }
81  m_id = reader().readUInt32BE();
82  m_idLength = 4;
83  if(m_dataSize == 1) { // atom denotes 64-bit size
84  m_dataSize = reader().readUInt64BE();
85  m_sizeLength = 12; // 4 bytes indicate long size denotation + 8 bytes for actual size denotation
86  if(dataSize() < 16 && m_dataSize != 1) {
87  addNotification(NotificationType::Critical, "Atom denoting 64-bit size is smaller than 16 byte and hence invalid.", parsingContext());
88  throw TruncatedDataException();
89  }
90  } else {
91  m_sizeLength = 4;
92  }
93  if(maxTotalSize() < m_dataSize) { // currently m_dataSize holds data size plus header size!
94  addNotification(NotificationType::Warning, "The atom seems to be truncated; unable to parse siblings of that ", parsingContext());
95  m_dataSize = maxTotalSize(); // using max size instead
96  }
97  // currently m_dataSize holds data size plus header size!
98  m_dataSize -= headerSize();
99  Mp4Atom *child = nullptr;
100  if(uint64 firstChildOffset = this->firstChildOffset()) {
101  if(firstChildOffset + minimumElementSize() <= totalSize()) {
102  child = new Mp4Atom(static_cast<Mp4Atom &>(*this), startOffset() + firstChildOffset);
103  }
104  }
105  m_firstChild.reset(child);
106  Mp4Atom *sibling = nullptr;
107  if(totalSize() < maxTotalSize()) {
108  if(parent()) {
109  sibling = new Mp4Atom(*(parent()), startOffset() + totalSize());
110  } else {
111  sibling = new Mp4Atom(container(), startOffset() + totalSize(), maxTotalSize() - totalSize());
112  }
113  }
114  m_nextSibling.reset(sibling);
115 }
116 
126 void Mp4Atom::seekBackAndWriteAtomSize(std::ostream &stream, const ostream::pos_type &startOffset)
127 {
128  ostream::pos_type currentOffset = stream.tellp();
129  stream.seekp(startOffset);
130  BinaryWriter writer(&stream);
131  writer.writeUInt32BE(currentOffset - startOffset);
132  stream.seekp(currentOffset);
133 }
134 
144 void Mp4Atom::seekBackAndWriteAtomSize64(std::ostream &stream, const ostream::pos_type &startOffset)
145 {
146  ostream::pos_type currentOffset = stream.tellp();
147  stream.seekp(startOffset);
148  BinaryWriter writer(&stream);
149  writer.writeUInt32BE(1);
150  stream.seekp(4, ios_base::cur);
151  writer.writeUInt64BE(currentOffset - startOffset);
152  stream.seekp(currentOffset);
153 }
154 
158 void Mp4Atom::makeHeader(uint64 size, uint32 id, BinaryWriter &writer)
159 {
160  if(size < numeric_limits<uint32>::max()) {
161  writer.writeUInt32BE(static_cast<uint32>(size));
162  writer.writeUInt32BE(id);
163  } else {
164  writer.writeUInt32BE(1);
165  writer.writeUInt32BE(id);
166  writer.writeUInt64BE(size);
167  }
168 }
169 
177 bool Mp4Atom::isParent() const
178 {
179  using namespace Mp4AtomIds;
180  // some atom ids are known to be parents
181  switch(id()) {
182  case Movie: case Track: case Media: case MediaInformation: case DataInformation:
183  case SampleTable: case UserData: case Meta: case ItunesList: case MovieFragment:
189  return true;
190  default:
191  if(parent()) {
192  // some atom ids are known to contain parents
193  switch(parent()->id()) {
194  case ItunesList:
195  return true;
196  default: ;
197  }
198  }
199  }
200  return false;
201 }
202 
209 bool Mp4Atom::isPadding() const
210 {
211  using namespace Mp4AtomIds;
212  switch(id()) {
213  case Free: case Skip:
214  return true;
215  default:
216  return false;
217  }
218 }
219 
229 {
230  using namespace Mp4AtomIds;
231  using namespace FourccIds;
232  if(isParent()) {
233  switch(id()) {
234  case Meta: return headerSize() + 0x4u;
235  case DataReference: return headerSize() + 0x8u;
236  default: return headerSize();
237  }
238  } else {
239  switch(id()) {
240  case SampleDescription: return headerSize() + 0x08u;
241  default: return 0x00u;
242  }
243  }
244 }
245 
246 }
void internalParse()
Parses the MP4 atom.
Definition: mp4atom.cpp:59
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:228
bool isPadding() const
Returns an indication whether the atom is a padding element.
Definition: mp4atom.cpp:209
bool isParent() const
Returns an indication whether the atom is a parent element.
Definition: mp4atom.cpp:177
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:126
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:144
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:158
Mp4Atom(containerType &container, uint64 startOffset)