Tag Parser  9.3.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
mp4track.cpp
Go to the documentation of this file.
1 #include "./mp4track.h"
2 #include "./mp4atom.h"
3 #include "./mp4container.h"
4 #include "./mp4ids.h"
5 #include "./mpeg4descriptor.h"
6 
7 #include "../av1/av1configuration.h"
8 
9 #include "../avc/avcconfiguration.h"
10 
11 #include "../mpegaudio/mpegaudioframe.h"
12 #include "../mpegaudio/mpegaudioframestream.h"
13 
14 #include "../exceptions.h"
15 #include "../mediaformat.h"
16 
17 #include <c++utilities/conversion/stringbuilder.h>
18 #include <c++utilities/io/binaryreader.h>
19 #include <c++utilities/io/binarywriter.h>
20 #include <c++utilities/io/bitreader.h>
21 
22 #include <cmath>
23 #include <locale>
24 
25 using namespace std;
26 using namespace CppUtilities;
27 
28 namespace TagParser {
29 
37  friend class Mp4Track;
38 
39 private:
40  constexpr TrackHeaderInfo();
41 
43  std::uint64_t requiredSize;
45  bool canUseExisting;
47  bool truncated;
49  std::uint8_t version;
51  bool versionUnknown;
53  std::uint8_t additionalDataOffset;
55  bool discardBuffer;
56 };
57 
58 constexpr TrackHeaderInfo::TrackHeaderInfo()
59  : requiredSize(100)
60  , canUseExisting(false)
61  , truncated(false)
62  , version(0)
63  , versionUnknown(false)
64  , additionalDataOffset(0)
65  , discardBuffer(false)
66 {
67 }
68 
70 const DateTime startDate = DateTime::fromDate(1904, 1, 1);
71 
79  : audioObjectType(0)
80  , sampleFrequencyIndex(0xF)
81  , sampleFrequency(0)
82  , channelConfiguration(0)
83  , extensionAudioObjectType(0)
84  , sbrPresent(false)
85  , psPresent(false)
86  , extensionSampleFrequencyIndex(0xF)
87  , extensionSampleFrequency(0)
88  , extensionChannelConfiguration(0)
89  , frameLengthFlag(false)
90  , dependsOnCoreCoder(false)
91  , coreCoderDelay(0)
92  , extensionFlag(0)
93  , layerNr(0)
94  , numOfSubFrame(0)
95  , layerLength(0)
96  , resilienceFlags(0)
97  , epConfig(0)
98 {
99 }
100 
110  : profile(0)
111 {
112 }
113 
131  : AbstractTrack(trakAtom.stream(), trakAtom.startOffset())
132  , m_trakAtom(&trakAtom)
133  , m_tkhdAtom(nullptr)
134  , m_mdiaAtom(nullptr)
135  , m_mdhdAtom(nullptr)
136  , m_hdlrAtom(nullptr)
137  , m_minfAtom(nullptr)
138  , m_stblAtom(nullptr)
139  , m_stsdAtom(nullptr)
140  , m_stscAtom(nullptr)
141  , m_stcoAtom(nullptr)
142  , m_stszAtom(nullptr)
143  , m_framesPerSample(1)
144  , m_chunkOffsetSize(4)
145  , m_chunkCount(0)
146  , m_sampleToChunkEntryCount(0)
147 {
148 }
149 
154 {
155 }
156 
158 {
159  return TrackType::Mp4Track;
160 }
161 
172 std::vector<std::uint64_t> Mp4Track::readChunkOffsets(bool parseFragments, Diagnostics &diag)
173 {
174  static const string context("reading chunk offset table of MP4 track");
175  if (!isHeaderValid() || !m_istream) {
176  diag.emplace_back(DiagLevel::Critical, "Track has not been parsed.", context);
177  throw InvalidDataException();
178  }
179  vector<std::uint64_t> offsets;
180  if (m_stcoAtom) {
181  // verify integrity of the chunk offset table
182  std::uint64_t actualTableSize = m_stcoAtom->dataSize();
183  if (actualTableSize < (8 + chunkOffsetSize())) {
184  diag.emplace_back(DiagLevel::Critical, "The stco atom is truncated. There are no chunk offsets present.", context);
185  throw InvalidDataException();
186  } else {
187  actualTableSize -= 8;
188  }
189  std::uint32_t actualChunkCount = chunkCount();
190  std::uint64_t calculatedTableSize = chunkCount() * chunkOffsetSize();
191  if (calculatedTableSize < actualTableSize) {
192  diag.emplace_back(
193  DiagLevel::Critical, "The stco atom stores more chunk offsets as denoted. The additional chunk offsets will be ignored.", context);
194  } else if (calculatedTableSize > actualTableSize) {
195  diag.emplace_back(DiagLevel::Critical, "The stco atom is truncated. It stores less chunk offsets as denoted.", context);
196  actualChunkCount = static_cast<std::uint32_t>(floor(static_cast<double>(actualTableSize) / static_cast<double>(chunkOffsetSize())));
197  }
198  // read the table
199  offsets.reserve(actualChunkCount);
200  m_istream->seekg(static_cast<streamoff>(m_stcoAtom->dataOffset() + 8));
201  switch (chunkOffsetSize()) {
202  case 4:
203  for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
204  offsets.push_back(reader().readUInt32BE());
205  }
206  break;
207  case 8:
208  for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
209  offsets.push_back(reader().readUInt64BE());
210  }
211  break;
212  default:
213  diag.emplace_back(DiagLevel::Critical, "The determined chunk offset size is invalid.", context);
214  throw InvalidDataException();
215  }
216  }
217  // read sample offsets of fragments
218  if (parseFragments) {
219  std::uint64_t totalDuration = 0;
220  for (Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingByIdIncludingThis(Mp4AtomIds::MovieFragment, diag); moofAtom;
221  moofAtom = moofAtom->siblingById(Mp4AtomIds::MovieFragment, diag)) {
222  moofAtom->parse(diag);
223  for (Mp4Atom *trafAtom = moofAtom->childById(Mp4AtomIds::TrackFragment, diag); trafAtom;
224  trafAtom = trafAtom->siblingById(Mp4AtomIds::TrackFragment, diag)) {
225  trafAtom->parse(diag);
226  for (Mp4Atom *tfhdAtom = trafAtom->childById(Mp4AtomIds::TrackFragmentHeader, diag); tfhdAtom;
227  tfhdAtom = tfhdAtom->siblingById(Mp4AtomIds::TrackFragmentHeader, diag)) {
228  tfhdAtom->parse(diag);
229  std::uint32_t calculatedDataSize = 0;
230  if (tfhdAtom->dataSize() < calculatedDataSize) {
231  diag.emplace_back(DiagLevel::Critical, "tfhd atom is truncated.", context);
232  } else {
233  inputStream().seekg(static_cast<streamoff>(tfhdAtom->dataOffset() + 1));
234  const std::uint32_t flags = reader().readUInt24BE();
235  if (m_id == reader().readUInt32BE()) { // check track ID
236  if (flags & 0x000001) { // base-data-offset present
237  calculatedDataSize += 8;
238  }
239  if (flags & 0x000002) { // sample-description-index present
240  calculatedDataSize += 4;
241  }
242  if (flags & 0x000008) { // default-sample-duration present
243  calculatedDataSize += 4;
244  }
245  if (flags & 0x000010) { // default-sample-size present
246  calculatedDataSize += 4;
247  }
248  if (flags & 0x000020) { // default-sample-flags present
249  calculatedDataSize += 4;
250  }
251  // some variables are currently skipped because they are currently not interesting
252  //uint64 baseDataOffset = moofAtom->startOffset();
253  //uint32 defaultSampleDescriptionIndex = 0;
254  std::uint32_t defaultSampleDuration = 0;
255  std::uint32_t defaultSampleSize = 0;
256  //uint32 defaultSampleFlags = 0;
257  if (tfhdAtom->dataSize() < calculatedDataSize) {
258  diag.emplace_back(DiagLevel::Critical, "tfhd atom is truncated (presence of fields denoted).", context);
259  } else {
260  if (flags & 0x000001) { // base-data-offset present
261  //baseDataOffset = reader.readUInt64();
262  inputStream().seekg(8, ios_base::cur);
263  }
264  if (flags & 0x000002) { // sample-description-index present
265  //defaultSampleDescriptionIndex = reader.readUInt32();
266  inputStream().seekg(4, ios_base::cur);
267  }
268  if (flags & 0x000008) { // default-sample-duration present
269  defaultSampleDuration = reader().readUInt32BE();
270  //inputStream().seekg(4, ios_base::cur);
271  }
272  if (flags & 0x000010) { // default-sample-size present
273  defaultSampleSize = reader().readUInt32BE();
274  }
275  if (flags & 0x000020) { // default-sample-flags present
276  //defaultSampleFlags = reader().readUInt32BE();
277  inputStream().seekg(4, ios_base::cur);
278  }
279  }
280  for (Mp4Atom *trunAtom = trafAtom->childById(Mp4AtomIds::TrackFragmentRun, diag); trunAtom;
281  trunAtom = trunAtom->siblingById(Mp4AtomIds::TrackFragmentRun, diag)) {
282  std::uint32_t calculatedDataSize = 8;
283  if (trunAtom->dataSize() < calculatedDataSize) {
284  diag.emplace_back(DiagLevel::Critical, "trun atom is truncated.", context);
285  } else {
286  inputStream().seekg(static_cast<streamoff>(trunAtom->dataOffset() + 1));
287  std::uint32_t flags = reader().readUInt24BE();
288  std::uint32_t sampleCount = reader().readUInt32BE();
290  if (flags & 0x000001) { // data offset present
291  calculatedDataSize += 4;
292  }
293  if (flags & 0x000004) { // first-sample-flags present
294  calculatedDataSize += 4;
295  }
296  std::uint32_t entrySize = 0;
297  if (flags & 0x000100) { // sample-duration present
298  entrySize += 4;
299  }
300  if (flags & 0x000200) { // sample-size present
301  entrySize += 4;
302  }
303  if (flags & 0x000400) { // sample-flags present
304  entrySize += 4;
305  }
306  if (flags & 0x000800) { // sample-composition-time-offsets present
307  entrySize += 4;
308  }
309  calculatedDataSize += entrySize * sampleCount;
310  if (trunAtom->dataSize() < calculatedDataSize) {
311  diag.emplace_back(DiagLevel::Critical, "trun atom is truncated (presence of fields denoted).", context);
312  } else {
313  if (flags & 0x000001) { // data offset present
314  inputStream().seekg(4, ios_base::cur);
315  //int32 dataOffset = reader().readInt32BE();
316  }
317  if (flags & 0x000004) { // first-sample-flags present
318  inputStream().seekg(4, ios_base::cur);
319  }
320  for (std::uint32_t i = 0; i < sampleCount; ++i) {
321  if (flags & 0x000100) { // sample-duration present
322  totalDuration += reader().readUInt32BE();
323  } else {
324  totalDuration += defaultSampleDuration;
325  }
326  if (flags & 0x000200) { // sample-size present
327  m_sampleSizes.push_back(reader().readUInt32BE());
328  m_size += m_sampleSizes.back();
329  } else {
330  m_size += defaultSampleSize;
331  }
332  if (flags & 0x000400) { // sample-flags present
333  inputStream().seekg(4, ios_base::cur);
334  }
335  if (flags & 0x000800) { // sample-composition-time-offsets present
336  inputStream().seekg(4, ios_base::cur);
337  }
338  }
339  }
340  }
341  }
342  if (m_sampleSizes.empty() && defaultSampleSize) {
343  m_sampleSizes.push_back(defaultSampleSize);
344  }
345  }
346  }
347  }
348  }
349  }
350  }
351  return offsets;
352 }
353 
358 std::uint64_t Mp4Track::accumulateSampleSizes(size_t &sampleIndex, size_t count, Diagnostics &diag)
359 {
360  if (sampleIndex + count <= m_sampleSizes.size()) {
361  std::uint64_t sum = 0;
362  for (size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
363  sum += m_sampleSizes[sampleIndex];
364  }
365  return sum;
366  } else if (m_sampleSizes.size() == 1) {
367  sampleIndex += count;
368  return static_cast<std::uint64_t>(m_sampleSizes.front()) * count;
369  } else {
370  diag.emplace_back(DiagLevel::Critical, "There are not as many sample size entries as samples.", "reading chunk sizes of MP4 track");
371  throw InvalidDataException();
372  }
373 }
374 
383 void Mp4Track::addChunkSizeEntries(
384  std::vector<std::uint64_t> &chunkSizeTable, size_t count, size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag)
385 {
386  for (size_t i = 0; i < count; ++i) {
387  chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex, sampleCount, diag));
388  }
389 }
390 
395 TrackHeaderInfo Mp4Track::verifyPresentTrackHeader() const
396 {
397  TrackHeaderInfo info;
398 
399  // return the default TrackHeaderInfo in case there is no track header prsent
400  if (!m_tkhdAtom) {
401  return info;
402  }
403 
404  // ensure the tkhd atom is buffered but mark the buffer to be discarded again if it has not been present
405  info.discardBuffer = m_tkhdAtom->buffer() == nullptr;
406  if (info.discardBuffer) {
407  m_tkhdAtom->makeBuffer();
408  }
409 
410  // check the version of the existing tkhd atom to determine where additional data starts
411  switch (info.version = static_cast<std::uint8_t>(m_tkhdAtom->buffer()[m_tkhdAtom->headerSize()])) {
412  case 0:
413  info.additionalDataOffset = 32;
414  break;
415  case 1:
416  info.additionalDataOffset = 44;
417  break;
418  default:
419  info.additionalDataOffset = 44;
420  info.versionUnknown = true;
421  }
422 
423  // check whether the existing tkhd atom is not truncated
424  if (info.additionalDataOffset + 48u <= m_tkhdAtom->dataSize()) {
425  info.canUseExisting = true;
426  } else {
427  info.truncated = true;
428  info.canUseExisting = info.additionalDataOffset < m_tkhdAtom->dataSize();
429  if (!info.canUseExisting && info.discardBuffer) {
430  m_tkhdAtom->discardBuffer();
431  }
432  }
433 
434  // determine required size
435  info.requiredSize = m_tkhdAtom->dataSize() + 8;
436  // -> add 12 byte to size if update from version 0 to version 1 is required (which needs 12 byte more)
437  if ((info.version == 0)
438  && (static_cast<std::uint64_t>((m_creationTime - startDate).totalSeconds()) > numeric_limits<std::uint32_t>::max()
439  || static_cast<std::uint64_t>((m_modificationTime - startDate).totalSeconds()) > numeric_limits<std::uint32_t>::max()
440  || static_cast<std::uint64_t>(m_duration.totalSeconds() * m_timeScale) > numeric_limits<std::uint32_t>::max())) {
441  info.requiredSize += 12;
442  }
443  // -> add 8 byte to the size because it must be denoted using a 64-bit integer
444  if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
445  info.requiredSize += 8;
446  }
447  return info;
448 }
449 
457 vector<tuple<std::uint32_t, std::uint32_t, std::uint32_t>> Mp4Track::readSampleToChunkTable(Diagnostics &diag)
458 {
459  static const string context("reading sample to chunk table of MP4 track");
460  if (!isHeaderValid() || !m_istream || !m_stscAtom) {
461  diag.emplace_back(DiagLevel::Critical, "Track has not been parsed or is invalid.", context);
462  throw InvalidDataException();
463  }
464  // verify integrity of the sample to chunk table
465  std::uint64_t actualTableSize = m_stscAtom->dataSize();
466  if (actualTableSize < 20) {
467  diag.emplace_back(DiagLevel::Critical, "The stsc atom is truncated. There are no \"sample to chunk\" entries present.", context);
468  throw InvalidDataException();
469  } else {
470  actualTableSize -= 8;
471  }
472  std::uint64_t actualSampleToChunkEntryCount = sampleToChunkEntryCount();
473  std::uint64_t calculatedTableSize = actualSampleToChunkEntryCount * 12;
474  if (calculatedTableSize < actualTableSize) {
475  diag.emplace_back(DiagLevel::Critical, "The stsc atom stores more entries as denoted. The additional entries will be ignored.", context);
476  } else if (calculatedTableSize > actualTableSize) {
477  diag.emplace_back(DiagLevel::Critical, "The stsc atom is truncated. It stores less entries as denoted.", context);
478  actualSampleToChunkEntryCount = actualTableSize / 12;
479  }
480  // prepare reading
481  vector<tuple<std::uint32_t, std::uint32_t, std::uint32_t>> sampleToChunkTable;
482  sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
483  m_istream->seekg(static_cast<streamoff>(m_stscAtom->dataOffset() + 8));
484  for (std::uint32_t i = 0; i < actualSampleToChunkEntryCount; ++i) {
485  // read entry
486  std::uint32_t firstChunk = reader().readUInt32BE();
487  std::uint32_t samplesPerChunk = reader().readUInt32BE();
488  std::uint32_t sampleDescriptionIndex = reader().readUInt32BE();
489  sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
490  }
491  return sampleToChunkTable;
492 }
493 
506 vector<std::uint64_t> Mp4Track::readChunkSizes(Diagnostics &diag)
507 {
508  static const string context("reading chunk sizes of MP4 track");
509  if (!isHeaderValid() || !m_istream || !m_stcoAtom) {
510  diag.emplace_back(DiagLevel::Critical, "Track has not been parsed or is invalid.", context);
511  throw InvalidDataException();
512  }
513  // read sample to chunk table
514  const auto sampleToChunkTable = readSampleToChunkTable(diag);
515  // accumulate chunk sizes from the table
516  vector<std::uint64_t> chunkSizes;
517  if (!sampleToChunkTable.empty()) {
518  // prepare reading
519  auto tableIterator = sampleToChunkTable.cbegin();
520  chunkSizes.reserve(m_chunkCount);
521  // read first entry
522  size_t sampleIndex = 0;
523  std::uint32_t previousChunkIndex = get<0>(*tableIterator); // the first chunk has the index 1 and not zero!
524  if (previousChunkIndex != 1) {
525  diag.emplace_back(DiagLevel::Critical, "The first chunk of the first \"sample to chunk\" entry must be 1.", context);
526  previousChunkIndex = 1; // try to read the entry anyway
527  }
528  std::uint32_t samplesPerChunk = get<1>(*tableIterator);
529  // read the following entries
530  ++tableIterator;
531  for (const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
532  std::uint32_t firstChunkIndex = get<0>(*tableIterator);
533  if (firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
534  addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
535  } else {
536  diag.emplace_back(DiagLevel::Critical,
537  "The first chunk index of a \"sample to chunk\" entry must be greather than the first chunk of the previous entry and not "
538  "greather than the chunk count.",
539  context);
540  throw InvalidDataException();
541  }
542  previousChunkIndex = firstChunkIndex;
543  samplesPerChunk = get<1>(*tableIterator);
544  }
545  if (m_chunkCount >= previousChunkIndex) {
546  addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
547  }
548  }
549  return chunkSizes;
550 }
551 
556 std::unique_ptr<Mpeg4ElementaryStreamInfo> Mp4Track::parseMpeg4ElementaryStreamInfo(
557  CppUtilities::BinaryReader &reader, Mp4Atom *esDescAtom, Diagnostics &diag)
558 {
559  static const string context("parsing MPEG-4 elementary stream descriptor");
560  using namespace Mpeg4ElementaryStreamObjectIds;
561  unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
562  if (esDescAtom->dataSize() >= 12) {
563  reader.stream()->seekg(static_cast<streamoff>(esDescAtom->dataOffset()));
564  // read version/flags
565  if (reader.readUInt32BE() != 0) {
566  diag.emplace_back(DiagLevel::Warning, "Unknown version/flags.", context);
567  }
568  // read extended descriptor
569  Mpeg4Descriptor esDesc(esDescAtom->container(), static_cast<std::uint64_t>(reader.stream()->tellg()), esDescAtom->dataSize() - 4);
570  try {
571  esDesc.parse(diag);
572  // check ID
574  diag.emplace_back(DiagLevel::Critical, "Invalid descriptor found.", context);
575  throw Failure();
576  }
577  // read stream info
578  reader.stream()->seekg(static_cast<streamoff>(esDesc.dataOffset()));
579  esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
580  esInfo->id = reader.readUInt16BE();
581  esInfo->esDescFlags = reader.readByte();
582  if (esInfo->dependencyFlag()) {
583  esInfo->dependsOnId = reader.readUInt16BE();
584  }
585  if (esInfo->urlFlag()) {
586  esInfo->url = reader.readString(reader.readByte());
587  }
588  if (esInfo->ocrFlag()) {
589  esInfo->ocrId = reader.readUInt16BE();
590  }
591  for (Mpeg4Descriptor *esDescChild
592  = esDesc.denoteFirstChild(static_cast<std::uint32_t>(static_cast<std::uint64_t>(reader.stream()->tellg()) - esDesc.startOffset()));
593  esDescChild; esDescChild = esDescChild->nextSibling()) {
594  esDescChild->parse(diag);
595  switch (esDescChild->id()) {
597  // read decoder config descriptor
598  reader.stream()->seekg(static_cast<streamoff>(esDescChild->dataOffset()));
599  esInfo->objectTypeId = reader.readByte();
600  esInfo->decCfgDescFlags = reader.readByte();
601  esInfo->bufferSize = reader.readUInt24BE();
602  esInfo->maxBitrate = reader.readUInt32BE();
603  esInfo->averageBitrate = reader.readUInt32BE();
604  for (Mpeg4Descriptor *decCfgDescChild = esDescChild->denoteFirstChild(esDescChild->headerSize() + 13); decCfgDescChild;
605  decCfgDescChild = decCfgDescChild->nextSibling()) {
606  decCfgDescChild->parse(diag);
607  switch (decCfgDescChild->id()) {
609  // read decoder specific info
610  switch (esInfo->objectTypeId) {
611  case Aac:
612  case Mpeg2AacMainProfile:
615  case Mpeg2Audio:
616  case Mpeg1Audio:
617  esInfo->audioSpecificConfig
618  = parseAudioSpecificConfig(*reader.stream(), decCfgDescChild->dataOffset(), decCfgDescChild->dataSize(), diag);
619  break;
620  case Mpeg4Visual:
621  esInfo->videoSpecificConfig
622  = parseVideoSpecificConfig(reader, decCfgDescChild->dataOffset(), decCfgDescChild->dataSize(), diag);
623  break;
624  default:; // TODO: cover more object types
625  }
626  break;
627  }
628  }
629  break;
631  // uninteresting
632  break;
633  }
634  }
635  } catch (const Failure &) {
636  diag.emplace_back(DiagLevel::Critical, "The MPEG-4 descriptor element structure is invalid.", context);
637  }
638  } else {
639  diag.emplace_back(DiagLevel::Warning, "Elementary stream descriptor atom (esds) is truncated.", context);
640  }
641  return esInfo;
642 }
643 
648 unique_ptr<Mpeg4AudioSpecificConfig> Mp4Track::parseAudioSpecificConfig(
649  istream &stream, std::uint64_t startOffset, std::uint64_t size, Diagnostics &diag)
650 {
651  static const string context("parsing MPEG-4 audio specific config from elementary stream descriptor");
652  using namespace Mpeg4AudioObjectIds;
653  // read config into buffer and construct BitReader for bitwise reading
654  stream.seekg(static_cast<streamoff>(startOffset));
655  auto buff = make_unique<char[]>(size);
656  stream.read(buff.get(), static_cast<streamoff>(size));
657  BitReader bitReader(buff.get(), size);
658  auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
659  try {
660  // read audio object type
661  auto getAudioObjectType = [&bitReader] {
662  std::uint8_t objType = bitReader.readBits<std::uint8_t>(5);
663  if (objType == 31) {
664  objType = 32 + bitReader.readBits<std::uint8_t>(6);
665  }
666  return objType;
667  };
668  audioCfg->audioObjectType = getAudioObjectType();
669  // read sampling frequency
670  if ((audioCfg->sampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
671  audioCfg->sampleFrequency = bitReader.readBits<std::uint32_t>(24);
672  }
673  // read channel config
674  audioCfg->channelConfiguration = bitReader.readBits<std::uint8_t>(4);
675  // read extension header
676  switch (audioCfg->audioObjectType) {
677  case Sbr:
678  case Ps:
679  audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
680  audioCfg->sbrPresent = true;
681  if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
682  audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
683  }
684  if ((audioCfg->audioObjectType = getAudioObjectType()) == ErBsac) {
685  audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
686  }
687  break;
688  }
689  switch (audioCfg->extensionAudioObjectType) {
690  case Ps:
691  audioCfg->psPresent = true;
692  audioCfg->extensionChannelConfiguration = Mpeg4ChannelConfigs::FrontLeftFrontRight;
693  break;
694  }
695  // read GA specific config
696  switch (audioCfg->audioObjectType) {
697  case AacMain:
698  case AacLc:
699  case AacLtp:
700  case AacScalable:
701  case TwinVq:
702  case ErAacLc:
703  case ErAacLtp:
704  case ErAacScalable:
705  case ErTwinVq:
706  case ErBsac:
707  case ErAacLd:
708  audioCfg->frameLengthFlag = bitReader.readBits<std::uint8_t>(1);
709  if ((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
710  audioCfg->coreCoderDelay = bitReader.readBits<std::uint8_t>(14);
711  }
712  audioCfg->extensionFlag = bitReader.readBit();
713  if (audioCfg->channelConfiguration == 0) {
714  throw NotImplementedException(); // TODO: parse program_config_element
715  }
716  switch (audioCfg->audioObjectType) {
717  case AacScalable:
718  case ErAacScalable:
719  audioCfg->layerNr = bitReader.readBits<std::uint8_t>(3);
720  break;
721  default:;
722  }
723  if (audioCfg->extensionFlag == 1) {
724  switch (audioCfg->audioObjectType) {
725  case ErBsac:
726  audioCfg->numOfSubFrame = bitReader.readBits<std::uint8_t>(5);
727  audioCfg->layerLength = bitReader.readBits<std::uint16_t>(11);
728  break;
729  case ErAacLc:
730  case ErAacLtp:
731  case ErAacScalable:
732  case ErAacLd:
733  audioCfg->resilienceFlags = bitReader.readBits<std::uint8_t>(3);
734  break;
735  default:;
736  }
737  if (bitReader.readBit() == 1) { // extension flag 3
738  throw NotImplementedException(); // TODO
739  }
740  }
741  break;
742  default:
743  throw NotImplementedException(); // TODO: cover remaining object types
744  }
745  // read error specific config
746  switch (audioCfg->audioObjectType) {
747  case ErAacLc:
748  case ErAacLtp:
749  case ErAacScalable:
750  case ErTwinVq:
751  case ErBsac:
752  case ErAacLd:
753  case ErCelp:
754  case ErHvxc:
755  case ErHiln:
756  case ErParametric:
757  case ErAacEld:
758  switch (audioCfg->epConfig = bitReader.readBits<std::uint8_t>(2)) {
759  case 2:
760  break;
761  case 3:
762  bitReader.skipBits(1);
763  break;
764  default:
765  throw NotImplementedException(); // TODO
766  }
767  break;
768  }
769  if (audioCfg->extensionAudioObjectType != Sbr && audioCfg->extensionAudioObjectType != Ps && bitReader.bitsAvailable() >= 16) {
770  std::uint16_t syncExtensionType = bitReader.readBits<std::uint16_t>(11);
771  if (syncExtensionType == 0x2B7) {
772  if ((audioCfg->extensionAudioObjectType = getAudioObjectType()) == Sbr) {
773  if ((audioCfg->sbrPresent = bitReader.readBit())) {
774  if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
775  audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
776  }
777  if (bitReader.bitsAvailable() >= 12) {
778  if ((syncExtensionType = bitReader.readBits<std::uint16_t>(11)) == 0x548) {
779  audioCfg->psPresent = bitReader.readBits<std::uint8_t>(1);
780  }
781  }
782  }
783  } else if (audioCfg->extensionAudioObjectType == ErBsac) {
784  if ((audioCfg->sbrPresent = bitReader.readBit())) {
785  if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
786  audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
787  }
788  }
789  audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
790  }
791  } else if (syncExtensionType == 0x548) {
792  audioCfg->psPresent = bitReader.readBit();
793  }
794  }
795  } catch (const NotImplementedException &) {
796  diag.emplace_back(DiagLevel::Information, "Not implemented for the format of audio track.", context);
797  } catch (const std::ios_base::failure &) {
798  if (stream.fail()) {
799  // IO error caused by input stream
800  throw;
801  } else {
802  // IO error caused by bitReader
803  diag.emplace_back(DiagLevel::Critical, "Audio specific configuration is truncated.", context);
804  }
805  }
806  return audioCfg;
807 }
808 
813 std::unique_ptr<Mpeg4VideoSpecificConfig> Mp4Track::parseVideoSpecificConfig(
814  BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size, Diagnostics &diag)
815 {
816  static const string context("parsing MPEG-4 video specific config from elementary stream descriptor");
817  using namespace Mpeg4AudioObjectIds;
818  auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
819  // seek to start
820  reader.stream()->seekg(static_cast<streamoff>(startOffset));
821  if (size > 3 && (reader.readUInt24BE() == 1)) {
822  size -= 3;
823  std::uint32_t buff1;
824  while (size) {
825  --size;
826  switch (reader.readByte()) { // read start code
828  if (size) {
829  videoCfg->profile = reader.readByte();
830  --size;
831  }
832  break;
834 
835  break;
837  buff1 = 0;
838  while (size >= 3) {
839  if ((buff1 = reader.readUInt24BE()) != 1) {
840  reader.stream()->seekg(-2, ios_base::cur);
841  videoCfg->userData.push_back(static_cast<char>(buff1 >> 16));
842  --size;
843  } else {
844  size -= 3;
845  break;
846  }
847  }
848  if (buff1 != 1 && size > 0) {
849  videoCfg->userData += reader.readString(size);
850  size = 0;
851  }
852  break;
853  default:;
854  }
855  // skip remainging values to get the start of the next video object
856  while (size >= 3) {
857  if (reader.readUInt24BE() != 1) {
858  reader.stream()->seekg(-2, ios_base::cur);
859  --size;
860  } else {
861  size -= 3;
862  break;
863  }
864  }
865  }
866  } else {
867  diag.emplace_back(DiagLevel::Critical, "\"Visual Object Sequence Header\" not found.", context);
868  }
869  return videoCfg;
870 }
871 
889 void Mp4Track::updateChunkOffsets(const vector<std::int64_t> &oldMdatOffsets, const vector<std::int64_t> &newMdatOffsets)
890 {
891  if (!isHeaderValid() || !m_ostream || !m_istream || !m_stcoAtom) {
892  throw InvalidDataException();
893  }
894  if (oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
895  throw InvalidDataException();
896  }
897  static const unsigned int stcoDataBegin = 8;
898  std::uint64_t startPos = m_stcoAtom->dataOffset() + stcoDataBegin;
899  std::uint64_t endPos = startPos + m_stcoAtom->dataSize() - stcoDataBegin;
900  m_istream->seekg(static_cast<streamoff>(startPos));
901  m_ostream->seekp(static_cast<streamoff>(startPos));
902  vector<std::int64_t>::size_type i;
903  vector<std::int64_t>::size_type size;
904  auto currentPos = static_cast<std::uint64_t>(m_istream->tellg());
905  switch (m_stcoAtom->id()) {
907  std::uint32_t off;
908  while ((currentPos + 4) <= endPos) {
909  off = m_reader.readUInt32BE();
910  for (i = 0, size = oldMdatOffsets.size(); i < size; ++i) {
911  if (off > static_cast<std::uint64_t>(oldMdatOffsets[i])) {
912  off += (newMdatOffsets[i] - oldMdatOffsets[i]);
913  break;
914  }
915  }
916  m_ostream->seekp(static_cast<streamoff>(currentPos));
917  m_writer.writeUInt32BE(off);
918  currentPos += static_cast<std::uint64_t>(m_istream->gcount());
919  }
920  break;
921  }
923  std::uint64_t off;
924  while ((currentPos + 8) <= endPos) {
925  off = m_reader.readUInt64BE();
926  for (i = 0, size = oldMdatOffsets.size(); i < size; ++i) {
927  if (off > static_cast<std::uint64_t>(oldMdatOffsets[i])) {
928  off += (newMdatOffsets[i] - oldMdatOffsets[i]);
929  break;
930  }
931  }
932  m_ostream->seekp(static_cast<streamoff>(currentPos));
933  m_writer.writeUInt64BE(off);
934  currentPos += static_cast<std::uint64_t>(m_istream->gcount());
935  }
936  break;
937  }
938  default:
939  throw InvalidDataException();
940  }
941 }
942 
956 void Mp4Track::updateChunkOffsets(const std::vector<std::uint64_t> &chunkOffsets)
957 {
958  if (!isHeaderValid() || !m_ostream || !m_istream || !m_stcoAtom) {
959  throw InvalidDataException();
960  }
961  if (chunkOffsets.size() != chunkCount()) {
962  throw InvalidDataException();
963  }
964  m_ostream->seekp(static_cast<streamoff>(m_stcoAtom->dataOffset() + 8));
965  switch (m_stcoAtom->id()) {
967  for (auto offset : chunkOffsets) {
968  m_writer.writeUInt32BE(static_cast<std::uint32_t>(offset));
969  }
970  break;
972  for (auto offset : chunkOffsets) {
973  m_writer.writeUInt64BE(offset);
974  }
975  break;
976  default:
977  throw InvalidDataException();
978  }
979 }
980 
994 void Mp4Track::updateChunkOffset(std::uint32_t chunkIndex, std::uint64_t offset)
995 {
996  if (!isHeaderValid() || !m_istream || !m_stcoAtom || chunkIndex >= m_chunkCount) {
997  throw InvalidDataException();
998  }
999  m_ostream->seekp(static_cast<streamoff>(m_stcoAtom->dataOffset() + 8 + chunkOffsetSize() * chunkIndex));
1000  switch (chunkOffsetSize()) {
1001  case 4:
1002  writer().writeUInt32BE(static_cast<std::uint32_t>(offset));
1003  break;
1004  case 8:
1005  writer().writeUInt64BE(offset);
1006  break;
1007  default:
1008  throw InvalidDataException();
1009  }
1010 }
1011 
1015 void Mp4Track::addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track)
1016 {
1017  if (!avcConfig.spsInfos.empty()) {
1018  const SpsInfo &spsInfo = avcConfig.spsInfos.back();
1019  track.m_format.sub = spsInfo.profileIndication;
1020  track.m_version = static_cast<double>(spsInfo.levelIndication) / 10;
1021  track.m_cropping = spsInfo.cropping;
1022  track.m_pixelSize = spsInfo.pictureSize;
1023  switch (spsInfo.chromaFormatIndication) {
1024  case 0:
1025  track.m_chromaFormat = "monochrome";
1026  break;
1027  case 1:
1028  track.m_chromaFormat = "YUV 4:2:0";
1029  break;
1030  case 2:
1031  track.m_chromaFormat = "YUV 4:2:2";
1032  break;
1033  case 3:
1034  track.m_chromaFormat = "YUV 4:4:4";
1035  break;
1036  default:;
1037  }
1038  track.m_pixelAspectRatio = spsInfo.pixelAspectRatio;
1039  } else {
1040  track.m_format.sub = avcConfig.profileIndication;
1041  track.m_version = static_cast<double>(avcConfig.levelIndication) / 10;
1042  }
1043 }
1044 
1049 void Mp4Track::addInfo(const Av1Configuration &av1Config, AbstractTrack &track)
1050 {
1051  CPP_UTILITIES_UNUSED(av1Config)
1052  CPP_UTILITIES_UNUSED(track)
1053  throw NotImplementedException();
1054 }
1055 
1063 {
1064  CPP_UTILITIES_UNUSED(diag)
1065 
1066  if (m_tkhdAtom) {
1067  m_tkhdAtom->makeBuffer();
1068  }
1069  for (Mp4Atom *trakChild = m_trakAtom->firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1070  if (trakChild->id() == Mp4AtomIds::Media) {
1071  continue;
1072  }
1073  trakChild->makeBuffer();
1074  }
1075  if (m_minfAtom) {
1076  for (Mp4Atom *childAtom = m_minfAtom->firstChild(); childAtom; childAtom = childAtom->nextSibling()) {
1077  childAtom->makeBuffer();
1078  }
1079  }
1080 }
1081 
1085 std::uint64_t Mp4Track::requiredSize(Diagnostics &diag) const
1086 {
1087  CPP_UTILITIES_UNUSED(diag)
1088 
1089  // add size of
1090  // ... trak header
1091  std::uint64_t size = 8;
1092  // ... tkhd atom (TODO: buffer TrackHeaderInfo in next major release)
1093  size += verifyPresentTrackHeader().requiredSize;
1094  // ... children beside tkhd and mdia
1095  for (Mp4Atom *trakChild = m_trakAtom->firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1096  if (trakChild->id() == Mp4AtomIds::Media || trakChild->id() == Mp4AtomIds::TrackHeader) {
1097  continue;
1098  }
1099  size += trakChild->totalSize();
1100  }
1101  // ... mdhd total size
1102  if (static_cast<std::uint64_t>((m_creationTime - startDate).totalSeconds()) > numeric_limits<std::uint32_t>::max()
1103  || static_cast<std::uint64_t>((m_modificationTime - startDate).totalSeconds()) > numeric_limits<std::uint32_t>::max()
1104  || static_cast<std::uint64_t>(m_duration.totalSeconds() * m_timeScale) > numeric_limits<std::uint32_t>::max()) {
1105  // write version 1 where those fields are 64-bit
1106  size += 44;
1107  } else {
1108  // write version 0 where those fields are 32-bit
1109  size += 32;
1110  }
1111  // ... mdia header + hdlr total size + minf header
1112  size += 8 + (33 + m_name.size()) + 8;
1113  // ... minf children
1114  bool dinfAtomWritten = false;
1115  if (m_minfAtom) {
1116  for (Mp4Atom *childAtom = m_minfAtom->firstChild(); childAtom; childAtom = childAtom->nextSibling()) {
1117  if (childAtom->id() == Mp4AtomIds::DataInformation) {
1118  dinfAtomWritten = true;
1119  }
1120  size += childAtom->totalSize();
1121  }
1122  }
1123  if (!dinfAtomWritten) {
1124  // take 36 bytes for a self-made dinf atom into account if the file lacks one
1125  size += 36;
1126  }
1127  return size;
1128 }
1129 
1139 {
1140  // write header
1141  ostream::pos_type trakStartOffset = outputStream().tellp();
1142  m_writer.writeUInt32BE(0); // write size later
1143  m_writer.writeUInt32BE(Mp4AtomIds::Track);
1144 
1145  // write tkhd atom
1146  makeTrackHeader(diag);
1147 
1148  // write children of trak atom except mdia
1149  for (Mp4Atom *trakChild = trakAtom().firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1150  if (trakChild->id() == Mp4AtomIds::Media || trakChild->id() == Mp4AtomIds::TrackHeader) {
1151  continue;
1152  }
1153  trakChild->copyPreferablyFromBuffer(outputStream(), diag, nullptr);
1154  }
1155 
1156  // write mdia atom
1157  makeMedia(diag);
1158 
1159  // write size (of trak atom)
1160  Mp4Atom::seekBackAndWriteAtomSize(outputStream(), trakStartOffset, diag);
1161 }
1162 
1168 {
1169  // verify the existing track header to make the new one based on it (if possible)
1170  const TrackHeaderInfo info(verifyPresentTrackHeader());
1171 
1172  // add notifications in case the present track header could not be parsed
1173  if (info.versionUnknown) {
1174  diag.emplace_back(DiagLevel::Critical,
1175  argsToString("The version of the present \"tkhd\"-atom (", info.version, ") is unknown. Assuming version 1."),
1176  argsToString("making \"tkhd\"-atom of track ", m_id));
1177  }
1178  if (info.truncated) {
1179  diag.emplace_back(
1180  DiagLevel::Critical, argsToString("The present \"tkhd\"-atom is truncated."), argsToString("making \"tkhd\"-atom of track ", m_id));
1181  }
1182 
1183  // make size and element ID
1184  if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
1185  writer().writeUInt32BE(1);
1186  writer().writeUInt32BE(Mp4AtomIds::TrackHeader);
1187  writer().writeUInt64BE(info.requiredSize);
1188  } else {
1189  writer().writeUInt32BE(static_cast<std::uint32_t>(info.requiredSize));
1190  writer().writeUInt32BE(Mp4AtomIds::TrackHeader);
1191  }
1192 
1193  // determine time-related values and version
1194  const auto creationTime = static_cast<std::uint64_t>((m_creationTime - startDate).totalSeconds());
1195  const auto modificationTime = static_cast<std::uint64_t>((m_modificationTime - startDate).totalSeconds());
1196  const auto duration = static_cast<std::uint64_t>(m_duration.totalSeconds() * m_timeScale);
1197  const std::uint8_t version = (info.version == 0)
1198  && (creationTime > numeric_limits<std::uint32_t>::max() || modificationTime > numeric_limits<std::uint32_t>::max()
1199  || duration > numeric_limits<std::uint32_t>::max())
1200  ? 1
1201  : info.version;
1202 
1203  // make version and flags
1204  writer().writeByte(version);
1205  std::uint32_t flags = 0;
1206  if (m_enabled) {
1207  flags |= 0x000001;
1208  }
1209  if (m_usedInPresentation) {
1210  flags |= 0x000002;
1211  }
1212  if (m_usedWhenPreviewing) {
1213  flags |= 0x000004;
1214  }
1215  writer().writeUInt24BE(flags);
1216 
1217  // make creation and modification time
1218  if (version != 0) {
1219  writer().writeUInt64BE(creationTime);
1220  writer().writeUInt64BE(modificationTime);
1221  } else {
1222  writer().writeUInt32BE(static_cast<std::uint32_t>(creationTime));
1223  writer().writeUInt32BE(static_cast<std::uint32_t>(modificationTime));
1224  }
1225 
1226  // make track ID and duration
1227  writer().writeUInt32BE(static_cast<std::uint32_t>(m_id));
1228  writer().writeUInt32BE(0); // reserved
1229  if (version != 0) {
1230  writer().writeUInt64BE(duration);
1231  } else {
1232  writer().writeUInt32BE(static_cast<std::uint32_t>(duration));
1233  }
1234  writer().writeUInt32BE(0); // reserved
1235  writer().writeUInt32BE(0); // reserved
1236 
1237  // make further values, either from existing tkhd atom or just some defaults
1238  if (info.canUseExisting) {
1239  // write all bytes after the previously determined additionalDataOffset
1240  m_ostream->write(m_tkhdAtom->buffer().get() + m_tkhdAtom->headerSize() + info.additionalDataOffset,
1241  static_cast<streamoff>(m_tkhdAtom->dataSize() - info.additionalDataOffset));
1242  // discard the buffer again if it wasn't present before
1243  if (info.discardBuffer) {
1244  m_tkhdAtom->discardBuffer();
1245  }
1246  } else {
1247  // write default values
1248  diag.emplace_back(DiagLevel::Warning, "Writing some default values because the existing tkhd atom is truncated.", "making tkhd atom");
1249  writer().writeInt16BE(0); // layer
1250  writer().writeInt16BE(0); // alternate group
1251  writer().writeFixed8BE(1.0); // volume (fixed 8.8 - 2 byte)
1252  writer().writeUInt16BE(0); // reserved
1253  for (const std::int32_t value : { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }) { // unity matrix
1254  writer().writeInt32BE(value);
1255  }
1256  writer().writeFixed16BE(1.0); // width
1257  writer().writeFixed16BE(1.0); // height
1258  }
1259 }
1260 
1266 {
1267  ostream::pos_type mdiaStartOffset = outputStream().tellp();
1268  writer().writeUInt32BE(0); // write size later
1269  writer().writeUInt32BE(Mp4AtomIds::Media);
1270  // write mdhd atom
1271  const auto creationTime = static_cast<std::uint64_t>((m_creationTime - startDate).totalSeconds());
1272  const auto modificationTime = static_cast<std::uint64_t>((m_modificationTime - startDate).totalSeconds());
1273  const auto duration = static_cast<std::uint64_t>(m_duration.totalSeconds() * m_timeScale);
1274  const std::uint8_t version = (creationTime > numeric_limits<std::uint32_t>::max() || modificationTime > numeric_limits<std::uint32_t>::max()
1275  || duration > numeric_limits<std::uint32_t>::max())
1276  ? 1
1277  : 0;
1278  writer().writeUInt32BE(version != 0 ? 44 : 32); // size
1279  writer().writeUInt32BE(Mp4AtomIds::MediaHeader);
1280  writer().writeByte(version); // version
1281  writer().writeUInt24BE(0); // flags
1282  if (version != 0) {
1283  writer().writeUInt64BE(creationTime);
1284  writer().writeUInt64BE(modificationTime);
1285  } else {
1286  writer().writeUInt32BE(static_cast<std::uint32_t>(creationTime));
1287  writer().writeUInt32BE(static_cast<std::uint32_t>(modificationTime));
1288  }
1289  writer().writeUInt32BE(m_timeScale);
1290  if (version != 0) {
1291  writer().writeUInt64BE(duration);
1292  } else {
1293  writer().writeUInt32BE(static_cast<std::uint32_t>(duration));
1294  }
1295  // convert and write language
1296  std::uint16_t language = 0;
1297  for (size_t charIndex = 0; charIndex != 3; ++charIndex) {
1298  const char langChar = charIndex < m_language.size() ? m_language[charIndex] : 0;
1299  if (langChar >= 'a' && langChar <= 'z') {
1300  language |= static_cast<std::uint16_t>(langChar - 0x60) << (0xA - charIndex * 0x5);
1301  continue;
1302  }
1303 
1304  // handle invalid characters
1305  if (m_language.empty()) {
1306  // preserve empty language field
1307  language = 0;
1308  break;
1309  }
1310  diag.emplace_back(DiagLevel::Warning, "Assigned language \"" % m_language + "\" is of an invalid format. Setting language to undefined.",
1311  "making mdhd atom");
1312  language = 0x55C4; // und(efined)
1313  break;
1314  }
1315  if (m_language.size() > 3) {
1316  diag.emplace_back(
1317  DiagLevel::Warning, "Assigned language \"" % m_language + "\" is longer than 3 byte and hence will be truncated.", "making mdhd atom");
1318  }
1319  writer().writeUInt16BE(language);
1320  writer().writeUInt16BE(0); // pre defined
1321  // write hdlr atom
1322  writer().writeUInt32BE(33 + m_name.size()); // size
1323  writer().writeUInt32BE(Mp4AtomIds::HandlerReference);
1324  writer().writeUInt64BE(0); // version, flags, pre defined
1325  switch (m_mediaType) {
1326  case MediaType::Video:
1327  outputStream().write("vide", 4);
1328  break;
1329  case MediaType::Audio:
1330  outputStream().write("soun", 4);
1331  break;
1332  case MediaType::Hint:
1333  outputStream().write("hint", 4);
1334  break;
1335  case MediaType::Text:
1336  outputStream().write("text", 4);
1337  break;
1338  case MediaType::Meta:
1339  outputStream().write("meta", 4);
1340  break;
1341  default:
1342  diag.emplace_back(DiagLevel::Critical, "Media type is invalid; The media type video is assumed.", "making hdlr atom");
1343  outputStream().write("vide", 4);
1344  break;
1345  }
1346  for (int i = 0; i < 3; ++i)
1347  writer().writeUInt32BE(0); // reserved
1348  writer().writeTerminatedString(m_name);
1349  // write minf atom
1350  makeMediaInfo(diag);
1351  // write size (of mdia atom)
1352  Mp4Atom::seekBackAndWriteAtomSize(outputStream(), mdiaStartOffset, diag);
1353 }
1354 
1360 {
1361  ostream::pos_type minfStartOffset = outputStream().tellp();
1362  writer().writeUInt32BE(0); // write size later
1363  writer().writeUInt32BE(Mp4AtomIds::MediaInformation);
1364  bool dinfAtomWritten = false;
1365  if (m_minfAtom) {
1366  // copy existing atoms except sample table which is handled separately
1367  for (Mp4Atom *childAtom = m_minfAtom->firstChild(); childAtom; childAtom = childAtom->nextSibling()) {
1368  if (childAtom->id() == Mp4AtomIds::SampleTable) {
1369  continue;
1370  }
1371  if (childAtom->id() == Mp4AtomIds::DataInformation) {
1372  dinfAtomWritten = true;
1373  }
1374  childAtom->copyPreferablyFromBuffer(outputStream(), diag, nullptr);
1375  }
1376  }
1377  // write dinf atom if not written yet
1378  if (!dinfAtomWritten) {
1379  writer().writeUInt32BE(36); // size
1380  writer().writeUInt32BE(Mp4AtomIds::DataInformation);
1381  // write dref atom
1382  writer().writeUInt32BE(28); // size
1383  writer().writeUInt32BE(Mp4AtomIds::DataReference);
1384  writer().writeUInt32BE(0); // version and flags
1385  writer().writeUInt32BE(1); // entry count
1386  // write url atom
1387  writer().writeUInt32BE(12); // size
1388  writer().writeUInt32BE(Mp4AtomIds::DataEntryUrl);
1389  writer().writeByte(0); // version
1390  writer().writeUInt24BE(0x000001); // flags (media data is in the same file as the movie box)
1391  }
1392  // write stbl atom
1393  // -> just copy existing stbl atom because makeSampleTable() is not fully implemented (yet)
1394  bool stblAtomWritten = false;
1395  if (m_minfAtom) {
1396  if (Mp4Atom *const stblAtom = m_minfAtom->childById(Mp4AtomIds::SampleTable, diag)) {
1397  stblAtom->copyPreferablyFromBuffer(outputStream(), diag, nullptr);
1398  stblAtomWritten = true;
1399  }
1400  }
1401  if (!stblAtomWritten) {
1402  diag.emplace_back(DiagLevel::Critical,
1403  "Source track does not contain mandatory stbl atom and the tagparser lib is unable to make one from scratch.", "making stbl atom");
1404  }
1405  // write size (of minf atom)
1406  Mp4Atom::seekBackAndWriteAtomSize(outputStream(), minfStartOffset, diag);
1407 }
1408 
1415 {
1416  // ostream::pos_type stblStartOffset = outputStream().tellp(); (enable when function is fully implemented)
1417 
1418  writer().writeUInt32BE(0); // write size later
1419  writer().writeUInt32BE(Mp4AtomIds::SampleTable);
1420  Mp4Atom *const stblAtom = m_minfAtom ? m_minfAtom->childById(Mp4AtomIds::SampleTable, diag) : nullptr;
1421  // write stsd atom
1422  if (m_stsdAtom) {
1423  // copy existing stsd atom
1424  m_stsdAtom->copyEntirely(outputStream(), diag, nullptr);
1425  } else {
1426  diag.emplace_back(DiagLevel::Critical, "Unable to make stsd atom from scratch.", "making stsd atom");
1427  throw NotImplementedException();
1428  }
1429  // write stts and ctts atoms
1430  Mp4Atom *const sttsAtom = stblAtom ? stblAtom->childById(Mp4AtomIds::DecodingTimeToSample, diag) : nullptr;
1431  if (sttsAtom) {
1432  // copy existing stts atom
1433  sttsAtom->copyEntirely(outputStream(), diag, nullptr);
1434  } else {
1435  diag.emplace_back(DiagLevel::Critical, "Unable to make stts atom from scratch.", "making stts atom");
1436  throw NotImplementedException();
1437  }
1438  Mp4Atom *const cttsAtom = stblAtom ? stblAtom->childById(Mp4AtomIds::CompositionTimeToSample, diag) : nullptr;
1439  if (cttsAtom) {
1440  // copy existing ctts atom
1441  cttsAtom->copyEntirely(outputStream(), diag, nullptr);
1442  }
1443  // write stsc atom (sample-to-chunk table)
1444  throw NotImplementedException();
1445 
1446  // write stsz atom (sample sizes)
1447 
1448  // write stz2 atom (compact sample sizes)
1449 
1450  // write stco/co64 atom (chunk offset table)
1451 
1452  // write stss atom (sync sample table)
1453 
1454  // write stsh atom (shadow sync sample table)
1455 
1456  // write padb atom (sample padding bits)
1457 
1458  // write stdp atom (sample degradation priority)
1459 
1460  // write sdtp atom (independent and disposable samples)
1461 
1462  // write sbgp atom (sample group description)
1463 
1464  // write sbgp atom (sample-to-group)
1465 
1466  // write sgpd atom (sample group description)
1467 
1468  // write subs atom (sub-sample information)
1469 
1470  // write size of stbl atom (enable when function is fully implemented)
1471  // Mp4Atom::seekBackAndWriteAtomSize(outputStream(), stblStartOffset, diag);
1472 }
1473 
1475 {
1476  static const string context("parsing MP4 track");
1477  using namespace Mp4AtomIds;
1478  if (!m_trakAtom) {
1479  diag.emplace_back(DiagLevel::Critical, "\"trak\"-atom is null.", context);
1480  throw InvalidDataException();
1481  }
1482 
1483  // get atoms
1484  try {
1485  if (!(m_tkhdAtom = m_trakAtom->childById(TrackHeader, diag))) {
1486  diag.emplace_back(DiagLevel::Critical, "No \"tkhd\"-atom found.", context);
1487  throw InvalidDataException();
1488  }
1489  if (!(m_mdiaAtom = m_trakAtom->childById(Media, diag))) {
1490  diag.emplace_back(DiagLevel::Critical, "No \"mdia\"-atom found.", context);
1491  throw InvalidDataException();
1492  }
1493  if (!(m_mdhdAtom = m_mdiaAtom->childById(MediaHeader, diag))) {
1494  diag.emplace_back(DiagLevel::Critical, "No \"mdhd\"-atom found.", context);
1495  throw InvalidDataException();
1496  }
1497  if (!(m_hdlrAtom = m_mdiaAtom->childById(HandlerReference, diag))) {
1498  diag.emplace_back(DiagLevel::Critical, "No \"hdlr\"-atom found.", context);
1499  throw InvalidDataException();
1500  }
1501  if (!(m_minfAtom = m_mdiaAtom->childById(MediaInformation, diag))) {
1502  diag.emplace_back(DiagLevel::Critical, "No \"minf\"-atom found.", context);
1503  throw InvalidDataException();
1504  }
1505  if (!(m_stblAtom = m_minfAtom->childById(SampleTable, diag))) {
1506  diag.emplace_back(DiagLevel::Critical, "No \"stbl\"-atom found.", context);
1507  throw InvalidDataException();
1508  }
1509  if (!(m_stsdAtom = m_stblAtom->childById(SampleDescription, diag))) {
1510  diag.emplace_back(DiagLevel::Critical, "No \"stsd\"-atom found.", context);
1511  throw InvalidDataException();
1512  }
1513  if (!(m_stcoAtom = m_stblAtom->childById(ChunkOffset, diag)) && !(m_stcoAtom = m_stblAtom->childById(ChunkOffset64, diag))) {
1514  diag.emplace_back(DiagLevel::Critical, "No \"stco\"/\"co64\"-atom found.", context);
1515  throw InvalidDataException();
1516  }
1517  if (!(m_stscAtom = m_stblAtom->childById(SampleToChunk, diag))) {
1518  diag.emplace_back(DiagLevel::Critical, "No \"stsc\"-atom found.", context);
1519  throw InvalidDataException();
1520  }
1521  if (!(m_stszAtom = m_stblAtom->childById(SampleSize, diag)) && !(m_stszAtom = m_stblAtom->childById(CompactSampleSize, diag))) {
1522  diag.emplace_back(DiagLevel::Critical, "No \"stsz\"/\"stz2\"-atom found.", context);
1523  throw InvalidDataException();
1524  }
1525  } catch (const Failure &) {
1526  diag.emplace_back(DiagLevel::Critical, "Unable to parse relevant atoms.", context);
1527  throw InvalidDataException();
1528  }
1529 
1530  BinaryReader &reader = m_trakAtom->reader();
1531 
1532  // read tkhd atom
1533  m_istream->seekg(static_cast<streamoff>(m_tkhdAtom->startOffset() + 8)); // seek to beg, skip size and name
1534  auto atomVersion = reader.readByte(); // read version
1535  const auto flags = reader.readUInt24BE();
1536  m_enabled = flags & 0x000001;
1537  m_usedInPresentation = flags & 0x000002;
1538  m_usedWhenPreviewing = flags & 0x000004;
1539  switch (atomVersion) {
1540  case 0:
1541  m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
1542  m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
1543  m_id = reader.readUInt32BE();
1544  break;
1545  case 1:
1546  m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1547  m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1548  m_id = reader.readUInt32BE();
1549  break;
1550  default:
1551  diag.emplace_back(DiagLevel::Critical,
1552  "Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
1553  context);
1556  m_id = 0;
1557  }
1558 
1559  // read mdhd atom
1560  m_istream->seekg(static_cast<streamoff>(m_mdhdAtom->dataOffset())); // seek to beg, skip size and name
1561  atomVersion = reader.readByte(); // read version
1562  m_istream->seekg(3, ios_base::cur); // skip flags
1563  switch (atomVersion) {
1564  case 0:
1565  m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
1566  m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
1567  m_timeScale = reader.readUInt32BE();
1568  m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt32BE()) / static_cast<double>(m_timeScale));
1569  break;
1570  case 1:
1571  m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1572  m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1573  m_timeScale = reader.readUInt32BE();
1574  m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()) / static_cast<double>(m_timeScale));
1575  break;
1576  default:
1577  diag.emplace_back(DiagLevel::Warning,
1578  "Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
1579  "determined.",
1580  context);
1581  m_timeScale = 0;
1582  m_duration = TimeSpan();
1583  }
1584  std::uint16_t tmp = reader.readUInt16BE();
1585  if (tmp) {
1586  const char buff[] = {
1587  static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1588  static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1589  static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1590  };
1591  m_language = string(buff, 3);
1592  } else {
1593  m_language.clear();
1594  }
1595 
1596  // read hdlr atom
1597  // -> seek to begin skipping size, name, version, flags and reserved bytes
1598  m_istream->seekg(static_cast<streamoff>(m_hdlrAtom->dataOffset() + 8));
1599  // -> track type
1600  switch (reader.readUInt32BE()) {
1601  case 0x76696465:
1603  break;
1604  case 0x736F756E:
1606  break;
1607  case 0x68696E74:
1609  break;
1610  case 0x6D657461:
1612  break;
1613  case 0x74657874:
1614  m_mediaType = MediaType::Text;
1615  break;
1616  default:
1617  m_mediaType = MediaType::Unknown;
1618  }
1619  // FIXME: save raw media type in next major release so unknown ones can still be written correctly in Mp4Track::makeMedia()
1620  // -> name
1621  m_istream->seekg(12, ios_base::cur); // skip reserved bytes
1622  if (static_cast<std::uint64_t>(tmp = static_cast<std::uint8_t>(m_istream->peek())) == m_hdlrAtom->dataSize() - 12 - 4 - 8 - 1) {
1623  // assume size prefixed string (seems to appear in QuickTime files)
1624  m_istream->seekg(1, ios_base::cur);
1625  m_name = reader.readString(tmp);
1626  } else {
1627  // assume null terminated string (appears in MP4 files)
1628  m_name = reader.readTerminatedString(m_hdlrAtom->dataSize() - 12 - 4 - 8, 0);
1629  }
1630 
1631  // read stco atom (only chunk count)
1632  m_chunkOffsetSize = (m_stcoAtom->id() == Mp4AtomIds::ChunkOffset64) ? 8 : 4;
1633  m_istream->seekg(static_cast<streamoff>(m_stcoAtom->dataOffset() + 4));
1634  m_chunkCount = reader.readUInt32BE();
1635 
1636  // read stsd atom
1637  m_istream->seekg(static_cast<streamoff>(m_stsdAtom->dataOffset() + 4)); // seek to beg, skip size, name, version and flags
1638  const auto entryCount = reader.readUInt32BE();
1639  Mp4Atom *esDescParentAtom = nullptr;
1640  if (entryCount) {
1641  try {
1642  for (Mp4Atom *codecConfigContainerAtom = m_stsdAtom->firstChild(); codecConfigContainerAtom;
1643  codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
1644  codecConfigContainerAtom->parse(diag);
1645 
1646  // parse FOURCC
1647  m_formatId = interpretIntegerAsString<std::uint32_t>(codecConfigContainerAtom->id());
1648  m_format = FourccIds::fourccToMediaFormat(codecConfigContainerAtom->id());
1649 
1650  // parse codecConfigContainerAtom
1651  m_istream->seekg(static_cast<streamoff>(codecConfigContainerAtom->dataOffset()));
1652  switch (codecConfigContainerAtom->id()) {
1653  case FourccIds::Mpeg4Audio:
1655  case FourccIds::Amr:
1656  case FourccIds::Drms:
1657  case FourccIds::Alac:
1659  case FourccIds::Ac3:
1660  case FourccIds::EAc3:
1661  case FourccIds::DolbyMpl:
1662  case FourccIds::Dts:
1663  case FourccIds::DtsH:
1664  case FourccIds::DtsE:
1665  case FourccIds::Flac:
1666  case FourccIds::Opus:
1667  m_istream->seekg(6 + 2, ios_base::cur); // skip reserved bytes, data reference index
1668  tmp = reader.readUInt16BE(); // read sound version
1669  m_istream->seekg(6, ios_base::cur);
1670  m_channelCount = reader.readUInt16BE();
1671  m_bitsPerSample = reader.readUInt16BE();
1672  m_istream->seekg(4, ios_base::cur); // skip reserved bytes (again)
1673  if (!m_samplingFrequency) {
1674  m_samplingFrequency = reader.readUInt32BE() >> 16;
1675  if (codecConfigContainerAtom->id() != FourccIds::DolbyMpl) {
1676  m_samplingFrequency >>= 16;
1677  }
1678  } else {
1679  m_istream->seekg(4, ios_base::cur);
1680  }
1681  if (codecConfigContainerAtom->id() != FourccIds::WindowsMediaAudio) {
1682  switch (tmp) {
1683  case 1:
1684  codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1685  break;
1686  case 2:
1687  codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1688  break;
1689  default:
1690  codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1691  }
1692  if (!esDescParentAtom) {
1693  esDescParentAtom = codecConfigContainerAtom;
1694  }
1695  }
1696  break;
1697  case FourccIds::Mpeg4Video:
1699  case FourccIds::H2633GPP:
1700  case FourccIds::Avc1:
1701  case FourccIds::Avc2:
1702  case FourccIds::Avc3:
1703  case FourccIds::Avc4:
1704  case FourccIds::Drmi:
1705  case FourccIds::Hevc1:
1706  case FourccIds::Hevc2:
1707  case FourccIds::Av1_IVF:
1709  case FourccIds::Vp9_2:
1710  m_istream->seekg(6 + 2 + 16, ios_base::cur); // skip reserved bytes, data reference index, and reserved bytes (again)
1711  m_pixelSize.setWidth(reader.readUInt16BE());
1712  m_pixelSize.setHeight(reader.readUInt16BE());
1713  m_resolution.setWidth(static_cast<std::uint32_t>(reader.readFixed16BE()));
1714  m_resolution.setHeight(static_cast<std::uint32_t>(reader.readFixed16BE()));
1715  m_istream->seekg(4, ios_base::cur); // skip reserved bytes
1716  m_framesPerSample = reader.readUInt16BE();
1717  tmp = reader.readByte();
1718  m_compressorName = reader.readString(31);
1719  if (tmp == 0) {
1720  m_compressorName.clear();
1721  } else if (tmp < 32) {
1722  m_compressorName.resize(tmp);
1723  }
1724  m_depth = reader.readUInt16BE(); // 24: color without alpha
1725  codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1726  if (!esDescParentAtom) {
1727  esDescParentAtom = codecConfigContainerAtom;
1728  }
1729  break;
1731  // skip reserved bytes and data reference index
1732  codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1733  if (!esDescParentAtom) {
1734  esDescParentAtom = codecConfigContainerAtom;
1735  }
1736  break;
1738  break; // TODO
1740  break; // TODO
1741  default:;
1742  }
1743  }
1744 
1745  if (esDescParentAtom) {
1746  // parse AVC configuration
1747  if (auto *const avcConfigAtom = esDescParentAtom->childById(Mp4AtomIds::AvcConfiguration, diag)) {
1748  m_istream->seekg(static_cast<streamoff>(avcConfigAtom->dataOffset()));
1749  m_avcConfig = make_unique<TagParser::AvcConfiguration>();
1750  try {
1751  m_avcConfig->parse(reader, avcConfigAtom->dataSize(), diag);
1752  addInfo(*m_avcConfig, *this);
1753  } catch (const TruncatedDataException &) {
1754  diag.emplace_back(DiagLevel::Critical, "AVC configuration is truncated.", context);
1755  } catch (const Failure &) {
1756  diag.emplace_back(DiagLevel::Critical, "AVC configuration is invalid.", context);
1757  }
1758  }
1759 
1760  // parse AV1 configuration
1761  if (auto *const av1ConfigAtom = esDescParentAtom->childById(Mp4AtomIds::Av1Configuration, diag)) {
1762  m_istream->seekg(static_cast<streamoff>(av1ConfigAtom->dataOffset()));
1763  m_av1Config = make_unique<TagParser::Av1Configuration>();
1764  try {
1765  m_av1Config->parse(reader, av1ConfigAtom->dataSize(), diag);
1766  addInfo(*m_av1Config, *this);
1767  } catch (const NotImplementedException &) {
1768  diag.emplace_back(DiagLevel::Information, "Parsing AV1 configuration is not supported yet.", context);
1769  } catch (const TruncatedDataException &) {
1770  diag.emplace_back(DiagLevel::Critical, "AV1 configuration is truncated.", context);
1771  } catch (const Failure &) {
1772  diag.emplace_back(DiagLevel::Critical, "AV1 configuration is invalid.", context);
1773  }
1774  }
1775 
1776  // parse MPEG-4 elementary stream descriptor
1777  auto *esDescAtom = esDescParentAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor, diag);
1778  if (!esDescAtom) {
1779  esDescAtom = esDescParentAtom->childById(Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor2, diag);
1780  }
1781  if (esDescAtom) {
1782  try {
1783  if ((m_esInfo = parseMpeg4ElementaryStreamInfo(m_reader, esDescAtom, diag))) {
1785  m_bitrate = static_cast<double>(m_esInfo->averageBitrate) / 1000;
1786  m_maxBitrate = static_cast<double>(m_esInfo->maxBitrate) / 1000;
1787  if (m_esInfo->audioSpecificConfig) {
1788  // check the audio specific config for useful information
1789  m_format += Mpeg4AudioObjectIds::idToMediaFormat(m_esInfo->audioSpecificConfig->audioObjectType,
1790  m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
1791  if (m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1792  m_samplingFrequency = m_esInfo->audioSpecificConfig->sampleFrequency;
1793  } else if (m_esInfo->audioSpecificConfig->sampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
1794  m_samplingFrequency = mpeg4SamplingFrequencyTable[m_esInfo->audioSpecificConfig->sampleFrequencyIndex];
1795  } else {
1796  diag.emplace_back(DiagLevel::Warning, "Audio specific config has invalid sample frequency index.", context);
1797  }
1798  if (m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1799  m_extensionSamplingFrequency = m_esInfo->audioSpecificConfig->extensionSampleFrequency;
1800  } else if (m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex < sizeof(mpeg4SamplingFrequencyTable)) {
1802  = mpeg4SamplingFrequencyTable[m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex];
1803  } else {
1804  diag.emplace_back(
1805  DiagLevel::Warning, "Audio specific config has invalid extension sample frequency index.", context);
1806  }
1807  m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1808  m_extensionChannelConfig = m_esInfo->audioSpecificConfig->extensionChannelConfiguration;
1809  }
1810  if (m_esInfo->videoSpecificConfig) {
1811  // check the video specific config for useful information
1812  if (m_format.general == GeneralMediaFormat::Mpeg4Video && m_esInfo->videoSpecificConfig->profile) {
1813  m_format.sub = m_esInfo->videoSpecificConfig->profile;
1814  if (!m_esInfo->videoSpecificConfig->userData.empty()) {
1815  m_formatId += " / ";
1816  m_formatId += m_esInfo->videoSpecificConfig->userData;
1817  }
1818  }
1819  }
1820  // check the stream data for missing information
1821  switch (m_format.general) {
1824  MpegAudioFrame frame;
1825  m_istream->seekg(static_cast<streamoff>(m_stcoAtom->dataOffset() + 8));
1826  m_istream->seekg(static_cast<streamoff>(m_chunkOffsetSize == 8 ? reader.readUInt64BE() : reader.readUInt32BE()));
1827  frame.parseHeader(reader, diag);
1828  MpegAudioFrameStream::addInfo(frame, *this);
1829  break;
1830  }
1831  default:;
1832  }
1833  }
1834  } catch (const Failure &) {
1835  }
1836  }
1837  }
1838  } catch (const Failure &) {
1839  diag.emplace_back(DiagLevel::Critical, "Unable to parse child atoms of \"stsd\"-atom.", context);
1840  }
1841  }
1842 
1843  // read stsz atom which holds the sample size table
1844  m_sampleSizes.clear();
1845  m_size = m_sampleCount = 0;
1846  std::uint64_t actualSampleSizeTableSize = m_stszAtom->dataSize();
1847  if (actualSampleSizeTableSize < 12) {
1848  diag.emplace_back(DiagLevel::Critical,
1849  "The stsz atom is truncated. There are no sample sizes present. The size of the track can not be determined.", context);
1850  } else {
1851  actualSampleSizeTableSize -= 12; // subtract size of version and flags
1852  m_istream->seekg(static_cast<streamoff>(m_stszAtom->dataOffset() + 4)); // seek to beg, skip size, name, version and flags
1853  std::uint32_t fieldSize;
1854  std::uint32_t constantSize;
1855  if (m_stszAtom->id() == Mp4AtomIds::CompactSampleSize) {
1856  constantSize = 0;
1857  m_istream->seekg(3, ios_base::cur); // seek reserved bytes
1858  fieldSize = reader.readByte();
1859  m_sampleCount = reader.readUInt32BE();
1860  } else {
1861  constantSize = reader.readUInt32BE();
1862  m_sampleCount = reader.readUInt32BE();
1863  fieldSize = 32;
1864  }
1865  if (constantSize) {
1866  m_sampleSizes.push_back(constantSize);
1867  m_size = constantSize * m_sampleCount;
1868  } else {
1869  auto actualSampleCount = m_sampleCount;
1870  const auto calculatedSampleSizeTableSize = static_cast<std::uint64_t>(ceil((0.125 * fieldSize) * m_sampleCount));
1871  if (calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1872  diag.emplace_back(
1873  DiagLevel::Critical, "The stsz atom stores more entries as denoted. The additional entries will be ignored.", context);
1874  } else if (calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1875  diag.emplace_back(DiagLevel::Critical, "The stsz atom is truncated. It stores less entries as denoted.", context);
1876  actualSampleCount = static_cast<std::uint64_t>(floor(static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize)));
1877  }
1878  m_sampleSizes.reserve(actualSampleCount);
1879  std::uint32_t i = 1;
1880  switch (fieldSize) {
1881  case 4:
1882  for (; i <= actualSampleCount; i += 2) {
1883  std::uint8_t val = reader.readByte();
1884  m_sampleSizes.push_back(val >> 4);
1885  m_sampleSizes.push_back(val & 0xF0);
1886  m_size += (val >> 4) + (val & 0xF0);
1887  }
1888  if (i <= actualSampleCount + 1) {
1889  m_sampleSizes.push_back(reader.readByte() >> 4);
1890  m_size += m_sampleSizes.back();
1891  }
1892  break;
1893  case 8:
1894  for (; i <= actualSampleCount; ++i) {
1895  m_sampleSizes.push_back(reader.readByte());
1896  m_size += m_sampleSizes.back();
1897  }
1898  break;
1899  case 16:
1900  for (; i <= actualSampleCount; ++i) {
1901  m_sampleSizes.push_back(reader.readUInt16BE());
1902  m_size += m_sampleSizes.back();
1903  }
1904  break;
1905  case 32:
1906  for (; i <= actualSampleCount; ++i) {
1907  m_sampleSizes.push_back(reader.readUInt32BE());
1908  m_size += m_sampleSizes.back();
1909  }
1910  break;
1911  default:
1912  diag.emplace_back(DiagLevel::Critical,
1913  "The fieldsize used to store the sample sizes is not supported. The sample count and size of the track can not be determined.",
1914  context);
1915  }
1916  }
1917  }
1918 
1919  // no sample sizes found, search for trun atoms
1920  std::uint64_t totalDuration = 0;
1921  for (Mp4Atom *moofAtom = m_trakAtom->container().firstElement()->siblingByIdIncludingThis(MovieFragment, diag); moofAtom;
1922  moofAtom = moofAtom->siblingById(MovieFragment, diag)) {
1923  moofAtom->parse(diag);
1924  for (Mp4Atom *trafAtom = moofAtom->childById(TrackFragment, diag); trafAtom; trafAtom = trafAtom->siblingById(TrackFragment, diag)) {
1925  trafAtom->parse(diag);
1926  for (Mp4Atom *tfhdAtom = trafAtom->childById(TrackFragmentHeader, diag); tfhdAtom;
1927  tfhdAtom = tfhdAtom->siblingById(TrackFragmentHeader, diag)) {
1928  tfhdAtom->parse(diag);
1929  std::uint32_t calculatedDataSize = 0;
1930  if (tfhdAtom->dataSize() < calculatedDataSize) {
1931  diag.emplace_back(DiagLevel::Critical, "tfhd atom is truncated.", context);
1932  } else {
1933  m_istream->seekg(static_cast<streamoff>(tfhdAtom->dataOffset() + 1));
1934  std::uint32_t flags = reader.readUInt24BE();
1935  if (m_id == reader.readUInt32BE()) { // check track ID
1936  if (flags & 0x000001) { // base-data-offset present
1937  calculatedDataSize += 8;
1938  }
1939  if (flags & 0x000002) { // sample-description-index present
1940  calculatedDataSize += 4;
1941  }
1942  if (flags & 0x000008) { // default-sample-duration present
1943  calculatedDataSize += 4;
1944  }
1945  if (flags & 0x000010) { // default-sample-size present
1946  calculatedDataSize += 4;
1947  }
1948  if (flags & 0x000020) { // default-sample-flags present
1949  calculatedDataSize += 4;
1950  }
1951  //uint64 baseDataOffset = moofAtom->startOffset();
1952  //uint32 defaultSampleDescriptionIndex = 0;
1953  std::uint32_t defaultSampleDuration = 0;
1954  std::uint32_t defaultSampleSize = 0;
1955  //uint32 defaultSampleFlags = 0;
1956  if (tfhdAtom->dataSize() < calculatedDataSize) {
1957  diag.emplace_back(DiagLevel::Critical, "tfhd atom is truncated (presence of fields denoted).", context);
1958  } else {
1959  if (flags & 0x000001) { // base-data-offset present
1960  //baseDataOffset = reader.readUInt64();
1961  m_istream->seekg(8, ios_base::cur);
1962  }
1963  if (flags & 0x000002) { // sample-description-index present
1964  //defaultSampleDescriptionIndex = reader.readUInt32();
1965  m_istream->seekg(4, ios_base::cur);
1966  }
1967  if (flags & 0x000008) { // default-sample-duration present
1968  defaultSampleDuration = reader.readUInt32BE();
1969  //m_istream->seekg(4, ios_base::cur);
1970  }
1971  if (flags & 0x000010) { // default-sample-size present
1972  defaultSampleSize = reader.readUInt32BE();
1973  }
1974  if (flags & 0x000020) { // default-sample-flags present
1975  //defaultSampleFlags = reader.readUInt32BE();
1976  m_istream->seekg(4, ios_base::cur);
1977  }
1978  }
1979  for (Mp4Atom *trunAtom = trafAtom->childById(TrackFragmentRun, diag); trunAtom;
1980  trunAtom = trunAtom->siblingById(TrackFragmentRun, diag)) {
1981  std::uint32_t calculatedDataSize = 8;
1982  if (trunAtom->dataSize() < calculatedDataSize) {
1983  diag.emplace_back(DiagLevel::Critical, "trun atom is truncated.", context);
1984  } else {
1985  m_istream->seekg(static_cast<streamoff>(trunAtom->dataOffset() + 1));
1986  std::uint32_t flags = reader.readUInt24BE();
1987  std::uint32_t sampleCount = reader.readUInt32BE();
1989  if (flags & 0x000001) { // data offset present
1990  calculatedDataSize += 4;
1991  }
1992  if (flags & 0x000004) { // first-sample-flags present
1993  calculatedDataSize += 4;
1994  }
1995  std::uint32_t entrySize = 0;
1996  if (flags & 0x000100) { // sample-duration present
1997  entrySize += 4;
1998  }
1999  if (flags & 0x000200) { // sample-size present
2000  entrySize += 4;
2001  }
2002  if (flags & 0x000400) { // sample-flags present
2003  entrySize += 4;
2004  }
2005  if (flags & 0x000800) { // sample-composition-time-offsets present
2006  entrySize += 4;
2007  }
2008  calculatedDataSize += entrySize * sampleCount;
2009  if (trunAtom->dataSize() < calculatedDataSize) {
2010  diag.emplace_back(DiagLevel::Critical, "trun atom is truncated (presence of fields denoted).", context);
2011  } else {
2012  if (flags & 0x000001) { // data offset present
2013  m_istream->seekg(4, ios_base::cur);
2014  //int32 dataOffset = reader.readInt32();
2015  }
2016  if (flags & 0x000004) { // first-sample-flags present
2017  m_istream->seekg(4, ios_base::cur);
2018  }
2019  for (std::uint32_t i = 0; i < sampleCount; ++i) {
2020  if (flags & 0x000100) { // sample-duration present
2021  totalDuration += reader.readUInt32BE();
2022  } else {
2023  totalDuration += defaultSampleDuration;
2024  }
2025  if (flags & 0x000200) { // sample-size present
2026  m_sampleSizes.push_back(reader.readUInt32BE());
2027  m_size += m_sampleSizes.back();
2028  } else {
2029  m_size += defaultSampleSize;
2030  }
2031  if (flags & 0x000400) { // sample-flags present
2032  m_istream->seekg(4, ios_base::cur);
2033  }
2034  if (flags & 0x000800) { // sample-composition-time-offsets present
2035  m_istream->seekg(4, ios_base::cur);
2036  }
2037  }
2038  }
2039  }
2040  }
2041  if (m_sampleSizes.empty() && defaultSampleSize) {
2042  m_sampleSizes.push_back(defaultSampleSize);
2043  }
2044  }
2045  }
2046  }
2047  }
2048  }
2049 
2050  // set duration from "trun-information" if the duration has not been determined yet
2051  if (m_duration.isNull() && totalDuration) {
2052  std::uint32_t timeScale = m_timeScale;
2053  if (!timeScale) {
2054  timeScale = trakAtom().container().timeScale();
2055  }
2056  if (timeScale) {
2057  m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(timeScale));
2058  }
2059  }
2060 
2061  // caluculate average bitrate
2062  if (m_bitrate < 0.01 && m_bitrate > -0.01) {
2063  m_bitrate = (static_cast<double>(m_size) * 0.0078125) / m_duration.totalSeconds();
2064  }
2065 
2066  // read stsc atom (only number of entries)
2067  m_istream->seekg(static_cast<streamoff>(m_stscAtom->dataOffset() + 4));
2068  m_sampleToChunkEntryCount = reader.readUInt32BE();
2069 }
2070 
2071 } // namespace TagParser
TagParser::Mpeg4AudioObjectIds::idToMediaFormat
TAG_PARSER_EXPORT MediaFormat idToMediaFormat(std::uint8_t mpeg4AudioObjectId, bool sbrPresent=false, bool psPresent=false)
Definition: mp4ids.cpp:367
TagParser::Mp4AtomIds::TrackFragmentHeader
@ TrackFragmentHeader
Definition: mp4ids.h:69
TagParser::Mp4Track::updateChunkOffset
void updateChunkOffset(std::uint32_t chunkIndex, std::uint64_t offset)
Updates a particular chunk offset.
Definition: mp4track.cpp:994
TagParser::Mp4AtomIds::MediaHeader
@ MediaHeader
Definition: mp4ids.h:33
TagParser::AbstractTrack::m_samplingFrequency
std::uint32_t m_samplingFrequency
Definition: abstracttrack.h:140
mp4ids.h
TagParser::GenericFileElement::discardBuffer
void discardBuffer()
Discards buffered data.
Definition: genericfileelement.h:881
TagParser::Mpeg4ElementaryStreamObjectIds::Aac
@ Aac
Definition: mp4ids.h:455
TagParser::Mpeg4AudioObjectIds::ErTwinVq
@ ErTwinVq
Definition: mp4ids.h:582
TagParser::MpegAudioFrame::parseHeader
void parseHeader(CppUtilities::BinaryReader &reader, Diagnostics &diag)
Parses the header read using the specified reader.
Definition: mpegaudioframe.cpp:47
TagParser::AbstractTrack::m_enabled
bool m_enabled
Definition: abstracttrack.h:160
TagParser::AbstractTrack::m_bitrate
double m_bitrate
Definition: abstracttrack.h:135
mp4atom.h
TagParser::Mp4AtomIds::ChunkOffset64
@ ChunkOffset64
Definition: mp4ids.h:18
TagParser::VorbisCommentIds::version
constexpr TAG_PARSER_EXPORT const char * version()
Definition: vorbiscommentids.h:34
TagParser::Mpeg4AudioObjectIds::AacLtp
@ AacLtp
Definition: mp4ids.h:568
TagParser::Mpeg4AudioObjectIds::ErAacLc
@ ErAacLc
Definition: mp4ids.h:579
TagParser::Mpeg4ElementaryStreamObjectIds::streamObjectTypeFormat
TAG_PARSER_EXPORT MediaFormat streamObjectTypeFormat(std::uint8_t streamObjectTypeId)
Returns the TagParser::MediaFormat denoted by the specified MPEG-4 stream ID.
Definition: mp4ids.cpp:215
TagParser::AbstractTrack::m_resolution
Size m_resolution
Definition: abstracttrack.h:152
TagParser::Mp4AtomIds::SampleDescription
@ SampleDescription
Definition: mp4ids.h:62
TagParser::Mp4AtomIds::DataInformation
@ DataInformation
Definition: mp4ids.h:21
TagParser::Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor2
@ Mpeg4ElementaryStreamDescriptor2
Definition: mp4ids.h:432
TagParser::FourccIds::Drmi
@ Drmi
Definition: mp4ids.h:258
TagParser::Mp4Track::readChunkOffsets
std::vector< std::uint64_t > readChunkOffsets(bool parseFragments, Diagnostics &diag)
Reads the chunk offsets from the stco atom and fragments if parseFragments is true.
Definition: mp4track.cpp:172
TagParser::AbstractTrack::m_maxBitrate
double m_maxBitrate
Definition: abstracttrack.h:136
TagParser::AbstractTrack::m_language
std::string m_language
Definition: abstracttrack.h:139
TagParser::Mp4AtomIds::SampleSize
@ SampleSize
Definition: mp4ids.h:65
TagParser::Mpeg4AudioSpecificConfig::Mpeg4AudioSpecificConfig
Mpeg4AudioSpecificConfig()
Definition: mp4track.cpp:78
TagParser::Mp4AtomIds::ChunkOffset
@ ChunkOffset
Definition: mp4ids.h:59
TagParser::FourccIds::Avc4
@ Avc4
Definition: mp4ids.h:224
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg2AacScaleableSamplingRateProfile
@ Mpeg2AacScaleableSamplingRateProfile
Definition: mp4ids.h:464
mp4track.h
TagParser::Mpeg4AudioObjectIds::ErBsac
@ ErBsac
Definition: mp4ids.h:583
TagParser::SpsInfo::cropping
Margin cropping
Definition: avcinfo.h:89
TagParser::Mpeg4AudioObjectIds::ErAacLtp
@ ErAacLtp
Definition: mp4ids.h:580
TagParser::FourccIds::Hevc2
@ Hevc2
Definition: mp4ids.h:292
TagParser::AbstractTrack::m_version
double m_version
Definition: abstracttrack.h:129
TagParser::GenericFileElement::makeBuffer
void makeBuffer()
Buffers the element (header and data).
Definition: genericfileelement.h:871
TagParser::GenericFileElement::startOffset
std::uint64_t startOffset() const
Returns the start offset in the related stream.
Definition: genericfileelement.h:261
TagParser::SpsInfo::pictureSize
Size pictureSize
Definition: avcinfo.h:90
TagParser::Mpeg4AudioObjectIds::Sbr
@ Sbr
Definition: mp4ids.h:569
TagParser::MpegAudioFrame
The MpegAudioFrame class is used to parse MPEG audio frames.
Definition: mpegaudioframe.h:36
TagParser::Mp4AtomIds::Track
@ Track
Definition: mp4ids.h:72
TagParser::Mp4Track::makeMedia
void makeMedia(Diagnostics &diag)
Makes the media information (mdia atom) for the track.
Definition: mp4track.cpp:1265
TagParser::AbstractTrack::m_chromaFormat
const char * m_chromaFormat
Definition: abstracttrack.h:156
TagParser::TrackType::Mp4Track
@ Mp4Track
TagParser::AbstractTrack::m_timeScale
std::uint32_t m_timeScale
Definition: abstracttrack.h:159
TagParser::FourccIds::Dts
@ Dts
Definition: mp4ids.h:259
TagParser::AvcConfiguration::spsInfos
std::vector< SpsInfo > spsInfos
Definition: avcconfiguration.h:19
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
mpeg4descriptor.h
TagParser::AbstractTrack::modificationTime
const CppUtilities::DateTime & modificationTime() const
Returns the time of the last modification if known; otherwise returns a DateTime of zero ticks.
Definition: abstracttrack.h:407
TagParser::SpsInfo::pixelAspectRatio
AspectRatio pixelAspectRatio
Definition: avcinfo.h:87
TagParser::SpsInfo::profileIndication
std::uint8_t profileIndication
Definition: avcinfo.h:74
TagParser::AbstractTrack::Mp4Track
friend class Mp4Track
Definition: abstracttrack.h:42
TagParser::Mp4AtomIds::DataReference
@ DataReference
Definition: mp4ids.h:22
TagParser::Mp4AtomIds::Av1Configuration
@ Av1Configuration
Definition: mp4ids.h:14
TagParser::Mpeg4AudioObjectIds::AacMain
@ AacMain
Definition: mp4ids.h:565
TagParser::AbstractTrack::m_ostream
std::ostream * m_ostream
Definition: abstracttrack.h:120
TagParser::Mp4FormatExtensionIds::Mpeg4ElementaryStreamDescriptor
@ Mpeg4ElementaryStreamDescriptor
Definition: mp4ids.h:430
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::AbstractTrack::m_name
std::string m_name
Definition: abstracttrack.h:133
TagParser::AbstractTrack::m_formatId
std::string m_formatId
Definition: abstracttrack.h:126
TagParser::GenericFileElement::nextSibling
ImplementationType * nextSibling()
Returns the next sibling of the element.
Definition: genericfileelement.h:434
TagParser::Mp4Track::sampleToChunkEntryCount
std::uint32_t sampleToChunkEntryCount() const
Returns the number of "sample to chunk" entries within the stsc atom.
Definition: mp4track.h:236
TagParser::startDate
const DateTime startDate
Dates within MP4 tracks are expressed as the number of seconds since this date.
Definition: mp4track.cpp:70
TagParser::FourccIds::Mpeg4Audio
@ Mpeg4Audio
Definition: mp4ids.h:325
TagParser::Mp4AtomIds::DecodingTimeToSample
@ DecodingTimeToSample
Definition: mp4ids.h:66
TagParser::GenericFileElement::id
const IdentifierType & id() const
Returns the element ID.
Definition: genericfileelement.h:278
TagParser::Mpeg4DescriptorIds::DecoderSpecificInfo
@ DecoderSpecificInfo
Definition: mp4ids.h:521
TagParser::Mp4MediaTypeIds::Hint
@ Hint
Definition: mp4ids.h:139
TagParser::AbstractTrack::m_sampleCount
std::uint64_t m_sampleCount
Definition: abstracttrack.h:148
TagParser::FourccIds::Flac
@ Flac
Definition: mp4ids.h:272
TagParser::AbstractTrack::m_reader
CppUtilities::BinaryReader m_reader
Definition: abstracttrack.h:121
TagParser::Mp4AtomIds::MovieFragment
@ MovieFragment
Definition: mp4ids.h:44
TagParser::FourccIds::H2633GPP
@ H2633GPP
Definition: mp4ids.h:283
TagParser::GenericFileElement::parse
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
Definition: genericfileelement.h:771
TagParser::FourccIds::Opus
@ Opus
Definition: mp4ids.h:346
TagParser::mpeg4SamplingFrequencyTable
std::uint32_t mpeg4SamplingFrequencyTable[13]
Definition: mp4ids.cpp:423
TagParser::Mpeg4DescriptorIds::DecoderConfigDescr
@ DecoderConfigDescr
Definition: mp4ids.h:520
TagParser::FourccIds::DtsE
@ DtsE
Definition: mp4ids.h:261
TagParser::FourccIds::Ac3
@ Ac3
Definition: mp4ids.h:146
TagParser::SpsInfo::chromaFormatIndication
ugolomb chromaFormatIndication
Definition: avcinfo.h:77
TagParser::Mp4Track::updateChunkOffsets
void updateChunkOffsets(const std::vector< std::int64_t > &oldMdatOffsets, const std::vector< std::int64_t > &newMdatOffsets)
Updates the chunk offsets of the track.
Definition: mp4track.cpp:889
TagParser::AbstractTrack::outputStream
std::ostream & outputStream()
Returns the associated output stream.
Definition: abstracttrack.h:196
TagParser::Mp4Track::parseVideoSpecificConfig
static std::unique_ptr< Mpeg4VideoSpecificConfig > parseVideoSpecificConfig(CppUtilities::BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size, Diagnostics &diag)
Parses the video specific configuration for the track.
Definition: mp4track.cpp:813
TagParser::Mpeg4Descriptor
The Mpeg4Descriptor class helps to parse MPEG-4 descriptors.
Definition: mpeg4descriptor.h:31
TagParser::GenericFileElement::dataSize
DataSizeType dataSize() const
Returns the data size of the element in byte.
Definition: genericfileelement.h:315
TagParser::Mp4Track::chunkOffsetSize
unsigned int chunkOffsetSize() const
Returns the size of a single chunk offset denotation within the stco atom.
Definition: mp4track.h:220
TagParser::Mp4Track::makeTrackHeader
void makeTrackHeader(Diagnostics &diag)
Makes the track header (tkhd atom) for the track.
Definition: mp4track.cpp:1167
TagParser::Size::setWidth
void setWidth(std::uint32_t value)
Sets the width.
Definition: size.h:75
TagParser::AbstractTrack::m_depth
std::uint16_t m_depth
Definition: abstracttrack.h:154
TagParser::GenericFileElement::firstChild
ImplementationType * firstChild()
Returns the first child of the element.
Definition: genericfileelement.h:460
TagParser::FourccIds::Mpeg4Video
@ Mpeg4Video
Definition: mp4ids.h:331
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg1Audio
@ Mpeg1Audio
Definition: mp4ids.h:467
TagParser::Mp4AtomIds::PixalAspectRatio
@ PixalAspectRatio
Definition: mp4ids.h:51
TagParser::FourccIds::Avc2
@ Avc2
Definition: mp4ids.h:222
TagParser::SpsInfo::levelIndication
std::uint8_t levelIndication
Definition: avcinfo.h:76
TagParser::Mpeg4AudioObjectIds::ErParametric
@ ErParametric
Definition: mp4ids.h:588
TagParser::FourccIds::DtsH
@ DtsH
Definition: mp4ids.h:260
TagParser::AbstractTrack::size
std::uint64_t size() const
Returns the size in bytes if known; otherwise returns 0.
Definition: abstracttrack.h:316
TagParser::FourccIds::Hevc1
@ Hevc1
Definition: mp4ids.h:291
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
TagParser::AbstractTrack::m_duration
CppUtilities::TimeSpan m_duration
Definition: abstracttrack.h:134
TagParser::Mp4AtomIds::AvcConfiguration
@ AvcConfiguration
Definition: mp4ids.h:15
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg2Audio
@ Mpeg2Audio
Definition: mp4ids.h:465
TagParser::FourccIds::WindowsMediaAudio
@ WindowsMediaAudio
Definition: mp4ids.h:391
TagParser::TrackType
TrackType
Specifies the track type.
Definition: abstracttrack.h:27
TagParser::FourccIds::Drms
@ Drms
Definition: mp4ids.h:257
TagParser::AbstractTrack::duration
const CppUtilities::TimeSpan & duration() const
Returns the duration if known; otherwise returns a TimeSpan of zero ticks.
Definition: abstracttrack.h:375
TagParser::Mpeg4AudioObjectIds::TwinVq
@ TwinVq
Definition: mp4ids.h:571
TagParser::AbstractTrack::m_usedWhenPreviewing
bool m_usedWhenPreviewing
Definition: abstracttrack.h:166
TagParser::AbstractTrack::m_extensionSamplingFrequency
std::uint32_t m_extensionSamplingFrequency
Definition: abstracttrack.h:141
TagParser::AbstractTrack::inputStream
std::istream & inputStream()
Returns the associated input stream.
Definition: abstracttrack.h:177
TagParser::AbstractTrack::m_id
std::uint64_t m_id
Definition: abstracttrack.h:132
TagParser::GenericFileElement::buffer
const std::unique_ptr< char[]> & buffer()
Returns buffered data.
Definition: genericfileelement.h:910
TagParser::Mp4AtomIds::SampleTable
@ SampleTable
Definition: mp4ids.h:58
TagParser::Mp4AtomIds::HandlerReference
@ HandlerReference
Definition: mp4ids.h:29
TagParser::Mp4Track::addInfo
static void addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track)
Adds the information from the specified avcConfig to the specified track.
Definition: mp4track.cpp:1015
TagParser::AbstractTrack::m_size
std::uint64_t m_size
Definition: abstracttrack.h:130
TagParser::AvcConfiguration::levelIndication
std::uint8_t levelIndication
Definition: avcconfiguration.h:17
TagParser::GenericFileElement::denoteFirstChild
ImplementationType * denoteFirstChild(std::uint32_t offset)
Denotes the first child to start at the specified offset (relative to the start offset of this descri...
Definition: genericfileelement.h:948
TagParser::MpegAudioFrameStream::addInfo
static void addInfo(const MpegAudioFrame &frame, AbstractTrack &track)
Adds the information from the specified frame to the specified track.
Definition: mpegaudioframestream.cpp:21
CppUtilities
Definition: abstractcontainer.h:15
TagParser::Mpeg4AudioObjectIds::Ps
@ Ps
Definition: mp4ids.h:590
TagParser::AbstractTrack::sampleCount
std::uint64_t sampleCount() const
Returns the number of samples/frames if known; otherwise returns 0.
Definition: abstracttrack.h:480
TagParser::GenericFileElement::headerSize
std::uint32_t headerSize() const
Returns the header size of the element in byte.
Definition: genericfileelement.h:304
TagParser::AbstractTrack::version
double version() const
Returns the version/level of the track if known; otherwise returns 0.
Definition: abstracttrack.h:261
TagParser::Mp4AtomIds::Media
@ Media
Definition: mp4ids.h:34
TagParser::Mp4Track
Implementation of TagParser::AbstractTrack for the MP4 container.
Definition: mp4track.h:118
TagParser::Mp4Track::type
TrackType type() const override
Returns the type of the track if known; otherwise returns TrackType::Unspecified.
Definition: mp4track.cpp:157
TagParser::Mp4Track::bufferTrackAtoms
void bufferTrackAtoms(Diagnostics &diag)
Buffers all atoms required by the makeTrack() method.
Definition: mp4track.cpp:1062
TagParser::AbstractTrack
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:39
TagParser::SpsInfo
The SpsInfo struct holds the sequence parameter set.
Definition: avcinfo.h:71
TagParser::GenericFileElement::childById
ImplementationType * childById(const IdentifierType &id, Diagnostics &diag)
Returns the first child with the specified id.
Definition: genericfileelement.h:602
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg2AacLowComplexityProfile
@ Mpeg2AacLowComplexityProfile
Definition: mp4ids.h:463
TagParser::AbstractTrack::m_channelCount
std::uint16_t m_channelCount
Definition: abstracttrack.h:144
TagParser::AbstractTrack::m_pixelSize
Size m_pixelSize
Definition: abstracttrack.h:150
TagParser::AbstractTrack::m_modificationTime
CppUtilities::DateTime m_modificationTime
Definition: abstracttrack.h:138
TagParser::Mpeg4AudioObjectIds::ErCelp
@ ErCelp
Definition: mp4ids.h:585
TagParser::AbstractTrack::m_mediaType
MediaType m_mediaType
Definition: abstracttrack.h:128
TagParser::Mp4AtomIds::TrackHeader
@ TrackHeader
Definition: mp4ids.h:70
TagParser::AvcConfiguration::profileIndication
std::uint8_t profileIndication
Definition: avcconfiguration.h:15
TagParser::MediaFormat::general
GeneralMediaFormat general
Definition: mediaformat.h:258
TagParser::AbstractTrack::m_writer
CppUtilities::BinaryWriter m_writer
Definition: abstracttrack.h:122
TagParser::Av1Configuration
The Av1Configuration struct provides a parser for AV1 configuration found in ISOBMFF files.
Definition: av1configuration.h:17
TagParser::FourccIds::Av1_ISOBMFF
@ Av1_ISOBMFF
Definition: mp4ids.h:226
TagParser::Mp4Track::parseMpeg4ElementaryStreamInfo
static std::unique_ptr< Mpeg4ElementaryStreamInfo > parseMpeg4ElementaryStreamInfo(CppUtilities::BinaryReader &reader, Mp4Atom *esDescAtom, Diagnostics &diag)
Reads the MPEG-4 elementary stream descriptor for the track.
Definition: mp4track.cpp:556
TagParser::FourccIds::Avc3
@ Avc3
Definition: mp4ids.h:223
TagParser::Mp4AtomIds::CompositionTimeToSample
@ CompositionTimeToSample
Definition: mp4ids.h:19
TagParser::FourccIds::Mpeg4Sample
@ Mpeg4Sample
Definition: mp4ids.h:329
TagParser::Mpeg4VideoCodes::VisualObjectSequenceStart
@ VisualObjectSequenceStart
Definition: mp4ids.h:635
TagParser::Mp4AtomIds::SampleToChunk
@ SampleToChunk
Definition: mp4ids.h:61
TagParser::FourccIds::AmrNarrowband
@ AmrNarrowband
Definition: mp4ids.h:156
TagParser::Mpeg4AudioObjectIds::AacScalable
@ AacScalable
Definition: mp4ids.h:570
TagParser::Mp4Track::~Mp4Track
~Mp4Track() override
Destroys the track.
Definition: mp4track.cpp:153
TagParser::Mpeg4AudioObjectIds::ErHvxc
@ ErHvxc
Definition: mp4ids.h:586
TagParser::Mp4Track::readSampleToChunkTable
std::vector< std::tuple< std::uint32_t, std::uint32_t, std::uint32_t > > readSampleToChunkTable(Diagnostics &diag)
Reads the sample to chunk table.
Definition: mp4track.cpp:457
TagParser::AbstractTrack::m_bitsPerSample
std::uint16_t m_bitsPerSample
Definition: abstracttrack.h:142
TagParser::Mp4AtomIds::CompactSampleSize
@ CompactSampleSize
Definition: mp4ids.h:67
TagParser::Mp4Track::makeTrack
void makeTrack(Diagnostics &diag)
Makes the track entry ("trak"-atom) for the track.
Definition: mp4track.cpp:1138
TagParser::Mpeg4AudioObjectIds::ErHiln
@ ErHiln
Definition: mp4ids.h:587
TagParser::Mp4AtomIds::CleanAperature
@ CleanAperature
Definition: mp4ids.h:17
TagParser::Mp4AtomIds::TrackFragmentRun
@ TrackFragmentRun
Definition: mp4ids.h:75
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg4Visual
@ Mpeg4Visual
Definition: mp4ids.h:450
TagParser::AbstractTrack::creationTime
const CppUtilities::DateTime & creationTime() const
Returns the creation time if known; otherwise returns a DateTime of zero ticks.
Definition: abstracttrack.h:399
TagParser::Size::setHeight
void setHeight(std::uint32_t value)
Sets the height.
Definition: size.h:83
TagParser::Mpeg4AudioObjectIds::ErAacEld
@ ErAacEld
Definition: mp4ids.h:599
TagParser::AbstractTrack::isHeaderValid
bool isHeaderValid() const
Returns an indication whether the track header is valid.
Definition: abstracttrack.h:684
TagParser::InvalidDataException
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:25
TagParser::RawDataType::DateTime
@ DateTime
Definition: mp4tagfield.h:36
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::Mpeg4AudioObjectIds::AacLc
@ AacLc
Definition: mp4ids.h:566
TagParser::AbstractTrack::m_compressorName
std::string m_compressorName
Definition: abstracttrack.h:153
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::Mpeg4DescriptorIds::SlConfigDescr
@ SlConfigDescr
Definition: mp4ids.h:522
TagParser::Mp4AtomIds::TrackFragment
@ TrackFragment
Definition: mp4ids.h:71
TagParser::AbstractTrack::m_format
MediaFormat m_format
Definition: abstracttrack.h:125
TagParser::FourccIds::Alac
@ Alac
Definition: mp4ids.h:150
TagParser::TrackHeaderInfo
The TrackHeaderInfo struct holds information about the present track header (tkhd atom) and informati...
Definition: mp4track.cpp:36
TagParser::Mp4Track::chunkCount
std::uint32_t chunkCount() const
Returns the number of chunks denoted by the stco atom.
Definition: mp4track.h:228
TagParser::AvcConfiguration
The AvcConfiguration struct provides a parser for AVC configuration.
Definition: avcconfiguration.h:13
TagParser::AbstractTrack::language
const std::string & language() const
Returns the language of the track if known; otherwise returns an empty string.
Definition: abstracttrack.h:417
TagParser::MediaType::Unknown
@ Unknown
TagParser::FourccIds::EAc3
@ EAc3
Definition: mp4ids.h:266
TagParser::Mp4Track::makeMediaInfo
void makeMediaInfo(Diagnostics &diag)
Makes a media information (minf atom) for the track.
Definition: mp4track.cpp:1359
TagParser::Mp4AtomIds::MediaInformation
@ MediaInformation
Definition: mp4ids.h:40
TagParser::AbstractTrack::reader
CppUtilities::BinaryReader & reader()
Returns a binary reader for the associated stream.
Definition: abstracttrack.h:218
TagParser::Mpeg4DescriptorIds::ElementaryStreamDescr
@ ElementaryStreamDescr
Definition: mp4ids.h:519
TagParser::Mpeg4VideoCodes::VideoObjectLayerStart
@ VideoObjectLayerStart
Definition: mp4ids.h:634
TagParser::AbstractTrack::startOffset
std::uint64_t startOffset() const
Returns the start offset of the track in the associated stream.
Definition: abstracttrack.h:245
TagParser::Mpeg4VideoSpecificConfig::Mpeg4VideoSpecificConfig
Mpeg4VideoSpecificConfig()
Definition: mp4track.cpp:109
TagParser::MatroskaTrackType::Video
@ Video
Definition: matroskaid.h:403
TagParser::AbstractTrack::m_channelConfig
std::uint8_t m_channelConfig
Definition: abstracttrack.h:145
TagParser::Mp4Track::parseAudioSpecificConfig
static std::unique_ptr< Mpeg4AudioSpecificConfig > parseAudioSpecificConfig(std::istream &stream, std::uint64_t startOffset, std::uint64_t size, Diagnostics &diag)
Parses the audio specific configuration for the track.
Definition: mp4track.cpp:648
TagParser::Mp4Track::readChunkSizes
std::vector< std::uint64_t > readChunkSizes(TagParser::Diagnostics &diag)
Reads the chunk sizes from the stsz (sample sizes) and stsc (samples per chunk) atom.
Definition: mp4track.cpp:506
TagParser::AbstractTrack::m_pixelAspectRatio
AspectRatio m_pixelAspectRatio
Definition: abstracttrack.h:157
TagParser::GenericFileElement::container
ContainerType & container()
Returns the related container.
Definition: genericfileelement.h:220
TagParser::GenericFileElement::dataOffset
std::uint64_t dataOffset() const
Returns the data offset of the element in the related stream.
Definition: genericfileelement.h:333
TagParser::FourccIds::H263Quicktime
@ H263Quicktime
Definition: mp4ids.h:282
TagParser::Mpeg4ElementaryStreamObjectIds::Mpeg2AacMainProfile
@ Mpeg2AacMainProfile
Definition: mp4ids.h:462
TagParser::FourccIds::fourccToMediaFormat
TAG_PARSER_EXPORT MediaFormat fourccToMediaFormat(std::uint32_t fourccId)
Definition: mp4ids.cpp:47
TagParser::AbstractTrack::m_usedInPresentation
bool m_usedInPresentation
Definition: abstracttrack.h:165
mp4container.h
TagParser::Mp4Atom
The Mp4Atom class helps to parse MP4 files.
Definition: mp4atom.h:38
TagParser::FourccIds::Av1_IVF
@ Av1_IVF
Definition: mp4ids.h:225
TagParser::FourccIds::Vp9_2
@ Vp9_2
Definition: mp4ids.h:389
TagParser::AbstractTrack::timeScale
std::uint32_t timeScale() const
Returns the time scale if known; otherwise returns 0.
Definition: abstracttrack.h:593
TagParser::Mpeg4VideoCodes::UserDataStart
@ UserDataStart
Definition: mp4ids.h:637
TagParser::Mp4Track::trakAtom
Mp4Atom & trakAtom()
Returns the trak atom for the current instance.
Definition: mp4track.h:198
TagParser::FourccIds::Avc1
@ Avc1
Definition: mp4ids.h:221
TagParser::AbstractTrack::m_creationTime
CppUtilities::DateTime m_creationTime
Definition: abstracttrack.h:137
TagParser::FourccIds::DolbyMpl
@ DolbyMpl
Definition: mp4ids.h:317
TagParser::Mpeg4ChannelConfigs::FrontLeftFrontRight
@ FrontLeftFrontRight
Definition: mp4ids.h:618
TagParser::Mp4AtomIds::DataEntryUrl
@ DataEntryUrl
Definition: mp4ids.h:77
TagParser::MediaFormat::sub
unsigned char sub
Definition: mediaformat.h:259
TagParser::GenericFileElement::copyEntirely
void copyEntirely(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress)
Writes the entire element including all children to the specified targetStream.
Definition: genericfileelement.h:862
TagParser::AbstractTrack::m_cropping
Margin m_cropping
Definition: abstracttrack.h:168
TagParser::Mpeg4AudioObjectIds::ErAacScalable
@ ErAacScalable
Definition: mp4ids.h:581
TagParser::Mpeg4AudioObjectIds::ErAacLd
@ ErAacLd
Definition: mp4ids.h:584
TagParser::FourccIds::Amr
@ Amr
Definition: mp4ids.h:155
TagParser::AbstractTrack::m_istream
std::istream * m_istream
Definition: abstracttrack.h:119
TagParser::Mp4Track::requiredSize
std::uint64_t requiredSize(Diagnostics &diag) const
Returns the number of bytes written when calling makeTrack().
Definition: mp4track.cpp:1085
TagParser::Mp4Track::makeSampleTable
void makeSampleTable(Diagnostics &diag)
Makes the sample table (stbl atom) for the track.
Definition: mp4track.cpp:1414
TagParser::MatroskaTrackType::Audio
@ Audio
Definition: matroskaid.h:403
TagParser::AbstractTrack::writer
CppUtilities::BinaryWriter & writer()
Returns a binary writer for the associated stream.
Definition: abstracttrack.h:229
TagParser::AbstractTrack::m_extensionChannelConfig
std::uint8_t m_extensionChannelConfig
Definition: abstracttrack.h:146
TagParser::NotImplementedException
This exception is thrown when the an operation is invoked that has not been implemented yet.
Definition: exceptions.h:60
TagParser::Mp4Track::internalParseHeader
void internalParseHeader(Diagnostics &diag) override
This method is internally called to parse header information.
Definition: mp4track.cpp:1474