Tag Parser  8.0.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 "./mp4atom.h"
2 #include "./mp4container.h"
3 #include "./mp4ids.h"
4 #include "./mp4tag.h"
5 #include "./mp4track.h"
6 
7 #include "../exceptions.h"
8 #include "../mediafileinfo.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 TagParser {
21 
30 Mp4Atom::Mp4Atom(GenericFileElement::ContainerType &container, uint64 startOffset)
31  : GenericFileElement<Mp4Atom>(container, startOffset)
32 {
33 }
34 
38 Mp4Atom::Mp4Atom(GenericFileElement::ContainerType &container, uint64 startOffset, uint64 maxSize)
39  : GenericFileElement<Mp4Atom>(container, startOffset, maxSize)
40 {
41 }
42 
46 Mp4Atom::Mp4Atom(Mp4Atom &parent, uint64 startOffset)
47  : GenericFileElement<Mp4Atom>(parent, startOffset)
48 {
49 }
50 
54 string Mp4Atom::parsingContext() const
55 {
56  return "parsing " % idToString() % " atom at " + startOffset();
57 }
58 
63 {
64  static const string context("parsing MP4 atom");
65  if (maxTotalSize() < minimumElementSize()) {
66  diag.emplace_back(DiagLevel::Critical,
67  argsToString("Atom is smaller than 8 byte and hence invalid. The remaining size within the parent atom is ", maxTotalSize(), '.'),
68  context);
69  throw TruncatedDataException();
70  }
71  stream().seekg(static_cast<streamoff>(startOffset()));
72  m_dataSize = reader().readUInt32BE();
73  if (m_dataSize == 0) {
74  // atom size extends to rest of the file/enclosing container
76  }
77  if (!m_dataSize) {
78  diag.emplace_back(DiagLevel::Critical, "No data found (only null bytes).", context);
79  throw NoDataFoundException();
80  }
81  if (m_dataSize < 8 && m_dataSize != 1) {
82  diag.emplace_back(DiagLevel::Critical, "Atom is smaller than 8 byte and hence invalid.", context);
83  throw TruncatedDataException();
84  }
85  m_id = reader().readUInt32BE();
86  m_idLength = 4;
87  if (m_dataSize == 1) { // atom denotes 64-bit size
88  m_dataSize = reader().readUInt64BE();
89  m_sizeLength = 12; // 4 bytes indicate long size denotation + 8 bytes for actual size denotation
90  if (dataSize() < 16 && m_dataSize != 1) {
91  diag.emplace_back(DiagLevel::Critical, "Atom denoting 64-bit size is smaller than 16 byte and hence invalid.", parsingContext());
92  throw TruncatedDataException();
93  }
94  } else {
95  m_sizeLength = 4;
96  }
97  if (maxTotalSize() < m_dataSize) { // currently m_dataSize holds data size plus header size!
98  diag.emplace_back(DiagLevel::Warning, "The atom seems to be truncated; unable to parse siblings of that ", parsingContext());
99  m_dataSize = maxTotalSize(); // using max size instead
100  }
101  // currently m_dataSize holds data size plus header size!
102  m_dataSize -= headerSize();
103  Mp4Atom *child = nullptr;
104  if (uint64 firstChildOffset = this->firstChildOffset()) {
106  child = new Mp4Atom(static_cast<Mp4Atom &>(*this), startOffset() + firstChildOffset);
107  }
108  }
109  m_firstChild.reset(child);
110  Mp4Atom *sibling = nullptr;
111  if (totalSize() < maxTotalSize()) {
112  if (parent()) {
113  sibling = new Mp4Atom(*(parent()), startOffset() + totalSize());
114  } else {
115  sibling = new Mp4Atom(container(), startOffset() + totalSize(), maxTotalSize() - totalSize());
116  }
117  }
118  m_nextSibling.reset(sibling);
119 }
120 
134 void Mp4Atom::seekBackAndWriteAtomSize(std::ostream &stream, const ostream::pos_type &startOffset, Diagnostics &diag)
135 {
136  ostream::pos_type currentOffset = stream.tellp();
137  const auto atomSize(currentOffset - startOffset);
138  if (atomSize > numeric_limits<uint32>::max()) {
139  diag.emplace_back(DiagLevel::Fatal, argsToString(atomSize, " exceeds maximum."), "write 32-bit atom size");
140  throw Failure();
141  }
142  stream.seekp(startOffset);
143  BinaryWriter writer(&stream);
144  writer.writeUInt32BE(static_cast<uint32>(atomSize));
145  stream.seekp(currentOffset);
146 }
147 
158 void Mp4Atom::seekBackAndWriteAtomSize64(std::ostream &stream, const ostream::pos_type &startOffset)
159 {
160  ostream::pos_type currentOffset = stream.tellp();
161  stream.seekp(startOffset);
162  BinaryWriter writer(&stream);
163  writer.writeUInt32BE(1);
164  stream.seekp(4, ios_base::cur);
165  writer.writeUInt64BE(static_cast<uint64>(currentOffset - startOffset));
166  stream.seekp(currentOffset);
167 }
168 
172 void Mp4Atom::makeHeader(uint64 size, uint32 id, BinaryWriter &writer)
173 {
174  if (size < numeric_limits<uint32>::max()) {
175  writer.writeUInt32BE(static_cast<uint32>(size));
176  writer.writeUInt32BE(id);
177  } else {
178  writer.writeUInt32BE(1);
179  writer.writeUInt32BE(id);
180  writer.writeUInt64BE(size);
181  }
182 }
183 
191 bool Mp4Atom::isParent() const
192 {
193  using namespace Mp4AtomIds;
194  // some atom ids are known to be parents
195  switch (id()) {
196  case Movie:
197  case Track:
198  case Media:
199  case MediaInformation:
200  case DataInformation:
201  case SampleTable:
202  case UserData:
203  case Meta:
204  case ItunesList:
205  case MovieFragment:
206  case TrackFragment:
207  case MovieExtends:
208  case DataReference:
212  case FourccIds::Amr:
213  case FourccIds::Drms:
214  case FourccIds::Alac:
216  case FourccIds::Ac3:
217  case FourccIds::EAc3:
218  case FourccIds::DolbyMpl:
219  case FourccIds::Dts:
220  case FourccIds::DtsH:
221  case FourccIds::DtsE:
222  return true;
223  default:
224  if (parent()) {
225  // some atom ids are known to contain parents
226  switch (parent()->id()) {
227  case ItunesList:
228  return true;
229  default:;
230  }
231  }
232  }
233  return false;
234 }
235 
242 bool Mp4Atom::isPadding() const
243 {
244  using namespace Mp4AtomIds;
245  switch (id()) {
246  case Free:
247  case Skip:
248  return true;
249  default:
250  return false;
251  }
252 }
253 
263 {
264  using namespace Mp4AtomIds;
265  using namespace FourccIds;
266  if (isParent()) {
267  switch (id()) {
268  case Meta:
269  if (parent() && parent()->id() == Mp4AtomIds::UserData) {
270  return headerSize() + 0x4u;
271  }
272  return headerSize();
273  case DataReference:
274  return headerSize() + 0x8u;
275  default:
276  return headerSize();
277  }
278  } else {
279  switch (id()) {
280  case SampleDescription:
281  return headerSize() + 0x08u;
282  default:
283  return 0x00u;
284  }
285  }
286 }
287 
288 } // namespace TagParser
bool isParent() const
Returns an indication whether the atom is a parent element.
Definition: mp4atom.cpp:191
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:32
Mp4Atom(ContainerType &container, uint64 startOffset)
Constructs a new top level atom with the specified container at the specified startOffset.
Definition: mp4atom.cpp:30
std::unique_ptr< ImplementationType > m_firstChild
std::unique_ptr< ImplementationType > m_nextSibling
DataSizeType dataSize() const
Returns the data size of the element in byte.
STL namespace.
uint64 startOffset() const
Returns the start offset in the related stream.
The Mp4Atom class helps to parse MP4 files.
Definition: mp4atom.h:38
bool isPadding() const
Returns an indication whether the atom is a padding element.
Definition: mp4atom.cpp:242
uint64 totalSize() const
Returns the total size of the element.
The GenericFileElement class helps to parse binary files which consist of an arboreal element strucut...
static void seekBackAndWriteAtomSize(std::ostream &stream, const std::ostream::pos_type &startOffset, Diagnostics &diag)
This function helps to write the atom size after writing an atom to a stream.
Definition: mp4atom.cpp:134
static constexpr byte minimumElementSize()
Returns the mimimum element size.
IoUtilities::BinaryReader & reader()
Returns the related BinaryReader.
Contains utility classes helping to read and write streams.
ContainerType & container()
Returns the related container.
void internalParse(Diagnostics &diag)
Parses the MP4 atom.
Definition: mp4atom.cpp:62
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:158
The exception that is thrown when the data to be parsed holds no parsable information.
Definition: exceptions.h:18
uint64 firstChildOffset() const
Returns the offset of the first child (relative to the start offset of this atom).
Definition: mp4atom.cpp:262
uint32 headerSize() const
Returns the header size of the element in byte.
IoUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
uint64 maxTotalSize() const
Returns maximum total size.
static void makeHeader(uint64 size, uint32 id, IoUtilities::BinaryWriter &writer)
Writes an MP4 atom header to the specified stream.
Definition: mp4atom.cpp:172
typename FileElementTraits< ImplementationType >::ContainerType ContainerType
Specifies the type of the corresponding container.
std::iostream & stream()
Returns the related stream.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:9
std::string idToString() const
Converts the specified atom ID to a printable string.
Definition: mp4atom.h:67
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
ImplementationType * parent()
Returns the parent of the element.