Tag Parser  9.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 "./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
mp4ids.h
mp4atom.h
TagParser::GenericFileElement::m_nextSibling
std::unique_ptr< ImplementationType > m_nextSibling
Definition: genericfileelement.h:138
TagParser::Mp4AtomIds::SampleDescription
Definition: mp4ids.h:62
TagParser::Mp4AtomIds::DataInformation
Definition: mp4ids.h:21
TagParser::Mp4TagMediaType::Movie
TagParser::GenericFileElement::reader
CppUtilities::BinaryReader & reader()
Returns the related BinaryReader.
Definition: genericfileelement.h:245
TagParser::GenericFileElement::m_sizeLength
std::uint32_t m_sizeLength
Definition: genericfileelement.h:136
TagParser::DiagLevel::Fatal
TagParser::GenericFileElement::m_idLength
std::uint32_t m_idLength
Definition: genericfileelement.h:135
TagParser::GenericFileElement::stream
std::iostream & stream()
Returns the related stream.
Definition: genericfileelement.h:237
mp4track.h
TagParser::DiagLevel::Warning
TagParser::Mp4AtomIds::Free
Definition: mp4ids.h:27
TagParser::GenericFileElement::startOffset
std::uint64_t startOffset() const
Returns the start offset in the related stream.
Definition: genericfileelement.h:261
TagParser::Mp4Atom::firstChildOffset
std::uint64_t firstChildOffset() const
Returns the offset of the first child (relative to the start offset of this atom).
Definition: mp4atom.cpp:264
TagParser::FourccIds::Dts
Definition: mp4ids.h:259
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
TagParser::Mp4AtomIds::DataReference
Definition: mp4ids.h:22
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::Mp4Atom::isPadding
bool isPadding() const
Returns an indication whether the atom is a padding element.
Definition: mp4atom.cpp:244
TagParser::FourccIds::Mpeg4Audio
Definition: mp4ids.h:325
TagParser::Mp4AtomIds::UserData
Definition: mp4ids.h:76
TagParser::GenericFileElement::minimumElementSize
static constexpr std::uint8_t minimumElementSize()
Returns the mimimum element size.
Definition: genericfileelement.h:977
TagParser::Mp4AtomIds::MovieFragment
Definition: mp4ids.h:44
TagParser::Mp4Atom::makeHeader
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
TagParser::Mp4Atom::idToString
std::string idToString() const
Converts the specified atom ID to a printable string.
Definition: mp4atom.h:67
TagParser::FourccIds::DtsE
Definition: mp4ids.h:261
TagParser::FourccIds::Ac3
Definition: mp4ids.h:146
TagParser::GenericFileElement::dataSize
DataSizeType dataSize() const
Returns the data size of the element in byte.
Definition: genericfileelement.h:315
TagParser::GenericFileElement::m_dataSize
DataSizeType m_dataSize
Definition: genericfileelement.h:134
TagParser::GenericFileElement
The GenericFileElement class helps to parse binary files which consist of an arboreal element strucut...
Definition: genericfileelement.h:45
TagParser::FourccIds::DtsH
Definition: mp4ids.h:260
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
TagParser::Mp4AtomIds::Skip
Definition: mp4ids.h:56
TagParser::Mp4AtomIds::AvcConfiguration
Definition: mp4ids.h:15
TagParser::FourccIds::WindowsMediaAudio
Definition: mp4ids.h:391
TagParser::FourccIds::Drms
Definition: mp4ids.h:257
TagParser::Mp4AtomIds::SampleTable
Definition: mp4ids.h:58
TagParser::Mp4AtomIds::Edit
Definition: mp4ids.h:25
TagParser::DiagLevel::Critical
CppUtilities
Definition: abstractcontainer.h:15
TagParser::GenericFileElement::headerSize
std::uint32_t headerSize() const
Returns the header size of the element in byte.
Definition: genericfileelement.h:304
TagParser::Mp4Atom::isParent
bool isParent() const
Returns an indication whether the atom is a parent element.
Definition: mp4atom.cpp:190
TagParser::Mp4AtomIds::Media
Definition: mp4ids.h:34
TagParser::Mp4Atom::seekBackAndWriteAtomSize64
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
TagParser::GenericFileElement::m_id
IdentifierType m_id
Definition: genericfileelement.h:131
TagParser::FourccIds::AmrNarrowband
Definition: mp4ids.h:156
TagParser::Mp4AtomIds::TrackReference
Definition: mp4ids.h:73
TagParser::TagTargetLevel::Track
TagParser::NoDataFoundException
The exception that is thrown when the data to be parsed holds no parsable information (e....
Definition: exceptions.h:18
TagParser::TruncatedDataException
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
Definition: exceptions.h:39
TagParser::Mp4Atom::seekBackAndWriteAtomSize
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
TagParser::Mp4AtomIds::TrackFragment
Definition: mp4ids.h:71
TagParser::FourccIds::Alac
Definition: mp4ids.h:150
TagParser::Mp4Atom::internalParse
void internalParse(Diagnostics &diag)
Parses the MP4 atom.
Definition: mp4atom.cpp:61
TagParser::GenericFileElement::ContainerType
typename FileElementTraits< ImplementationType >::ContainerType ContainerType
Specifies the type of the corresponding container.
Definition: genericfileelement.h:52
TagParser::FourccIds::EAc3
Definition: mp4ids.h:266
TagParser::Mp4AtomIds::MediaInformation
Definition: mp4ids.h:40
TagParser::GenericFileElement::container
ContainerType & container()
Returns the related container.
Definition: genericfileelement.h:220
TagParser::Mp4AtomIds::MovieExtends
Definition: mp4ids.h:46
TagParser::GenericFileElement::m_firstChild
std::unique_ptr< ImplementationType > m_firstChild
Definition: genericfileelement.h:139
mp4container.h
TagParser::GenericFileElement::writer
CppUtilities::BinaryWriter & writer()
Returns the related BinaryWriter.
Definition: genericfileelement.h:253
TagParser::Mp4Atom
The Mp4Atom class helps to parse MP4 files.
Definition: mp4atom.h:38
TagParser::Mp4AtomIds::MediaInformationHeader
Definition: mp4ids.h:41
TagParser::FourccIds::DolbyMpl
Definition: mp4ids.h:317
TagParser::GenericFileElement::parent
ImplementationType * parent()
Returns the parent of the element.
Definition: genericfileelement.h:385
TagParser::Mp4AtomIds::ItunesList
Definition: mp4ids.h:31
TagParser::GenericFileElement::maxTotalSize
std::uint64_t maxTotalSize() const
Returns maximum total size.
Definition: genericfileelement.h:362
TagParser::FourccIds::Amr
Definition: mp4ids.h:155
TagParser::GenericFileElement::totalSize
std::uint64_t totalSize() const
Returns the total size of the element.
Definition: genericfileelement.h:343
TagParser::Mp4Atom::Mp4Atom
Mp4Atom(ContainerType &container, std::uint64_t startOffset)
Constructs a new top level atom with the specified container at the specified startOffset.
Definition: mp4atom.cpp:29
TagParser::MediaType::Meta
mp4tag.h