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