18#include <c++utilities/conversion/stringbuilder.h>
19#include <c++utilities/io/binaryreader.h>
20#include <c++utilities/io/binarywriter.h>
21#include <c++utilities/io/bitreader.h>
53 std::uint64_t requiredSize = 100;
55 bool canUseExisting =
false;
57 bool truncated =
false;
59 std::uint8_t version = 0;
61 std::uint8_t writeVersion = 0;
63 bool versionUnknown =
false;
67 std::uint8_t timingsVersion = 0;
69 std::uint8_t additionalDataOffset = 0;
71 bool discardBuffer =
false;
77 ||
tkhdDuration > std::numeric_limits<std::uint32_t>::max())
85 ||
mdhdDuration > std::numeric_limits<std::uint32_t>::max())
98 , sampleFrequencyIndex(0xF)
100 , channelConfiguration(0)
101 , extensionAudioObjectType(0)
104 , extensionSampleFrequencyIndex(0xF)
105 , extensionSampleFrequency(0)
106 , extensionChannelConfiguration(0)
107 , frameLengthFlag(false)
108 , dependsOnCoreCoder(false)
150 , m_trakAtom(&trakAtom)
151 , m_tkhdAtom(nullptr)
152 , m_mdiaAtom(nullptr)
153 , m_mdhdAtom(nullptr)
154 , m_hdlrAtom(nullptr)
155 , m_minfAtom(nullptr)
156 , m_stblAtom(nullptr)
157 , m_stsdAtom(nullptr)
158 , m_stscAtom(nullptr)
159 , m_stcoAtom(nullptr)
160 , m_stszAtom(nullptr)
162 , m_framesPerSample(1)
163 , m_chunkOffsetSize(4)
165 , m_sampleToChunkEntryCount(0)
166 , m_rawTkhdCreationTime(0)
167 , m_rawMdhdCreationTime(0)
168 , m_rawTkhdModificationTime(0)
169 , m_rawMdhdModificationTime(0)
170 , m_rawTkhdDuration(0)
171 , m_rawMdhdDuration(0)
199 static const string context(
"reading chunk offset table of MP4 track");
204 vector<std::uint64_t> offsets;
207 std::uint64_t actualTableSize = m_stcoAtom->
dataSize();
209 diag.emplace_back(
DiagLevel::Critical,
"The stco atom is truncated. There are no chunk offsets present.", context);
212 actualTableSize -= 8;
214 std::uint32_t actualChunkCount =
chunkCount();
216 if (calculatedTableSize < actualTableSize) {
218 DiagLevel::Critical,
"The stco atom stores more chunk offsets as denoted. The additional chunk offsets will be ignored.", context);
219 }
else if (calculatedTableSize > actualTableSize) {
220 diag.emplace_back(
DiagLevel::Critical,
"The stco atom is truncated. It stores less chunk offsets as denoted.", context);
221 actualChunkCount =
static_cast<std::uint32_t
>(floor(
static_cast<double>(actualTableSize) /
static_cast<double>(
chunkOffsetSize())));
224 offsets.reserve(actualChunkCount);
228 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
229 offsets.push_back(
reader().readUInt32BE());
233 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
234 offsets.push_back(
reader().readUInt64BE());
238 diag.emplace_back(
DiagLevel::Critical,
"The determined chunk offset size is invalid.", context);
243 if (parseFragments) {
247 moofAtom->parse(diag);
250 trafAtom->parse(diag);
253 tfhdAtom->parse(diag);
254 std::uint32_t calculatedDataSize = 0;
255 if (tfhdAtom->dataSize() < calculatedDataSize) {
258 inputStream().seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
259 const std::uint32_t
flags =
reader().readUInt24BE();
261 if (
flags & 0x000001) {
262 calculatedDataSize += 8;
264 if (
flags & 0x000002) {
265 calculatedDataSize += 4;
267 if (
flags & 0x000008) {
268 calculatedDataSize += 4;
270 if (
flags & 0x000010) {
271 calculatedDataSize += 4;
273 if (
flags & 0x000020) {
274 calculatedDataSize += 4;
280 std::uint32_t defaultSampleSize = 0;
282 if (tfhdAtom->dataSize() < calculatedDataSize) {
283 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
285 if (
flags & 0x000001) {
289 if (
flags & 0x000002) {
293 if (
flags & 0x000008) {
297 if (
flags & 0x000010) {
298 defaultSampleSize =
reader().readUInt32BE();
300 if (
flags & 0x000020) {
307 std::uint32_t trunCalculatedDataSize = 8;
308 if (trunAtom->dataSize() < trunCalculatedDataSize) {
311 inputStream().seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
312 std::uint32_t trunFlags =
reader().readUInt24BE();
315 if (trunFlags & 0x000001) {
316 trunCalculatedDataSize += 4;
318 if (trunFlags & 0x000004) {
319 trunCalculatedDataSize += 4;
321 std::uint32_t entrySize = 0;
322 if (trunFlags & 0x000100) {
325 if (trunFlags & 0x000200) {
328 if (trunFlags & 0x000400) {
331 if (trunFlags & 0x000800) {
335 if (trunAtom->dataSize() < trunCalculatedDataSize) {
336 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
338 if (trunFlags & 0x000001) {
342 if (trunFlags & 0x000004) {
346 if (trunFlags & 0x000100) {
352 if (trunFlags & 0x000200) {
353 m_sampleSizes.push_back(
reader().readUInt32BE());
354 m_size += m_sampleSizes.back();
356 m_size += defaultSampleSize;
358 if (trunFlags & 0x000400) {
361 if (trunFlags & 0x000800) {
368 if (m_sampleSizes.empty() && defaultSampleSize) {
369 m_sampleSizes.push_back(defaultSampleSize);
384std::uint64_t Mp4Track::accumulateSampleSizes(
size_t &sampleIndex,
size_t count,
Diagnostics &diag)
386 if (sampleIndex + count <= m_sampleSizes.size()) {
387 std::uint64_t sum = 0;
388 for (
size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
389 sum += m_sampleSizes[sampleIndex];
392 }
else if (m_sampleSizes.size() == 1) {
393 sampleIndex += count;
394 return static_cast<std::uint64_t
>(m_sampleSizes.front()) * count;
396 diag.emplace_back(
DiagLevel::Critical,
"There are not as many sample size entries as samples.",
"reading chunk sizes of MP4 track");
397 throw InvalidDataException();
409void Mp4Track::addChunkSizeEntries(
410 std::vector<std::uint64_t> &chunkSizeTable,
size_t count,
size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag)
412 for (
size_t i = 0; i < count; ++i) {
413 chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex,
sampleCount, diag));
421const TrackHeaderInfo &Mp4Track::verifyPresentTrackHeader()
const
423 if (m_trackHeaderInfo) {
424 return *m_trackHeaderInfo;
428 auto &info = *(m_trackHeaderInfo = std::make_unique<TrackHeaderInfo>());
434 info.discardBuffer = m_tkhdAtom->
buffer() ==
nullptr;
435 if (info.discardBuffer) {
440 switch (info.version =
static_cast<std::uint8_t
>(m_tkhdAtom->
buffer()[m_tkhdAtom->
headerSize()])) {
442 info.additionalDataOffset = 32;
445 info.additionalDataOffset = 44;
448 info.additionalDataOffset = 44;
449 info.versionUnknown =
true;
453 if (info.additionalDataOffset + 48u <= m_tkhdAtom->
dataSize()) {
454 info.canUseExisting =
true;
456 info.truncated =
true;
457 info.canUseExisting = info.additionalDataOffset < m_tkhdAtom->
dataSize();
458 if (!info.canUseExisting && info.discardBuffer) {
464 info.requiredSize = m_tkhdAtom->
dataSize() + 8;
465 info.timings = computeTimings();
466 info.timingsVersion = info.timings.requiredTkhdVersion();
467 if (info.version == 0) {
468 info.writeVersion = info.timingsVersion;
470 if (info.writeVersion != 0) {
471 info.requiredSize += 12;
474 info.writeVersion = info.version;
477 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
478 info.requiredSize += 8;
486Mp4Timings Mp4Track::computeTimings()
const
488 auto timings = Mp4Timings();
490 timings.tkhdCreationTime = m_rawTkhdCreationTime;
491 timings.tkhdModificationTime = m_rawTkhdModificationTime;
492 timings.tkhdDuration = m_rawTkhdDuration;
493 timings.mdhdCreationTime = m_rawMdhdCreationTime;
494 timings.mdhdModificationTime = m_rawMdhdModificationTime;
495 timings.mdhdDuration = m_rawMdhdDuration;
498 timings.tkhdModificationTime = timings.mdhdModificationTime
500 timings.tkhdDuration = timings.mdhdDuration =
static_cast<std::uint64_t
>(
m_duration.totalTicks() *
m_timeScale / TimeSpan::ticksPerSecond);
514 static const string context(
"reading sample to chunk table of MP4 track");
516 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
520 std::uint64_t actualTableSize = m_stscAtom->
dataSize();
521 if (actualTableSize < 20) {
522 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. There are no \"sample to chunk\" entries present.", context);
525 actualTableSize -= 8;
528 std::uint64_t calculatedTableSize = actualSampleToChunkEntryCount * 12;
529 if (calculatedTableSize < actualTableSize) {
530 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom stores more entries as denoted. The additional entries will be ignored.", context);
531 }
else if (calculatedTableSize > actualTableSize) {
532 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. It stores less entries as denoted.", context);
533 actualSampleToChunkEntryCount = actualTableSize / 12;
536 vector<tuple<std::uint32_t, std::uint32_t, std::uint32_t>> sampleToChunkTable;
537 sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
539 for (std::uint32_t i = 0; i < actualSampleToChunkEntryCount; ++i) {
541 std::uint32_t firstChunk =
reader().readUInt32BE();
542 std::uint32_t samplesPerChunk =
reader().readUInt32BE();
543 std::uint32_t sampleDescriptionIndex =
reader().readUInt32BE();
544 sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
546 return sampleToChunkTable;
563 static const string context(
"reading chunk sizes of MP4 track");
565 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
571 vector<std::uint64_t> chunkSizes;
572 if (!sampleToChunkTable.empty()) {
574 auto tableIterator = sampleToChunkTable.cbegin();
575 chunkSizes.reserve(m_chunkCount);
577 size_t sampleIndex = 0;
578 std::uint32_t previousChunkIndex = get<0>(*tableIterator);
579 if (previousChunkIndex != 1) {
580 diag.emplace_back(
DiagLevel::Critical,
"The first chunk of the first \"sample to chunk\" entry must be 1.", context);
581 previousChunkIndex = 1;
583 std::uint32_t samplesPerChunk = get<1>(*tableIterator);
586 for (
const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
587 std::uint32_t firstChunkIndex = get<0>(*tableIterator);
588 if (firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
589 addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
592 "The first chunk index of a \"sample to chunk\" entry must be greater than the first chunk of the previous entry and not "
593 "greater than the chunk count.",
597 previousChunkIndex = firstChunkIndex;
598 samplesPerChunk = get<1>(*tableIterator);
600 if (m_chunkCount >= previousChunkIndex) {
601 addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
614 static const string context(
"parsing MPEG-4 elementary stream descriptor");
615 using namespace Mpeg4ElementaryStreamObjectIds;
616 unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
620 if (
reader.readUInt32BE() != 0) {
634 esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
635 esInfo->id =
reader.readUInt16BE();
636 esInfo->esDescFlags =
reader.readByte();
637 if (esInfo->dependencyFlag()) {
638 esInfo->dependsOnId =
reader.readUInt16BE();
640 if (esInfo->urlFlag()) {
643 if (esInfo->ocrFlag()) {
644 esInfo->ocrId =
reader.readUInt16BE();
648 esDescChild; esDescChild = esDescChild->nextSibling()) {
649 esDescChild->parse(diag);
650 switch (esDescChild->id()) {
653 reader.stream()->seekg(
static_cast<streamoff
>(esDescChild->dataOffset()));
654 esInfo->objectTypeId =
reader.readByte();
655 esInfo->decCfgDescFlags =
reader.readByte();
656 esInfo->bufferSize =
reader.readUInt24BE();
657 esInfo->maxBitrate =
reader.readUInt32BE();
658 esInfo->averageBitrate =
reader.readUInt32BE();
660 decCfgDescChild = decCfgDescChild->
nextSibling()) {
661 decCfgDescChild->parse(diag);
662 switch (decCfgDescChild->id()) {
665 switch (esInfo->objectTypeId) {
667 case Mpeg2AacMainProfile:
668 case Mpeg2AacLowComplexityProfile:
669 case Mpeg2AacScaleableSamplingRateProfile:
672 esInfo->audioSpecificConfig
676 esInfo->videoSpecificConfig
691 diag.emplace_back(
DiagLevel::Critical,
"The MPEG-4 descriptor element structure is invalid.", context);
694 diag.emplace_back(
DiagLevel::Warning,
"Elementary stream descriptor atom (esds) is truncated.", context);
704 istream &stream, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
706 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
707 using namespace Mpeg4AudioObjectIds;
710 auto buff = make_unique<char[]>(
size);
711 stream.read(buff.get(),
static_cast<streamoff
>(
size));
712 BitReader bitReader(buff.get(),
size);
713 auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
716 auto getAudioObjectType = [&bitReader] {
717 std::uint8_t objType = bitReader.readBits<std::uint8_t>(5);
719 objType = 32 + bitReader.readBits<std::uint8_t>(6);
723 audioCfg->audioObjectType = getAudioObjectType();
725 if ((audioCfg->sampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
726 audioCfg->sampleFrequency = bitReader.readBits<std::uint32_t>(24);
729 audioCfg->channelConfiguration = bitReader.readBits<std::uint8_t>(4);
731 switch (audioCfg->audioObjectType) {
734 audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
735 audioCfg->sbrPresent =
true;
736 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
737 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
739 if ((audioCfg->audioObjectType = getAudioObjectType()) == ErBsac) {
740 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
744 switch (audioCfg->extensionAudioObjectType) {
746 audioCfg->psPresent =
true;
751 switch (audioCfg->audioObjectType) {
763 audioCfg->frameLengthFlag = bitReader.readBits<std::uint8_t>(1);
764 if ((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
765 audioCfg->coreCoderDelay = bitReader.readBits<std::uint8_t>(14);
767 audioCfg->extensionFlag = bitReader.readBit();
768 if (audioCfg->channelConfiguration == 0) {
771 switch (audioCfg->audioObjectType) {
774 audioCfg->layerNr = bitReader.readBits<std::uint8_t>(3);
778 if (audioCfg->extensionFlag == 1) {
779 switch (audioCfg->audioObjectType) {
781 audioCfg->numOfSubFrame = bitReader.readBits<std::uint8_t>(5);
782 audioCfg->layerLength = bitReader.readBits<std::uint16_t>(11);
788 audioCfg->resilienceFlags = bitReader.readBits<std::uint8_t>(3);
792 if (bitReader.readBit() == 1) {
801 switch (audioCfg->audioObjectType) {
813 switch (audioCfg->epConfig = bitReader.readBits<std::uint8_t>(2)) {
817 bitReader.skipBits(1);
824 if (audioCfg->extensionAudioObjectType != Sbr && audioCfg->extensionAudioObjectType != Ps && bitReader.bitsAvailable() >= 16) {
825 std::uint16_t syncExtensionType = bitReader.readBits<std::uint16_t>(11);
826 if (syncExtensionType == 0x2B7) {
827 if ((audioCfg->extensionAudioObjectType = getAudioObjectType()) == Sbr) {
828 if ((audioCfg->sbrPresent = bitReader.readBit())) {
829 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
830 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
832 if (bitReader.bitsAvailable() >= 12) {
833 if ((syncExtensionType = bitReader.readBits<std::uint16_t>(11)) == 0x548) {
834 audioCfg->psPresent = bitReader.readBits<std::uint8_t>(1);
838 }
else if (audioCfg->extensionAudioObjectType == ErBsac) {
839 if ((audioCfg->sbrPresent = bitReader.readBit())) {
840 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
841 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
844 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
846 }
else if (syncExtensionType == 0x548) {
847 audioCfg->psPresent = bitReader.readBit();
852 }
catch (
const std::ios_base::failure &) {
858 diag.emplace_back(
DiagLevel::Critical,
"Audio specific configuration is truncated.", context);
869 BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
871 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
872 using namespace Mpeg4AudioObjectIds;
873 auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
876 if (
size > 3 && (
reader.readUInt24BE() == 1)) {
881 switch (
reader.readByte()) {
884 videoCfg->profile =
reader.readByte();
894 if ((buff1 =
reader.readUInt24BE()) != 1) {
895 reader.stream()->seekg(-2, ios_base::cur);
896 videoCfg->userData.push_back(
static_cast<char>(buff1 >> 16));
903 if (buff1 != 1 &&
size > 0) {
904 videoCfg->userData +=
reader.readString(
size);
912 if (
reader.readUInt24BE() != 1) {
913 reader.stream()->seekg(-2, ios_base::cur);
922 diag.emplace_back(
DiagLevel::Critical,
"\"Visual Object Sequence Header\" not found.", context);
949 if (oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
952 static const unsigned int stcoDataBegin = 8;
953 std::uint64_t startPos = m_stcoAtom->
dataOffset() + stcoDataBegin;
954 std::uint64_t endPos = startPos + m_stcoAtom->
dataSize() - stcoDataBegin;
955 m_istream->seekg(
static_cast<streamoff
>(startPos));
956 m_ostream->seekp(
static_cast<streamoff
>(startPos));
957 vector<std::int64_t>::size_type i;
958 vector<std::int64_t>::size_type
size;
959 auto currentPos =
static_cast<std::uint64_t
>(
m_istream->tellg());
960 switch (m_stcoAtom->
id()) {
963 while ((currentPos + 4) <= endPos) {
965 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
966 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
967 off +=
static_cast<std::uint32_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
971 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
973 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
979 while ((currentPos + 8) <= endPos) {
981 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
982 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
983 off +=
static_cast<std::uint64_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
987 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
989 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
1020 switch (m_stcoAtom->
id()) {
1022 for (
auto offset : chunkOffsets) {
1023 m_writer.writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1027 for (
auto offset : chunkOffsets) {
1057 writer().writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1060 writer().writeUInt64BE(offset);
1106 CPP_UTILITIES_UNUSED(av1Config)
1107 CPP_UTILITIES_UNUSED(track)
1119 CPP_UTILITIES_UNUSED(diag)
1124 for (
Mp4Atom *trakChild = m_trakAtom->
firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1128 trakChild->makeBuffer();
1132 childAtom->makeBuffer();
1142 CPP_UTILITIES_UNUSED(diag)
1144 const auto &info = verifyPresentTrackHeader();
1147 std::uint64_t
size = 8;
1149 size += info.requiredSize;
1151 for (
Mp4Atom *trakChild = m_trakAtom->
firstChild(); trakChild; trakChild = trakChild->nextSibling()) {
1155 size += trakChild->totalSize();
1158 if (info.timingsVersion == 0) {
1168 bool dinfAtomWritten =
false;
1172 dinfAtomWritten =
true;
1174 size += childAtom->totalSize();
1177 if (!dinfAtomWritten) {
1195 ostream::pos_type trakStartOffset =
outputStream().tellp();
1207 trakChild->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1224 const auto &info = verifyPresentTrackHeader();
1227 if (info.versionUnknown) {
1229 argsToString(
"The version of the present \"tkhd\"-atom (", info.version,
") is unknown. Assuming version 1."),
1230 argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1232 if (info.truncated) {
1234 DiagLevel::Critical, argsToString(
"The present \"tkhd\"-atom is truncated."), argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1238 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
1239 writer().writeUInt32BE(1);
1241 writer().writeUInt64BE(info.requiredSize);
1243 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.requiredSize));
1248 writer().writeByte(info.writeVersion);
1249 std::uint32_t
flags = 0;
1262 if (info.writeVersion != 0) {
1263 writer().writeUInt64BE(info.timings.tkhdCreationTime);
1264 writer().writeUInt64BE(info.timings.tkhdModificationTime);
1266 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdCreationTime));
1267 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdModificationTime));
1271 writer().writeUInt32BE(
static_cast<std::uint32_t
>(
m_id));
1272 writer().writeUInt32BE(0);
1273 if (info.writeVersion != 0) {
1274 writer().writeUInt64BE(info.timings.tkhdDuration);
1276 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdDuration));
1278 writer().writeUInt32BE(0);
1279 writer().writeUInt32BE(0);
1282 if (info.canUseExisting) {
1285 static_cast<streamoff
>(m_tkhdAtom->
dataSize() - info.additionalDataOffset));
1287 if (info.discardBuffer) {
1292 diag.emplace_back(
DiagLevel::Warning,
"Writing some default values because the existing tkhd atom is truncated.",
"making tkhd atom");
1293 writer().writeInt16BE(0);
1294 writer().writeInt16BE(0);
1295 writer().writeFixed8BE(1.0);
1296 writer().writeUInt16BE(0);
1297 for (
const std::int32_t value : { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }) {
1298 writer().writeInt32BE(value);
1300 writer().writeFixed16BE(1.0);
1301 writer().writeFixed16BE(1.0);
1311 ostream::pos_type mdiaStartOffset =
outputStream().tellp();
1312 writer().writeUInt32BE(0);
1315 const auto &info = verifyPresentTrackHeader();
1316 const auto &timings = info.timings;
1317 const auto timingsVersion = timings.requiredMdhdVersion();
1318 writer().writeUInt32BE(timingsVersion != 0 ? 44 : 32);
1320 writer().writeByte(timingsVersion);
1321 writer().writeUInt24BE(0);
1322 if (timingsVersion != 0) {
1323 writer().writeUInt64BE(timings.mdhdCreationTime);
1324 writer().writeUInt64BE(timings.mdhdModificationTime);
1326 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdCreationTime));
1327 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdModificationTime));
1330 if (timingsVersion != 0) {
1331 writer().writeUInt64BE(timings.mdhdDuration);
1333 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdDuration));
1338 for (
const auto &detail :
m_locale) {
1344 auto codedLanguage =
static_cast<std::uint16_t
>(0u);
1345 for (
auto charIndex =
static_cast<std::size_t
>(0); charIndex != 3; ++charIndex) {
1346 const char langChar = charIndex < language->size() ? (*language)[charIndex] : 0;
1347 if (langChar >=
'a' && langChar <=
'z') {
1348 codedLanguage |=
static_cast<std::uint16_t
>((langChar - 0x60) << (0xA - charIndex * 0x5));
1353 if (language->empty()) {
1358 diag.emplace_back(
DiagLevel::Warning,
"Assigned language \"" % *language +
"\" is of an invalid format. Setting language to undefined.",
1359 "making mdhd atom");
1360 codedLanguage = 0x55C4;
1363 if (language->size() > 3) {
1365 DiagLevel::Warning,
"Assigned language \"" % *language +
"\" is longer than 3 byte and hence will be truncated.",
"making mdhd atom");
1367 writer().writeUInt16BE(codedLanguage);
1368 writer().writeUInt16BE(0);
1370 writer().writeUInt32BE(33 +
static_cast<std::uint32_t
>(
m_name.size()));
1372 writer().writeUInt64BE(0);
1391 diag.emplace_back(
DiagLevel::Critical,
"Media type is invalid; keeping media type as-is.",
"making hdlr atom");
1393 writer().writeUInt32BE(m_rawMediaType);
1396 for (
int i = 0; i < 3; ++i)
1397 writer().writeUInt32BE(0);
1411 ostream::pos_type minfStartOffset =
outputStream().tellp();
1412 writer().writeUInt32BE(0);
1414 bool dinfAtomWritten =
false;
1417 for (
Mp4Atom *childAtom = m_minfAtom->
firstChild(); childAtom; childAtom = childAtom->nextSibling()) {
1422 dinfAtomWritten =
true;
1424 childAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1428 if (!dinfAtomWritten) {
1429 writer().writeUInt32BE(36);
1432 writer().writeUInt32BE(28);
1434 writer().writeUInt32BE(0);
1435 writer().writeUInt32BE(1);
1437 writer().writeUInt32BE(12);
1440 writer().writeUInt24BE(0x000001);
1444 bool stblAtomWritten =
false;
1447 stblAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1448 stblAtomWritten =
true;
1451 if (!stblAtomWritten) {
1453 "Source track does not contain mandatory stbl atom and the tagparser lib is unable to make one from scratch.",
"making stbl atom");
1468 writer().writeUInt32BE(0);
1476 diag.emplace_back(
DiagLevel::Critical,
"Unable to make stsd atom from scratch.",
"making stsd atom");
1485 diag.emplace_back(
DiagLevel::Critical,
"Unable to make stts atom from scratch.",
"making stts atom");
1526 CPP_UTILITIES_UNUSED(progress)
1528 static const string context(
"parsing MP4 track");
1529 using namespace Mp4AtomIds;
1537 if (!(m_tkhdAtom = m_trakAtom->
childById(TrackHeader, diag))) {
1541 if (!(m_mdiaAtom = m_trakAtom->
childById(Media, diag))) {
1545 if (!(m_mdhdAtom = m_mdiaAtom->
childById(MediaHeader, diag))) {
1549 if (!(m_hdlrAtom = m_mdiaAtom->
childById(HandlerReference, diag))) {
1553 if (!(m_minfAtom = m_mdiaAtom->
childById(MediaInformation, diag))) {
1557 if (!(m_stblAtom = m_minfAtom->
childById(SampleTable, diag))) {
1561 if (!(m_stsdAtom = m_stblAtom->
childById(SampleDescription, diag))) {
1565 if (!(m_stcoAtom = m_stblAtom->
childById(ChunkOffset, diag)) && !(m_stcoAtom = m_stblAtom->
childById(ChunkOffset64, diag))) {
1569 if (!(m_stscAtom = m_stblAtom->
childById(SampleToChunk, diag))) {
1573 if (!(m_stszAtom = m_stblAtom->
childById(SampleSize, diag)) && !(m_stszAtom = m_stblAtom->
childById(CompactSampleSize, diag))) {
1586 auto atomVersion =
reader.readByte();
1591 switch (atomVersion) {
1593 m_rawTkhdCreationTime =
reader.readUInt32BE();
1594 m_rawTkhdModificationTime =
reader.readUInt32BE();
1596 m_istream->seekg(4, std::ios_base::cur);
1597 m_rawTkhdDuration =
reader.readUInt32BE();
1600 m_rawTkhdCreationTime =
reader.readUInt64BE();
1601 m_rawTkhdModificationTime =
reader.readUInt64BE();
1603 m_istream->seekg(4, std::ios_base::cur);
1604 m_rawTkhdDuration =
reader.readUInt64BE();
1608 "Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
1610 m_rawTkhdCreationTime = m_rawTkhdModificationTime = m_rawTkhdDuration = 0;
1618 atomVersion =
reader.readByte();
1620 switch (atomVersion) {
1622 m_rawMdhdCreationTime =
reader.readUInt32BE();
1623 m_rawMdhdModificationTime =
reader.readUInt32BE();
1625 m_rawMdhdDuration =
reader.readUInt32BE();
1628 m_rawMdhdCreationTime =
reader.readUInt64BE();
1629 m_rawMdhdModificationTime =
reader.readUInt64BE();
1631 m_rawMdhdDuration =
reader.readUInt64BE();
1635 "Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
1638 m_rawMdhdCreationTime = m_rawMdhdModificationTime = m_rawMdhdDuration = 0;
1644 m_duration = TimeSpan::fromSeconds(
static_cast<TimeSpan::TickType
>(m_rawMdhdDuration)) /
static_cast<TimeSpan::TickType
>(
m_timeScale);
1646 std::uint16_t tmp =
reader.readUInt16BE();
1648 const char buff[] = {
1649 static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1650 static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1651 static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1662 switch (m_rawMediaType =
reader.readUInt32BE()) {
1683 if (
static_cast<std::uint64_t
>(tmp =
static_cast<std::uint8_t
>(
m_istream->peek())) == m_hdlrAtom->
dataSize() - 12 - 4 - 8 - 1) {
1695 m_chunkCount =
reader.readUInt32BE();
1699 const auto entryCount =
reader.readUInt32BE();
1700 Mp4Atom *esDescParentAtom =
nullptr;
1703 for (
Mp4Atom *codecConfigContainerAtom = m_stsdAtom->
firstChild(); codecConfigContainerAtom;
1704 codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
1705 codecConfigContainerAtom->
parse(diag);
1708 m_formatId = interpretIntegerAsString<std::uint32_t>(codecConfigContainerAtom->id());
1712 m_istream->seekg(
static_cast<streamoff
>(codecConfigContainerAtom->dataOffset()));
1713 switch (codecConfigContainerAtom->id()) {
1729 tmp =
reader.readUInt16BE();
1745 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1748 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1751 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1753 if (!esDescParentAtom) {
1754 esDescParentAtom = codecConfigContainerAtom;
1771 m_istream->seekg(6 + 2 + 16, ios_base::cur);
1777 m_framesPerSample =
reader.readUInt16BE();
1782 }
else if (tmp < 32) {
1786 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1787 if (!esDescParentAtom) {
1788 esDescParentAtom = codecConfigContainerAtom;
1793 codecConfigContainerAtom->
denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1794 if (!esDescParentAtom) {
1795 esDescParentAtom = codecConfigContainerAtom;
1806 if (esDescParentAtom) {
1809 m_istream->seekg(
static_cast<streamoff
>(avcConfigAtom->dataOffset()));
1810 m_avcConfig = make_unique<TagParser::AvcConfiguration>();
1812 m_avcConfig->parse(
reader, avcConfigAtom->dataSize(), diag);
1823 m_istream->seekg(
static_cast<streamoff
>(av1ConfigAtom->dataOffset()));
1824 m_av1Config = make_unique<TagParser::Av1Configuration>();
1826 m_av1Config->parse(
reader, av1ConfigAtom->dataSize(), diag);
1846 m_bitrate =
static_cast<double>(m_esInfo->averageBitrate) / 1000;
1847 m_maxBitrate =
static_cast<double>(m_esInfo->maxBitrate) / 1000;
1848 if (m_esInfo->audioSpecificConfig) {
1851 m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
1852 if (m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1857 diag.emplace_back(
DiagLevel::Warning,
"Audio specific config has invalid sample frequency index.", context);
1859 if (m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1866 DiagLevel::Warning,
"Audio specific config has invalid extension sample frequency index.", context);
1868 m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1871 if (m_esInfo->videoSpecificConfig) {
1874 m_format.
sub = m_esInfo->videoSpecificConfig->profile;
1875 if (!m_esInfo->videoSpecificConfig->userData.empty()) {
1877 m_formatId += m_esInfo->videoSpecificConfig->userData;
1887 m_istream->seekg(
static_cast<streamoff
>(m_chunkOffsetSize == 8 ?
reader.readUInt64BE() :
reader.readUInt32BE()));
1900 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse child atoms of \"stsd\"-atom.", context);
1905 m_sampleSizes.clear();
1907 std::uint64_t actualSampleSizeTableSize = m_stszAtom->
dataSize();
1908 if (actualSampleSizeTableSize < 12) {
1910 "The stsz atom is truncated. There are no sample sizes present. The size of the track can not be determined.", context);
1912 actualSampleSizeTableSize -= 12;
1914 std::uint32_t fieldSize;
1915 std::uint32_t constantSize;
1919 fieldSize =
reader.readByte();
1922 constantSize =
reader.readUInt32BE();
1927 m_sampleSizes.push_back(constantSize);
1931 const auto calculatedSampleSizeTableSize
1932 =
static_cast<std::uint64_t
>(std::ceil((0.125 * fieldSize) *
static_cast<double>(
m_sampleCount)));
1933 if (calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1935 DiagLevel::Critical,
"The stsz atom stores more entries as denoted. The additional entries will be ignored.", context);
1936 }
else if (calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1937 diag.emplace_back(
DiagLevel::Critical,
"The stsz atom is truncated. It stores less entries as denoted.", context);
1938 actualSampleCount =
static_cast<std::uint64_t
>(floor(
static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize)));
1940 m_sampleSizes.reserve(actualSampleCount);
1941 std::uint32_t i = 1;
1942 switch (fieldSize) {
1944 for (; i <= actualSampleCount; i += 2) {
1945 std::uint8_t val =
reader.readByte();
1946 m_sampleSizes.push_back(val >> 4);
1947 m_sampleSizes.push_back(val & 0xF0);
1948 m_size += (val >> 4) + (val & 0xF0);
1950 if (i <= actualSampleCount + 1) {
1951 m_sampleSizes.push_back(
reader.readByte() >> 4);
1952 m_size += m_sampleSizes.back();
1956 for (; i <= actualSampleCount; ++i) {
1957 m_sampleSizes.push_back(
reader.readByte());
1958 m_size += m_sampleSizes.back();
1962 for (; i <= actualSampleCount; ++i) {
1963 m_sampleSizes.push_back(
reader.readUInt16BE());
1964 m_size += m_sampleSizes.back();
1968 for (; i <= actualSampleCount; ++i) {
1969 m_sampleSizes.push_back(
reader.readUInt32BE());
1970 m_size += m_sampleSizes.back();
1975 "The fieldsize used to store the sample sizes is not supported. The sample count and size of the track can not be determined.",
1982 std::uint64_t totalDuration = 0;
1983 for (
Mp4Atom *moofAtom = m_trakAtom->
container().firstElement()->siblingByIdIncludingThis(MovieFragment, diag); moofAtom;
1984 moofAtom = moofAtom->siblingById(MovieFragment, diag)) {
1985 moofAtom->parse(diag);
1986 for (
Mp4Atom *trafAtom = moofAtom->
childById(TrackFragment, diag); trafAtom; trafAtom = trafAtom->
siblingById(TrackFragment, diag)) {
1987 trafAtom->parse(diag);
1988 for (
Mp4Atom *tfhdAtom = trafAtom->
childById(TrackFragmentHeader, diag); tfhdAtom;
1989 tfhdAtom = tfhdAtom->
siblingById(TrackFragmentHeader, diag)) {
1990 tfhdAtom->parse(diag);
1991 std::uint32_t calculatedDataSize = 0;
1992 if (tfhdAtom->dataSize() < calculatedDataSize) {
1995 m_istream->seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
1996 std::uint32_t tfhdFlags =
reader.readUInt24BE();
1998 if (tfhdFlags & 0x000001) {
1999 calculatedDataSize += 8;
2001 if (tfhdFlags & 0x000002) {
2002 calculatedDataSize += 4;
2004 if (tfhdFlags & 0x000008) {
2005 calculatedDataSize += 4;
2007 if (tfhdFlags & 0x000010) {
2008 calculatedDataSize += 4;
2010 if (tfhdFlags & 0x000020) {
2011 calculatedDataSize += 4;
2015 std::uint32_t defaultSampleDuration = 0;
2016 std::uint32_t defaultSampleSize = 0;
2018 if (tfhdAtom->dataSize() < calculatedDataSize) {
2019 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
2021 if (tfhdFlags & 0x000001) {
2025 if (tfhdFlags & 0x000002) {
2029 if (tfhdFlags & 0x000008) {
2030 defaultSampleDuration =
reader.readUInt32BE();
2033 if (tfhdFlags & 0x000010) {
2034 defaultSampleSize =
reader.readUInt32BE();
2036 if (tfhdFlags & 0x000020) {
2041 for (
Mp4Atom *trunAtom = trafAtom->
childById(TrackFragmentRun, diag); trunAtom;
2042 trunAtom = trunAtom->
siblingById(TrackFragmentRun, diag)) {
2043 std::uint32_t trunCalculatedDataSize = 8;
2044 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2047 m_istream->seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
2048 std::uint32_t trunFlags =
reader.readUInt24BE();
2051 if (trunFlags & 0x000001) {
2052 trunCalculatedDataSize += 4;
2054 if (trunFlags & 0x000004) {
2055 trunCalculatedDataSize += 4;
2057 std::uint32_t entrySize = 0;
2058 if (trunFlags & 0x000100) {
2061 if (trunFlags & 0x000200) {
2064 if (trunFlags & 0x000400) {
2067 if (trunFlags & 0x000800) {
2070 trunCalculatedDataSize += entrySize *
sampleCount;
2071 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2072 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
2074 if (trunFlags & 0x000001) {
2078 if (trunFlags & 0x000004) {
2082 if (trunFlags & 0x000100) {
2083 totalDuration +=
reader.readUInt32BE();
2085 totalDuration += defaultSampleDuration;
2087 if (trunFlags & 0x000200) {
2088 m_sampleSizes.push_back(
reader.readUInt32BE());
2089 m_size += m_sampleSizes.back();
2091 m_size += defaultSampleSize;
2093 if (trunFlags & 0x000400) {
2096 if (trunFlags & 0x000800) {
2103 if (m_sampleSizes.empty() && defaultSampleSize) {
2104 m_sampleSizes.push_back(defaultSampleSize);
2119 m_duration = TimeSpan::fromSeconds(
static_cast<double>(totalDuration) /
static_cast<double>(
timeScale));
2130 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