1#define CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS
9#include "../av1/av1configuration.h"
11#include "../avc/avcconfiguration.h"
13#include "../mpegaudio/mpegaudioframe.h"
14#include "../mpegaudio/mpegaudioframestream.h"
16#include "../exceptions.h"
17#include "../mediafileinfo.h"
18#include "../mediaformat.h"
20#include <c++utilities/conversion/stringbuilder.h>
21#include <c++utilities/io/binaryreader.h>
22#include <c++utilities/io/binarywriter.h>
23#include <c++utilities/io/bitreader.h>
55 std::uint64_t requiredSize = 100;
57 bool canUseExisting =
false;
59 bool truncated =
false;
61 std::uint8_t version = 0;
63 std::uint8_t writeVersion = 0;
65 bool versionUnknown =
false;
69 std::uint8_t timingsVersion = 0;
71 std::uint8_t additionalDataOffset = 0;
73 bool discardBuffer =
false;
79 ||
tkhdDuration > std::numeric_limits<std::uint32_t>::max())
87 ||
mdhdDuration > std::numeric_limits<std::uint32_t>::max())
100 , sampleFrequencyIndex(0xF)
102 , channelConfiguration(0)
103 , extensionAudioObjectType(0)
106 , extensionSampleFrequencyIndex(0xF)
107 , extensionSampleFrequency(0)
108 , extensionChannelConfiguration(0)
109 , frameLengthFlag(false)
110 , dependsOnCoreCoder(false)
152 , m_trakAtom(&trakAtom)
153 , m_tkhdAtom(nullptr)
154 , m_mdiaAtom(nullptr)
155 , m_mdhdAtom(nullptr)
156 , m_hdlrAtom(nullptr)
157 , m_minfAtom(nullptr)
158 , m_stblAtom(nullptr)
159 , m_stsdAtom(nullptr)
160 , m_stscAtom(nullptr)
161 , m_stcoAtom(nullptr)
162 , m_stszAtom(nullptr)
164 , m_framesPerSample(1)
165 , m_chunkOffsetSize(4)
167 , m_sampleToChunkEntryCount(0)
168 , m_rawTkhdCreationTime(0)
169 , m_rawMdhdCreationTime(0)
170 , m_rawTkhdModificationTime(0)
171 , m_rawMdhdModificationTime(0)
172 , m_rawTkhdDuration(0)
173 , m_rawMdhdDuration(0)
201 static const string context(
"reading chunk offset table of MP4 track");
206 vector<std::uint64_t> offsets;
209 std::uint64_t actualTableSize = m_stcoAtom->
dataSize();
211 diag.emplace_back(
DiagLevel::Critical,
"The stco atom is truncated. There are no chunk offsets present.", context);
214 actualTableSize -= 8;
216 std::uint32_t actualChunkCount =
chunkCount();
218 if (calculatedTableSize < actualTableSize) {
220 DiagLevel::Critical,
"The stco atom stores more chunk offsets as denoted. The additional chunk offsets will be ignored.", context);
221 }
else if (calculatedTableSize > actualTableSize) {
222 diag.emplace_back(
DiagLevel::Critical,
"The stco atom is truncated. It stores less chunk offsets as denoted.", context);
223 actualChunkCount =
static_cast<std::uint32_t
>(floor(
static_cast<double>(actualTableSize) /
static_cast<double>(
chunkOffsetSize())));
226 offsets.reserve(actualChunkCount);
230 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
231 offsets.push_back(
reader().readUInt32BE());
235 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
236 offsets.push_back(
reader().readUInt64BE());
240 diag.emplace_back(
DiagLevel::Critical,
"The determined chunk offset size is invalid.", context);
245 if (parseFragments) {
249 moofAtom->parse(diag);
252 trafAtom->parse(diag);
255 tfhdAtom->parse(diag);
256 std::uint32_t calculatedDataSize = 0;
257 if (tfhdAtom->dataSize() < calculatedDataSize) {
260 inputStream().seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
261 const std::uint32_t
flags =
reader().readUInt24BE();
263 if (
flags & 0x000001) {
264 calculatedDataSize += 8;
266 if (
flags & 0x000002) {
267 calculatedDataSize += 4;
269 if (
flags & 0x000008) {
270 calculatedDataSize += 4;
272 if (
flags & 0x000010) {
273 calculatedDataSize += 4;
275 if (
flags & 0x000020) {
276 calculatedDataSize += 4;
282 std::uint32_t defaultSampleSize = 0;
284 if (tfhdAtom->dataSize() < calculatedDataSize) {
285 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
287 if (
flags & 0x000001) {
291 if (
flags & 0x000002) {
295 if (
flags & 0x000008) {
299 if (
flags & 0x000010) {
300 defaultSampleSize =
reader().readUInt32BE();
302 if (
flags & 0x000020) {
309 std::uint32_t trunCalculatedDataSize = 8;
310 if (trunAtom->dataSize() < trunCalculatedDataSize) {
313 inputStream().seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
314 std::uint32_t trunFlags =
reader().readUInt24BE();
317 if (trunFlags & 0x000001) {
318 trunCalculatedDataSize += 4;
320 if (trunFlags & 0x000004) {
321 trunCalculatedDataSize += 4;
323 std::uint32_t entrySize = 0;
324 if (trunFlags & 0x000100) {
327 if (trunFlags & 0x000200) {
330 if (trunFlags & 0x000400) {
333 if (trunFlags & 0x000800) {
337 if (trunAtom->dataSize() < trunCalculatedDataSize) {
338 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
340 if (trunFlags & 0x000001) {
344 if (trunFlags & 0x000004) {
348 if (trunFlags & 0x000100) {
354 if (trunFlags & 0x000200) {
355 m_sampleSizes.push_back(
reader().readUInt32BE());
356 m_size += m_sampleSizes.back();
358 m_size += defaultSampleSize;
360 if (trunFlags & 0x000400) {
363 if (trunFlags & 0x000800) {
370 if (m_sampleSizes.empty() && defaultSampleSize) {
371 m_sampleSizes.push_back(defaultSampleSize);
386std::uint64_t Mp4Track::accumulateSampleSizes(
size_t &sampleIndex,
size_t count,
Diagnostics &diag)
388 if (sampleIndex + count <= m_sampleSizes.size()) {
389 std::uint64_t sum = 0;
390 for (
size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
391 sum += m_sampleSizes[sampleIndex];
394 }
else if (m_sampleSizes.size() == 1) {
395 sampleIndex += count;
396 return static_cast<std::uint64_t
>(m_sampleSizes.front()) * count;
398 diag.emplace_back(
DiagLevel::Critical,
"There are not as many sample size entries as samples.",
"reading chunk sizes of MP4 track");
399 throw InvalidDataException();
411void Mp4Track::addChunkSizeEntries(
412 std::vector<std::uint64_t> &chunkSizeTable,
size_t count,
size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag)
414 for (
size_t i = 0; i < count; ++i) {
415 chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex,
sampleCount, diag));
423const TrackHeaderInfo &Mp4Track::verifyPresentTrackHeader()
const
425 if (m_trackHeaderInfo) {
426 return *m_trackHeaderInfo;
430 auto &info = *(m_trackHeaderInfo = std::make_unique<TrackHeaderInfo>());
436 info.discardBuffer = m_tkhdAtom->
buffer() ==
nullptr;
437 if (info.discardBuffer) {
442 switch (info.version =
static_cast<std::uint8_t
>(m_tkhdAtom->
buffer()[m_tkhdAtom->
headerSize()])) {
444 info.additionalDataOffset = 32;
447 info.additionalDataOffset = 44;
450 info.additionalDataOffset = 44;
451 info.versionUnknown =
true;
455 if (info.additionalDataOffset + 48u <= m_tkhdAtom->
dataSize()) {
456 info.canUseExisting =
true;
458 info.truncated =
true;
459 info.canUseExisting = info.additionalDataOffset < m_tkhdAtom->
dataSize();
460 if (!info.canUseExisting && info.discardBuffer) {
466 info.requiredSize = m_tkhdAtom->
dataSize() + 8;
467 info.timings = computeTimings();
468 info.timingsVersion = info.timings.requiredTkhdVersion();
469 if (info.version == 0) {
470 info.writeVersion = info.timingsVersion;
472 if (info.writeVersion != 0) {
473 info.requiredSize += 12;
476 info.writeVersion = info.version;
479 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
480 info.requiredSize += 8;
488Mp4Timings Mp4Track::computeTimings()
const
490 auto timings = Mp4Timings();
492 timings.tkhdCreationTime = m_rawTkhdCreationTime;
493 timings.tkhdModificationTime = m_rawTkhdModificationTime;
494 timings.tkhdDuration = m_rawTkhdDuration;
495 timings.mdhdCreationTime = m_rawMdhdCreationTime;
496 timings.mdhdModificationTime = m_rawMdhdModificationTime;
497 timings.mdhdDuration = m_rawMdhdDuration;
500 timings.tkhdModificationTime = timings.mdhdModificationTime
502 timings.tkhdDuration = timings.mdhdDuration =
static_cast<std::uint64_t
>(
m_duration.totalTicks() *
m_timeScale / TimeSpan::ticksPerSecond);
516 static const string context(
"reading sample to chunk table of MP4 track");
518 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
522 std::uint64_t actualTableSize = m_stscAtom->
dataSize();
523 if (actualTableSize < 20) {
524 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. There are no \"sample to chunk\" entries present.", context);
527 actualTableSize -= 8;
530 std::uint64_t calculatedTableSize = actualSampleToChunkEntryCount * 12;
531 if (calculatedTableSize < actualTableSize) {
532 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom stores more entries as denoted. The additional entries will be ignored.", context);
533 }
else if (calculatedTableSize > actualTableSize) {
534 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. It stores less entries as denoted.", context);
535 actualSampleToChunkEntryCount = actualTableSize / 12;
538 vector<tuple<std::uint32_t, std::uint32_t, std::uint32_t>> sampleToChunkTable;
539 sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
541 for (std::uint32_t i = 0; i < actualSampleToChunkEntryCount; ++i) {
543 std::uint32_t firstChunk =
reader().readUInt32BE();
544 std::uint32_t samplesPerChunk =
reader().readUInt32BE();
545 std::uint32_t sampleDescriptionIndex =
reader().readUInt32BE();
546 sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
548 return sampleToChunkTable;
565 static const string context(
"reading chunk sizes of MP4 track");
567 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
573 vector<std::uint64_t> chunkSizes;
574 if (!sampleToChunkTable.empty()) {
576 auto tableIterator = sampleToChunkTable.cbegin();
577 chunkSizes.reserve(m_chunkCount);
579 size_t sampleIndex = 0;
580 std::uint32_t previousChunkIndex = get<0>(*tableIterator);
581 if (previousChunkIndex != 1) {
582 diag.emplace_back(
DiagLevel::Critical,
"The first chunk of the first \"sample to chunk\" entry must be 1.", context);
583 previousChunkIndex = 1;
585 std::uint32_t samplesPerChunk = get<1>(*tableIterator);
588 for (
const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
589 std::uint32_t firstChunkIndex = get<0>(*tableIterator);
590 if (firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
591 addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
594 "The first chunk index of a \"sample to chunk\" entry must be greater than the first chunk of the previous entry and not "
595 "greater than the chunk count.",
599 previousChunkIndex = firstChunkIndex;
600 samplesPerChunk = get<1>(*tableIterator);
602 if (m_chunkCount >= previousChunkIndex) {
603 addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
616 static const string context(
"parsing MPEG-4 elementary stream descriptor");
617 using namespace Mpeg4ElementaryStreamObjectIds;
618 unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
622 if (
reader.readUInt32BE() != 0) {
636 esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
637 esInfo->id =
reader.readUInt16BE();
638 esInfo->esDescFlags =
reader.readByte();
639 if (esInfo->dependencyFlag()) {
640 esInfo->dependsOnId =
reader.readUInt16BE();
642 if (esInfo->urlFlag()) {
645 if (esInfo->ocrFlag()) {
646 esInfo->ocrId =
reader.readUInt16BE();
650 esDescChild; esDescChild = esDescChild->nextSibling()) {
651 esDescChild->parse(diag);
652 switch (esDescChild->id()) {
655 reader.stream()->seekg(
static_cast<streamoff
>(esDescChild->dataOffset()));
656 esInfo->objectTypeId =
reader.readByte();
657 esInfo->decCfgDescFlags =
reader.readByte();
658 esInfo->bufferSize =
reader.readUInt24BE();
659 esInfo->maxBitrate =
reader.readUInt32BE();
660 esInfo->averageBitrate =
reader.readUInt32BE();
662 decCfgDescChild = decCfgDescChild->
nextSibling()) {
663 decCfgDescChild->parse(diag);
664 switch (decCfgDescChild->id()) {
667 switch (esInfo->objectTypeId) {
669 case Mpeg2AacMainProfile:
670 case Mpeg2AacLowComplexityProfile:
671 case Mpeg2AacScaleableSamplingRateProfile:
674 esInfo->audioSpecificConfig
678 esInfo->videoSpecificConfig
693 diag.emplace_back(
DiagLevel::Critical,
"The MPEG-4 descriptor element structure is invalid.", context);
696 diag.emplace_back(
DiagLevel::Warning,
"Elementary stream descriptor atom (esds) is truncated.", context);
706 istream &stream, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
708 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
709 using namespace Mpeg4AudioObjectIds;
712 auto buff = make_unique<char[]>(
size);
713 stream.read(buff.get(),
static_cast<streamoff
>(
size));
714 BitReader bitReader(buff.get(),
size);
715 auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
718 auto getAudioObjectType = [&bitReader] {
719 std::uint8_t objType = bitReader.readBits<std::uint8_t>(5);
721 objType = 32 + bitReader.readBits<std::uint8_t>(6);
725 audioCfg->audioObjectType = getAudioObjectType();
727 if ((audioCfg->sampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
728 audioCfg->sampleFrequency = bitReader.readBits<std::uint32_t>(24);
731 audioCfg->channelConfiguration = bitReader.readBits<std::uint8_t>(4);
733 switch (audioCfg->audioObjectType) {
736 audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
737 audioCfg->sbrPresent =
true;
738 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
739 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
741 if ((audioCfg->audioObjectType = getAudioObjectType()) == ErBsac) {
742 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
746 switch (audioCfg->extensionAudioObjectType) {
748 audioCfg->psPresent =
true;
753 switch (audioCfg->audioObjectType) {
765 audioCfg->frameLengthFlag = bitReader.readBits<std::uint8_t>(1);
766 if ((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
767 audioCfg->coreCoderDelay = bitReader.readBits<std::uint8_t>(14);
769 audioCfg->extensionFlag = bitReader.readBit();
770 if (audioCfg->channelConfiguration == 0) {
773 switch (audioCfg->audioObjectType) {
776 audioCfg->layerNr = bitReader.readBits<std::uint8_t>(3);
780 if (audioCfg->extensionFlag == 1) {
781 switch (audioCfg->audioObjectType) {
783 audioCfg->numOfSubFrame = bitReader.readBits<std::uint8_t>(5);
784 audioCfg->layerLength = bitReader.readBits<std::uint16_t>(11);
790 audioCfg->resilienceFlags = bitReader.readBits<std::uint8_t>(3);
794 if (bitReader.readBit() == 1) {
803 switch (audioCfg->audioObjectType) {
815 switch (audioCfg->epConfig = bitReader.readBits<std::uint8_t>(2)) {
819 bitReader.skipBits(1);
826 if (audioCfg->extensionAudioObjectType != Sbr && audioCfg->extensionAudioObjectType != Ps && bitReader.bitsAvailable() >= 16) {
827 std::uint16_t syncExtensionType = bitReader.readBits<std::uint16_t>(11);
828 if (syncExtensionType == 0x2B7) {
829 if ((audioCfg->extensionAudioObjectType = getAudioObjectType()) == Sbr) {
830 if ((audioCfg->sbrPresent = bitReader.readBit())) {
831 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
832 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
834 if (bitReader.bitsAvailable() >= 12) {
835 if ((syncExtensionType = bitReader.readBits<std::uint16_t>(11)) == 0x548) {
836 audioCfg->psPresent = bitReader.readBits<std::uint8_t>(1);
840 }
else if (audioCfg->extensionAudioObjectType == ErBsac) {
841 if ((audioCfg->sbrPresent = bitReader.readBit())) {
842 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
843 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
846 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
848 }
else if (syncExtensionType == 0x548) {
849 audioCfg->psPresent = bitReader.readBit();
854 }
catch (
const std::ios_base::failure &) {
860 diag.emplace_back(
DiagLevel::Critical,
"Audio specific configuration is truncated.", context);
871 BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
873 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
874 using namespace Mpeg4AudioObjectIds;
875 auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
878 if (
size > 3 && (
reader.readUInt24BE() == 1)) {
883 switch (
reader.readByte()) {
886 videoCfg->profile =
reader.readByte();
896 if ((buff1 =
reader.readUInt24BE()) != 1) {
897 reader.stream()->seekg(-2, ios_base::cur);
898 videoCfg->userData.push_back(
static_cast<char>(buff1 >> 16));
905 if (buff1 != 1 &&
size > 0) {
906 videoCfg->userData +=
reader.readString(
size);
914 if (
reader.readUInt24BE() != 1) {
915 reader.stream()->seekg(-2, ios_base::cur);
924 diag.emplace_back(
DiagLevel::Critical,
"\"Visual Object Sequence Header\" not found.", context);
951 if (oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
954 static const unsigned int stcoDataBegin = 8;
955 std::uint64_t startPos = m_stcoAtom->
dataOffset() + stcoDataBegin;
956 std::uint64_t endPos = startPos + m_stcoAtom->
dataSize() - stcoDataBegin;
957 m_istream->seekg(
static_cast<streamoff
>(startPos));
958 m_ostream->seekp(
static_cast<streamoff
>(startPos));
959 vector<std::int64_t>::size_type i;
960 vector<std::int64_t>::size_type
size;
961 auto currentPos =
static_cast<std::uint64_t
>(
m_istream->tellg());
962 switch (m_stcoAtom->
id()) {
965 while ((currentPos + 4) <= endPos) {
967 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
968 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
969 off +=
static_cast<std::uint32_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
973 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
975 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
981 while ((currentPos + 8) <= endPos) {
983 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
984 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
985 off +=
static_cast<std::uint64_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
989 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
991 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
1022 switch (m_stcoAtom->
id()) {
1024 for (
auto offset : chunkOffsets) {
1025 m_writer.writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1029 for (
auto offset : chunkOffsets) {
1059 writer().writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1062 writer().writeUInt64BE(offset);
1108 CPP_UTILITIES_UNUSED(av1Config)
1109 CPP_UTILITIES_UNUSED(track)
1121 CPP_UTILITIES_UNUSED(diag)
1126 for (
Mp4Atom *trakChild = m_trakAtom->
firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1130 trakChild->makeBuffer();
1134 childAtom->makeBuffer();
1144 CPP_UTILITIES_UNUSED(diag)
1146 const auto &info = verifyPresentTrackHeader();
1149 std::uint64_t
size = 8;
1151 size += info.requiredSize;
1153 for (
Mp4Atom *trakChild = m_trakAtom->
firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1157 size += trakChild->totalSize();
1160 if (info.timingsVersion == 0) {
1170 bool dinfAtomWritten =
false;
1174 dinfAtomWritten =
true;
1176 size += childAtom->totalSize();
1179 if (!dinfAtomWritten) {
1197 ostream::pos_type trakStartOffset =
outputStream().tellp();
1209 trakChild->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1226 const auto &info = verifyPresentTrackHeader();
1229 if (info.versionUnknown) {
1231 argsToString(
"The version of the present \"tkhd\"-atom (", info.version,
") is unknown. Assuming version 1."),
1232 argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1234 if (info.truncated) {
1236 DiagLevel::Critical, argsToString(
"The present \"tkhd\"-atom is truncated."), argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1240 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
1241 writer().writeUInt32BE(1);
1243 writer().writeUInt64BE(info.requiredSize);
1245 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.requiredSize));
1250 writer().writeByte(info.writeVersion);
1251 std::uint32_t
flags = 0;
1264 if (info.writeVersion != 0) {
1265 writer().writeUInt64BE(info.timings.tkhdCreationTime);
1266 writer().writeUInt64BE(info.timings.tkhdModificationTime);
1268 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdCreationTime));
1269 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdModificationTime));
1273 writer().writeUInt32BE(
static_cast<std::uint32_t
>(
m_id));
1274 writer().writeUInt32BE(0);
1275 if (info.writeVersion != 0) {
1276 writer().writeUInt64BE(info.timings.tkhdDuration);
1278 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdDuration));
1280 writer().writeUInt32BE(0);
1281 writer().writeUInt32BE(0);
1284 if (info.canUseExisting) {
1287 static_cast<streamoff
>(m_tkhdAtom->
dataSize() - info.additionalDataOffset));
1289 if (info.discardBuffer) {
1294 diag.emplace_back(
DiagLevel::Warning,
"Writing some default values because the existing tkhd atom is truncated.",
"making tkhd atom");
1295 writer().writeInt16BE(0);
1296 writer().writeInt16BE(0);
1297 writer().writeFixed8BE(1.0);
1298 writer().writeUInt16BE(0);
1299 for (
const std::int32_t value : { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }) {
1300 writer().writeInt32BE(value);
1302 writer().writeFixed16BE(1.0);
1303 writer().writeFixed16BE(1.0);
1313 ostream::pos_type mdiaStartOffset =
outputStream().tellp();
1314 writer().writeUInt32BE(0);
1317 const auto &info = verifyPresentTrackHeader();
1318 const auto &timings = info.timings;
1319 const auto timingsVersion = timings.requiredMdhdVersion();
1320 writer().writeUInt32BE(timingsVersion != 0 ? 44 : 32);
1322 writer().writeByte(timingsVersion);
1323 writer().writeUInt24BE(0);
1324 if (timingsVersion != 0) {
1325 writer().writeUInt64BE(timings.mdhdCreationTime);
1326 writer().writeUInt64BE(timings.mdhdModificationTime);
1328 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdCreationTime));
1329 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdModificationTime));
1332 if (timingsVersion != 0) {
1333 writer().writeUInt64BE(timings.mdhdDuration);
1335 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdDuration));
1340 for (
const auto &detail :
m_locale) {
1346 auto codedLanguage =
static_cast<std::uint16_t
>(0u);
1347 for (
auto charIndex =
static_cast<std::size_t
>(0); charIndex != 3; ++charIndex) {
1348 const char langChar = charIndex < language->size() ? (*language)[charIndex] : 0;
1349 if (langChar >=
'a' && langChar <=
'z') {
1350 codedLanguage |=
static_cast<std::uint16_t
>((langChar - 0x60) << (0xA - charIndex * 0x5));
1355 if (language->empty()) {
1360 diag.emplace_back(
DiagLevel::Warning,
"Assigned language \"" % *language +
"\" is of an invalid format. Setting language to undefined.",
1361 "making mdhd atom");
1362 codedLanguage = 0x55C4;
1365 if (language->size() > 3) {
1367 DiagLevel::Warning,
"Assigned language \"" % *language +
"\" is longer than 3 byte and hence will be truncated.",
"making mdhd atom");
1369 writer().writeUInt16BE(codedLanguage);
1370 writer().writeUInt16BE(0);
1372 writer().writeUInt32BE(33 +
static_cast<std::uint32_t
>(
m_name.size()));
1374 writer().writeUInt64BE(0);
1393 diag.emplace_back(
DiagLevel::Critical,
"Media type is invalid; keeping media type as-is.",
"making hdlr atom");
1395 writer().writeUInt32BE(m_rawMediaType);
1398 for (
int i = 0; i < 3; ++i)
1399 writer().writeUInt32BE(0);
1413 ostream::pos_type minfStartOffset =
outputStream().tellp();
1414 writer().writeUInt32BE(0);
1416 bool dinfAtomWritten =
false;
1419 for (
Mp4Atom *childAtom = m_minfAtom->
firstChild(); childAtom; childAtom = childAtom->nextSibling()) {
1424 dinfAtomWritten =
true;
1426 childAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1430 if (!dinfAtomWritten) {
1431 writer().writeUInt32BE(36);
1434 writer().writeUInt32BE(28);
1436 writer().writeUInt32BE(0);
1437 writer().writeUInt32BE(1);
1439 writer().writeUInt32BE(12);
1442 writer().writeUInt24BE(0x000001);
1446 bool stblAtomWritten =
false;
1449 stblAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1450 stblAtomWritten =
true;
1453 if (!stblAtomWritten) {
1455 "Source track does not contain mandatory stbl atom and the tagparser lib is unable to make one from scratch.",
"making stbl atom");
1470 writer().writeUInt32BE(0);
1478 diag.emplace_back(
DiagLevel::Critical,
"Unable to make stsd atom from scratch.",
"making stsd atom");
1487 diag.emplace_back(
DiagLevel::Critical,
"Unable to make stts atom from scratch.",
"making stts atom");
1528 CPP_UTILITIES_UNUSED(progress)
1530 static const string context(
"parsing MP4 track");
1531 using namespace Mp4AtomIds;
1539 if (!(m_tkhdAtom = m_trakAtom->
childById(TrackHeader, diag))) {
1543 if (!(m_mdiaAtom = m_trakAtom->
childById(Media, diag))) {
1547 if (!(m_mdhdAtom = m_mdiaAtom->
childById(MediaHeader, diag))) {
1551 if (!(m_hdlrAtom = m_mdiaAtom->
childById(HandlerReference, diag))) {
1555 if (!(m_minfAtom = m_mdiaAtom->
childById(MediaInformation, diag))) {
1559 if (!(m_stblAtom = m_minfAtom->
childById(SampleTable, diag))) {
1563 if (!(m_stsdAtom = m_stblAtom->
childById(SampleDescription, diag))) {
1567 if (!(m_stcoAtom = m_stblAtom->
childById(ChunkOffset, diag)) && !(m_stcoAtom = m_stblAtom->
childById(ChunkOffset64, diag))) {
1571 if (!(m_stscAtom = m_stblAtom->
childById(SampleToChunk, diag))) {
1575 if (!(m_stszAtom = m_stblAtom->
childById(SampleSize, diag)) && !(m_stszAtom = m_stblAtom->
childById(CompactSampleSize, diag))) {
1588 auto atomVersion =
reader.readByte();
1593 switch (atomVersion) {
1595 m_rawTkhdCreationTime =
reader.readUInt32BE();
1596 m_rawTkhdModificationTime =
reader.readUInt32BE();
1598 m_istream->seekg(4, std::ios_base::cur);
1599 m_rawTkhdDuration =
reader.readUInt32BE();
1602 m_rawTkhdCreationTime =
reader.readUInt64BE();
1603 m_rawTkhdModificationTime =
reader.readUInt64BE();
1605 m_istream->seekg(4, std::ios_base::cur);
1606 m_rawTkhdDuration =
reader.readUInt64BE();
1610 "Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
1612 m_rawTkhdCreationTime = m_rawTkhdModificationTime = m_rawTkhdDuration = 0;
1620 atomVersion =
reader.readByte();
1622 switch (atomVersion) {
1624 m_rawMdhdCreationTime =
reader.readUInt32BE();
1625 m_rawMdhdModificationTime =
reader.readUInt32BE();
1627 m_rawMdhdDuration =
reader.readUInt32BE();
1630 m_rawMdhdCreationTime =
reader.readUInt64BE();
1631 m_rawMdhdModificationTime =
reader.readUInt64BE();
1633 m_rawMdhdDuration =
reader.readUInt64BE();
1637 "Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
1640 m_rawMdhdCreationTime = m_rawMdhdModificationTime = m_rawMdhdDuration = 0;
1646 m_duration = TimeSpan::fromSeconds(
static_cast<TimeSpan::TickType
>(m_rawMdhdDuration)) /
static_cast<TimeSpan::TickType
>(
m_timeScale);
1648 std::uint16_t tmp =
reader.readUInt16BE();
1650 const char buff[] = {
1651 static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1652 static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1653 static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1664 switch (m_rawMediaType =
reader.readUInt32BE()) {
1685 if (
static_cast<std::uint64_t
>(tmp =
static_cast<std::uint8_t
>(
m_istream->peek())) == m_hdlrAtom->
dataSize() - 12 - 4 - 8 - 1) {
1697 m_chunkCount =
reader.readUInt32BE();
1701 const auto entryCount =
reader.readUInt32BE();
1702 Mp4Atom *esDescParentAtom =
nullptr;
1705 for (
Mp4Atom *codecConfigContainerAtom = m_stsdAtom->
firstChild(); codecConfigContainerAtom;
1706 codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
1707 codecConfigContainerAtom->
parse(diag);
1710 m_formatId = interpretIntegerAsString<std::uint32_t>(codecConfigContainerAtom->id());
1714 m_istream->seekg(
static_cast<streamoff
>(codecConfigContainerAtom->dataOffset()));
1715 switch (codecConfigContainerAtom->id()) {
1731 tmp =
reader.readUInt16BE();
1747 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1750 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1753 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1755 if (!esDescParentAtom) {
1756 esDescParentAtom = codecConfigContainerAtom;
1773 m_istream->seekg(6 + 2 + 16, ios_base::cur);
1779 m_framesPerSample =
reader.readUInt16BE();
1784 }
else if (tmp < 32) {
1788 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1789 if (!esDescParentAtom) {
1790 esDescParentAtom = codecConfigContainerAtom;
1795 codecConfigContainerAtom->
denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1796 if (!esDescParentAtom) {
1797 esDescParentAtom = codecConfigContainerAtom;
1808 if (esDescParentAtom) {
1811 m_istream->seekg(
static_cast<streamoff
>(avcConfigAtom->dataOffset()));
1812 m_avcConfig = make_unique<TagParser::AvcConfiguration>();
1814 m_avcConfig->parse(
reader, avcConfigAtom->dataSize(), diag);
1825 m_istream->seekg(
static_cast<streamoff
>(av1ConfigAtom->dataOffset()));
1826 m_av1Config = make_unique<TagParser::Av1Configuration>();
1828 m_av1Config->parse(
reader, av1ConfigAtom->dataSize(), diag);
1848 m_bitrate =
static_cast<double>(m_esInfo->averageBitrate) / 1000;
1849 m_maxBitrate =
static_cast<double>(m_esInfo->maxBitrate) / 1000;
1850 if (m_esInfo->audioSpecificConfig) {
1853 m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
1854 if (m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1859 diag.emplace_back(
DiagLevel::Warning,
"Audio specific config has invalid sample frequency index.", context);
1861 if (m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1868 DiagLevel::Warning,
"Audio specific config has invalid extension sample frequency index.", context);
1870 m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1873 if (m_esInfo->videoSpecificConfig) {
1876 m_format.
sub = m_esInfo->videoSpecificConfig->profile;
1877 if (!m_esInfo->videoSpecificConfig->userData.empty()) {
1879 m_formatId += m_esInfo->videoSpecificConfig->userData;
1889 m_istream->seekg(
static_cast<streamoff
>(m_chunkOffsetSize == 8 ?
reader.readUInt64BE() :
reader.readUInt32BE()));
1902 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse child atoms of \"stsd\"-atom.", context);
1907 m_sampleSizes.clear();
1909 std::uint64_t actualSampleSizeTableSize = m_stszAtom->
dataSize();
1910 if (actualSampleSizeTableSize < 12) {
1912 "The stsz atom is truncated. There are no sample sizes present. The size of the track can not be determined.", context);
1914 actualSampleSizeTableSize -= 12;
1916 std::uint32_t fieldSize;
1917 std::uint32_t constantSize;
1921 fieldSize =
reader.readByte();
1924 constantSize =
reader.readUInt32BE();
1929 m_sampleSizes.push_back(constantSize);
1933 const auto calculatedSampleSizeTableSize
1934 =
static_cast<std::uint64_t
>(std::ceil((0.125 * fieldSize) *
static_cast<double>(
m_sampleCount)));
1935 if (calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1937 DiagLevel::Critical,
"The stsz atom stores more entries as denoted. The additional entries will be ignored.", context);
1938 }
else if (calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1939 diag.emplace_back(
DiagLevel::Critical,
"The stsz atom is truncated. It stores less entries as denoted.", context);
1940 actualSampleCount =
static_cast<std::uint64_t
>(floor(
static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize)));
1942 m_sampleSizes.reserve(actualSampleCount);
1943 std::uint32_t i = 1;
1944 switch (fieldSize) {
1946 for (; i <= actualSampleCount; i += 2) {
1947 std::uint8_t val =
reader.readByte();
1948 m_sampleSizes.push_back(val >> 4);
1949 m_sampleSizes.push_back(val & 0xF0);
1950 m_size += (val >> 4) + (val & 0xF0);
1952 if (i <= actualSampleCount + 1) {
1953 m_sampleSizes.push_back(
reader.readByte() >> 4);
1954 m_size += m_sampleSizes.back();
1958 for (; i <= actualSampleCount; ++i) {
1959 m_sampleSizes.push_back(
reader.readByte());
1960 m_size += m_sampleSizes.back();
1964 for (; i <= actualSampleCount; ++i) {
1965 m_sampleSizes.push_back(
reader.readUInt16BE());
1966 m_size += m_sampleSizes.back();
1970 for (; i <= actualSampleCount; ++i) {
1971 m_sampleSizes.push_back(
reader.readUInt32BE());
1972 m_size += m_sampleSizes.back();
1977 "The fieldsize used to store the sample sizes is not supported. The sample count and size of the track can not be determined.",
1984 std::uint64_t totalDuration = 0;
1985 for (
Mp4Atom *moofAtom = m_trakAtom->
container().firstElement()->siblingByIdIncludingThis(MovieFragment, diag); moofAtom;
1986 moofAtom = moofAtom->siblingById(MovieFragment, diag)) {
1987 moofAtom->parse(diag);
1988 for (
Mp4Atom *trafAtom = moofAtom->
childById(TrackFragment, diag); trafAtom; trafAtom = trafAtom->
siblingById(TrackFragment, diag)) {
1989 trafAtom->parse(diag);
1990 for (
Mp4Atom *tfhdAtom = trafAtom->
childById(TrackFragmentHeader, diag); tfhdAtom;
1991 tfhdAtom = tfhdAtom->
siblingById(TrackFragmentHeader, diag)) {
1992 tfhdAtom->parse(diag);
1993 std::uint32_t calculatedDataSize = 0;
1994 if (tfhdAtom->dataSize() < calculatedDataSize) {
1997 m_istream->seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
1998 std::uint32_t tfhdFlags =
reader.readUInt24BE();
2000 if (tfhdFlags & 0x000001) {
2001 calculatedDataSize += 8;
2003 if (tfhdFlags & 0x000002) {
2004 calculatedDataSize += 4;
2006 if (tfhdFlags & 0x000008) {
2007 calculatedDataSize += 4;
2009 if (tfhdFlags & 0x000010) {
2010 calculatedDataSize += 4;
2012 if (tfhdFlags & 0x000020) {
2013 calculatedDataSize += 4;
2017 std::uint32_t defaultSampleDuration = 0;
2018 std::uint32_t defaultSampleSize = 0;
2020 if (tfhdAtom->dataSize() < calculatedDataSize) {
2021 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
2023 if (tfhdFlags & 0x000001) {
2027 if (tfhdFlags & 0x000002) {
2031 if (tfhdFlags & 0x000008) {
2032 defaultSampleDuration =
reader.readUInt32BE();
2035 if (tfhdFlags & 0x000010) {
2036 defaultSampleSize =
reader.readUInt32BE();
2038 if (tfhdFlags & 0x000020) {
2043 for (
Mp4Atom *trunAtom = trafAtom->
childById(TrackFragmentRun, diag); trunAtom;
2044 trunAtom = trunAtom->
siblingById(TrackFragmentRun, diag)) {
2045 std::uint32_t trunCalculatedDataSize = 8;
2046 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2049 m_istream->seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
2050 std::uint32_t trunFlags =
reader.readUInt24BE();
2053 if (trunFlags & 0x000001) {
2054 trunCalculatedDataSize += 4;
2056 if (trunFlags & 0x000004) {
2057 trunCalculatedDataSize += 4;
2059 std::uint32_t entrySize = 0;
2060 if (trunFlags & 0x000100) {
2063 if (trunFlags & 0x000200) {
2066 if (trunFlags & 0x000400) {
2069 if (trunFlags & 0x000800) {
2072 trunCalculatedDataSize += entrySize *
sampleCount;
2073 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2074 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
2076 if (trunFlags & 0x000001) {
2080 if (trunFlags & 0x000004) {
2084 if (trunFlags & 0x000100) {
2085 totalDuration +=
reader.readUInt32BE();
2087 totalDuration += defaultSampleDuration;
2089 if (trunFlags & 0x000200) {
2090 m_sampleSizes.push_back(
reader.readUInt32BE());
2091 m_size += m_sampleSizes.back();
2093 m_size += defaultSampleSize;
2095 if (trunFlags & 0x000400) {
2098 if (trunFlags & 0x000800) {
2105 if (m_sampleSizes.empty() && defaultSampleSize) {
2106 m_sampleSizes.push_back(defaultSampleSize);
2121 m_duration = TimeSpan::fromSeconds(
static_cast<double>(totalDuration) /
static_cast<double>(
timeScale));
2126 if (m_bitrate < 0.01 && m_bitrate > -0.01) {
2132 m_sampleToChunkEntryCount =
reader.readUInt32BE();
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks.
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
std::uint64_t size() const
Returns the size in bytes if known; otherwise returns 0.
std::uint32_t timeScale() const
Returns the time scale if known; otherwise returns 0.
std::uint8_t m_extensionChannelConfig
std::string_view m_chromaFormat
std::uint64_t m_sampleCount
std::uint64_t startOffset() const
Returns the start offset of the track in the associated stream.
AspectRatio m_pixelAspectRatio
std::uint16_t m_bitsPerSample
std::istream & inputStream()
Returns the associated input stream.
bool isEnabled() const
Returns true if the track is marked as enabled; otherwise returns false.
std::uint16_t m_channelCount
std::uint64_t sampleCount() const
Returns the number of samples/frames if known; otherwise returns 0.
std::uint8_t m_channelConfig
CppUtilities::BinaryReader & reader()
Returns a binary reader for the associated stream.
CppUtilities::TimeSpan m_duration
CppUtilities::BinaryReader m_reader
TrackFlags flags() const
Returns flags (various boolean properties) of this track.
std::uint32_t m_timeScale
CppUtilities::DateTime m_modificationTime
CppUtilities::BinaryWriter m_writer
bool isHeaderValid() const
Returns an indication whether the track header is valid.
std::ostream & outputStream()
Returns the associated output stream.
std::uint32_t m_extensionSamplingFrequency
CppUtilities::DateTime m_creationTime
std::string m_compressorName
std::uint32_t m_samplingFrequency
CppUtilities::BinaryWriter & writer()
Returns a binary writer for the associated stream.
The Diagnostics class is a container for DiagMessage.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
void copyEntirely(TargetStream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress)
Writes the entire element including all children to the specified targetStream.
std::uint64_t startOffset() const
Returns the start offset in the related stream.
void discardBuffer()
Discards buffered data.
const std::unique_ptr< char[]> & buffer()
Returns buffered data.
std::uint32_t headerSize() const
Returns the header size of the element in byte.
const IdentifierType & id() const
Returns the element ID.
ImplementationType * childById(const IdentifierType &id, Diagnostics &diag)
Returns the first child with the specified id.
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * denoteFirstChild(std::uint32_t offset)
Denotes the first child to start at the specified offset (relative to the start offset of this descri...
ImplementationType * firstChild()
Returns the first child of the element.
DataSizeType dataSize() const
Returns the data size of the element in byte.
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
std::uint64_t dataOffset() const
Returns the data offset of the element in the related stream.
ContainerType & container()
Returns the related container.
CppUtilities::BinaryReader & reader()
Returns the related BinaryReader.
void makeBuffer()
Buffers the element (header and data).
ImplementationType * siblingById(const IdentifierType &id, Diagnostics &diag)
Returns the first sibling with the specified id.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
The Mp4Atom class helps to parse MP4 files.
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.
static const CppUtilities::DateTime epoch
Dates within MP4 tracks are expressed as the number of seconds since this date.
Implementation of TagParser::AbstractTrack for the MP4 container.
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.
static std::unique_ptr< Mpeg4ElementaryStreamInfo > parseMpeg4ElementaryStreamInfo(CppUtilities::BinaryReader &reader, Mp4Atom *esDescAtom, Diagnostics &diag)
Reads the MPEG-4 elementary stream descriptor for the track.
std::uint32_t chunkCount() const
Returns the number of chunks denoted by the stco atom.
std::vector< std::tuple< std::uint32_t, std::uint32_t, std::uint32_t > > readSampleToChunkTable(Diagnostics &diag)
Reads the sample to chunk table.
static void addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track)
Adds the information from the specified avcConfig to the specified track.
std::vector< std::uint64_t > readChunkSizes(TagParser::Diagnostics &diag)
Reads the chunk sizes from the stsz (sample sizes) and stsc (samples per chunk) atom.
void updateChunkOffsets(const std::vector< std::int64_t > &oldMdatOffsets, const std::vector< std::int64_t > &newMdatOffsets)
Updates the chunk offsets of the track.
void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override
This method is internally called to parse header information.
void makeSampleTable(Diagnostics &diag)
Makes the sample table (stbl atom) for the track.
std::uint64_t requiredSize(Diagnostics &diag) const
Returns the number of bytes written when calling makeTrack().
TrackType type() const override
Returns the type of the track if known; otherwise returns TrackType::Unspecified.
std::uint32_t sampleToChunkEntryCount() const
Returns the number of "sample to chunk" entries within the stsc atom.
void makeMedia(Diagnostics &diag)
Makes the media information (mdia atom) for the track.
std::vector< std::uint64_t > readChunkOffsets(bool parseFragments, Diagnostics &diag)
Reads the chunk offsets from the stco atom and fragments if parseFragments is true.
unsigned int chunkOffsetSize() const
Returns the size of a single chunk offset denotation within the stco atom.
~Mp4Track() override
Destroys the track.
void makeTrackHeader(Diagnostics &diag)
Makes the track header (tkhd atom) for the track.
void bufferTrackAtoms(Diagnostics &diag)
Buffers all atoms required by the makeTrack() method.
void makeMediaInfo(Diagnostics &diag)
Makes a media information (minf atom) for the track.
Mp4Atom & trakAtom()
Returns the trak atom for the current instance.
void makeTrack(Diagnostics &diag)
Makes the track entry ("trak"-atom) for the track.
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.
void updateChunkOffset(std::uint32_t chunkIndex, std::uint64_t offset)
Updates a particular chunk offset.
Mpeg4AudioSpecificConfig()
The Mpeg4Descriptor class helps to parse MPEG-4 descriptors.
Mpeg4VideoSpecificConfig()
static void addInfo(const MpegAudioFrame &frame, AbstractTrack &track)
Adds the information from the specified frame to the specified track.
The MpegAudioFrame class is used to parse MPEG audio frames.
void parseHeader(CppUtilities::BinaryReader &reader, Diagnostics &diag)
Parses the header read using the specified reader.
This exception is thrown when the an operation is invoked that has not been implemented yet.
void setWidth(std::uint32_t value)
Sets the width.
void setHeight(std::uint32_t value)
Sets the height.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
TAG_PARSER_EXPORT MediaFormat fourccToMediaFormat(std::uint32_t fourccId)
@ CompositionTimeToSample
@ Mpeg4ElementaryStreamDescriptor
@ Mpeg4ElementaryStreamDescriptor2
TAG_PARSER_EXPORT MediaFormat idToMediaFormat(std::uint8_t mpeg4AudioObjectId, bool sbrPresent=false, bool psPresent=false)
TAG_PARSER_EXPORT MediaFormat streamObjectTypeFormat(std::uint8_t streamObjectTypeId)
Returns the TagParser::MediaFormat denoted by the specified MPEG-4 stream ID.
@ VisualObjectSequenceStart
Contains all classes and functions of the TagInfo library.
std::uint32_t mpeg4SamplingFrequencyTable[13]
@ PreserveRawTimingValues
TrackType
The TrackType enum specifies the underlying file type of a track and the concrete class of the track ...
The Av1Configuration struct provides a parser for AV1 configuration found in ISOBMFF files.
The AvcConfiguration struct provides a parser for AVC configuration.
std::vector< SpsInfo > spsInfos
std::uint8_t profileIndication
std::uint8_t levelIndication
static const LocaleDetail & getEmpty()
Returns an empty LocaleDetail.
The Mp4Timings struct holds timing values found in multiple MP4 atoms.
std::uint64_t mdhdModificationTime
constexpr std::uint8_t requiredTkhdVersion() const
std::uint64_t tkhdModificationTime
std::uint64_t tkhdCreationTime
std::uint64_t tkhdDuration
std::uint64_t mdhdDuration
std::uint64_t mdhdCreationTime
constexpr std::uint8_t requiredMdhdVersion() const
The SpsInfo struct holds the sequence parameter set.
AspectRatio pixelAspectRatio
std::uint8_t profileIndication
std::uint8_t levelIndication
ugolomb chromaFormatIndication