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