Tag Parser  7.0.3
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(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 
130 void Mp4Atom::seekBackAndWriteAtomSize(std::ostream &stream, const ostream::pos_type &startOffset)
131 {
132  ostream::pos_type currentOffset = stream.tellp();
133  stream.seekp(startOffset);
134  BinaryWriter writer(&stream);
135  writer.writeUInt32BE(currentOffset - startOffset);
136  stream.seekp(currentOffset);
137 }
138 
148 void Mp4Atom::seekBackAndWriteAtomSize64(std::ostream &stream, const ostream::pos_type &startOffset)
149 {
150  ostream::pos_type currentOffset = stream.tellp();
151  stream.seekp(startOffset);
152  BinaryWriter writer(&stream);
153  writer.writeUInt32BE(1);
154  stream.seekp(4, ios_base::cur);
155  writer.writeUInt64BE(currentOffset - startOffset);
156  stream.seekp(currentOffset);
157 }
158 
162 void Mp4Atom::makeHeader(uint64 size, uint32 id, BinaryWriter &writer)
163 {
164  if (size < numeric_limits<uint32>::max()) {
165  writer.writeUInt32BE(static_cast<uint32>(size));
166  writer.writeUInt32BE(id);
167  } else {
168  writer.writeUInt32BE(1);
169  writer.writeUInt32BE(id);
170  writer.writeUInt64BE(size);
171  }
172 }
173 
181 bool Mp4Atom::isParent() const
182 {
183  using namespace Mp4AtomIds;
184  // some atom ids are known to be parents
185  switch (id()) {
186  case Movie:
187  case Track:
188  case Media:
189  case MediaInformation:
190  case DataInformation:
191  case SampleTable:
192  case UserData:
193  case Meta:
194  case ItunesList:
195  case MovieFragment:
196  case TrackFragment:
197  case MovieExtends:
198  case DataReference:
202  case FourccIds::Amr:
203  case FourccIds::Drms:
204  case FourccIds::Alac:
206  case FourccIds::Ac3:
207  case FourccIds::EAc3:
208  case FourccIds::DolbyMpl:
209  case FourccIds::Dts:
210  case FourccIds::DtsH:
211  case FourccIds::DtsE:
212  return true;
213  default:
214  if (parent()) {
215  // some atom ids are known to contain parents
216  switch (parent()->id()) {
217  case ItunesList:
218  return true;
219  default:;
220  }
221  }
222  }
223  return false;
224 }
225 
232 bool Mp4Atom::isPadding() const
233 {
234  using namespace Mp4AtomIds;
235  switch (id()) {
236  case Free:
237  case Skip:
238  return true;
239  default:
240  return false;
241  }
242 }
243 
253 {
254  using namespace Mp4AtomIds;
255  using namespace FourccIds;
256  if (isParent()) {
257  switch (id()) {
258  case Meta:
259  return headerSize() + 0x4u;
260  case DataReference:
261  return headerSize() + 0x8u;
262  default:
263  return headerSize();
264  }
265  } else {
266  switch (id()) {
267  case SampleDescription:
268  return headerSize() + 0x08u;
269  default:
270  return 0x00u;
271  }
272  }
273 }
274 
275 } // namespace TagParser
bool isParent() const
Returns an indication whether the atom is a parent element.
Definition: mp4atom.cpp:181
FileElementTraits< ImplementationType >::ContainerType ContainerType
Specifies the type of the corresponding container.
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.
bool isPadding() const
Returns an indication whether the atom is a padding element.
Definition: mp4atom.cpp:232
uint64 totalSize() const
Returns the total size of the element.
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:148
uint64 firstChildOffset() const
Returns the offset of the first child (relative to the start offset of this atom).
Definition: mp4atom.cpp:252
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:130
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:162
std::iostream & stream()
Returns the related stream.
std::string idToString() const
Converts the specified atom ID to a printable string.
Definition: mp4atom.h:67
ImplementationType * parent()
Returns the parent of the element.