Tag Parser  8.3.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 Edit:
199  case Media:
200  case MediaInformation:
202  case DataInformation:
203  case SampleTable:
204  case UserData:
205  case Meta:
206  case ItunesList:
207  case MovieFragment:
208  case TrackFragment:
209  case TrackReference:
210  case MovieExtends:
211  case DataReference:
215  case FourccIds::Amr:
216  case FourccIds::Drms:
217  case FourccIds::Alac:
219  case FourccIds::Ac3:
220  case FourccIds::EAc3:
221  case FourccIds::DolbyMpl:
222  case FourccIds::Dts:
223  case FourccIds::DtsH:
224  case FourccIds::DtsE:
225  return true;
226  default:
227  if (parent()) {
228  // some atom ids are known to contain parents
229  switch (parent()->id()) {
230  case ItunesList:
231  return true;
232  default:;
233  }
234  }
235  }
236  return false;
237 }
238 
245 bool Mp4Atom::isPadding() const
246 {
247  using namespace Mp4AtomIds;
248  switch (id()) {
249  case Free:
250  case Skip:
251  return true;
252  default:
253  return false;
254  }
255 }
256 
266 {
267  using namespace Mp4AtomIds;
268  using namespace FourccIds;
269  if (isParent()) {
270  switch (id()) {
271  case Meta:
272  if (parent() && parent()->id() == Mp4AtomIds::UserData) {
273  return headerSize() + 0x4u;
274  }
275  return headerSize();
276  case DataReference:
277  return headerSize() + 0x8u;
278  default:
279  return headerSize();
280  }
281  } else {
282  switch (id()) {
283  case SampleDescription:
284  return headerSize() + 0x08u;
285  default:
286  return 0x00u;
287  }
288  }
289 }
290 
291 } // 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:39
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.
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:245
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 (e....
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:265
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.