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)
155 , m_trakAtom(&trakAtom)
156 , m_tkhdAtom(nullptr)
157 , m_mdiaAtom(nullptr)
158 , m_mdhdAtom(nullptr)
159 , m_hdlrAtom(nullptr)
160 , m_minfAtom(nullptr)
161 , m_stblAtom(nullptr)
162 , m_stsdAtom(nullptr)
163 , m_stscAtom(nullptr)
164 , m_stcoAtom(nullptr)
165 , m_stszAtom(nullptr)
167 , m_framesPerSample(1)
168 , m_chunkOffsetSize(4)
170 , m_sampleToChunkEntryCount(0)
171 , m_rawTkhdCreationTime(0)
172 , m_rawMdhdCreationTime(0)
173 , m_rawTkhdModificationTime(0)
174 , m_rawMdhdModificationTime(0)
175 , m_rawTkhdDuration(0)
176 , m_rawMdhdDuration(0)
204 static const auto context = std::string(
"reading chunk offset table of MP4 track");
209 auto offsets = std::vector<std::uint64_t>();
213 auto actualTableSize = m_stcoAtom->
dataSize();
214 if (actualTableSize < (8 + offsetSize)) {
215 diag.emplace_back(
DiagLevel::Critical,
"The stco/co64 atom is truncated. There are no chunk offsets present.", context);
218 actualTableSize -= 8;
220 std::uint32_t actualChunkCount =
chunkCount();
221 std::uint64_t calculatedTableSize =
chunkCount() * offsetSize;
222 if (calculatedTableSize < actualTableSize) {
224 "The stco/co64 atom stores more chunk offsets as denoted. The additional chunk offsets will be ignored.", context);
225 }
else if (calculatedTableSize > actualTableSize) {
226 diag.emplace_back(
DiagLevel::Critical,
"The stco/co64 atom is truncated. It stores less chunk offsets as denoted.", context);
227 actualChunkCount =
static_cast<std::uint32_t
>(std::floor(
static_cast<double>(actualTableSize) /
static_cast<double>(offsetSize)));
230 offsets.reserve(actualChunkCount);
232 switch (offsetSize) {
234 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
235 offsets.push_back(
reader().readUInt32BE());
239 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
240 offsets.push_back(
reader().readUInt64BE());
246 if (parseFragments) {
250 moofAtom->parse(diag);
253 trafAtom->parse(diag);
256 tfhdAtom->parse(diag);
257 std::uint32_t calculatedDataSize = 0;
258 if (tfhdAtom->dataSize() < calculatedDataSize) {
261 inputStream().seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
262 const std::uint32_t
flags =
reader().readUInt24BE();
264 if (
flags & 0x000001) {
265 calculatedDataSize += 8;
267 if (
flags & 0x000002) {
268 calculatedDataSize += 4;
270 if (
flags & 0x000008) {
271 calculatedDataSize += 4;
273 if (
flags & 0x000010) {
274 calculatedDataSize += 4;
276 if (
flags & 0x000020) {
277 calculatedDataSize += 4;
283 std::uint32_t defaultSampleSize = 0;
285 if (tfhdAtom->dataSize() < calculatedDataSize) {
286 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
288 if (
flags & 0x000001) {
292 if (
flags & 0x000002) {
296 if (
flags & 0x000008) {
300 if (
flags & 0x000010) {
301 defaultSampleSize =
reader().readUInt32BE();
303 if (
flags & 0x000020) {
310 std::uint32_t trunCalculatedDataSize = 8;
311 if (trunAtom->dataSize() < trunCalculatedDataSize) {
314 inputStream().seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
315 std::uint32_t trunFlags =
reader().readUInt24BE();
318 if (trunFlags & 0x000001) {
319 trunCalculatedDataSize += 4;
321 if (trunFlags & 0x000004) {
322 trunCalculatedDataSize += 4;
324 std::uint32_t entrySize = 0;
325 if (trunFlags & 0x000100) {
328 if (trunFlags & 0x000200) {
331 if (trunFlags & 0x000400) {
334 if (trunFlags & 0x000800) {
338 if (trunAtom->dataSize() < trunCalculatedDataSize) {
339 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
341 if (trunFlags & 0x000001) {
345 if (trunFlags & 0x000004) {
349 if (trunFlags & 0x000100) {
355 if (trunFlags & 0x000200) {
356 m_sampleSizes.push_back(
reader().readUInt32BE());
357 m_size += m_sampleSizes.back();
359 m_size += defaultSampleSize;
361 if (trunFlags & 0x000400) {
364 if (trunFlags & 0x000800) {
371 if (m_sampleSizes.empty() && defaultSampleSize) {
372 m_sampleSizes.push_back(defaultSampleSize);
387std::uint64_t Mp4Track::accumulateSampleSizes(
size_t &sampleIndex,
size_t count,
Diagnostics &diag)
389 if (sampleIndex + count <= m_sampleSizes.size()) {
390 std::uint64_t sum = 0;
391 for (
size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
392 sum += m_sampleSizes[sampleIndex];
395 }
else if (m_sampleSizes.size() == 1) {
396 sampleIndex += count;
397 return static_cast<std::uint64_t
>(m_sampleSizes.front()) * count;
399 diag.emplace_back(
DiagLevel::Critical,
"There are not as many sample size entries as samples.",
"reading chunk sizes of MP4 track");
400 throw InvalidDataException();
412void Mp4Track::addChunkSizeEntries(
413 std::vector<std::uint64_t> &chunkSizeTable,
size_t count,
size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag)
415 for (
size_t i = 0; i < count; ++i) {
416 chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex,
sampleCount, diag));
424const TrackHeaderInfo &Mp4Track::verifyPresentTrackHeader()
const
426 if (m_trackHeaderInfo) {
427 return *m_trackHeaderInfo;
431 auto &info = *(m_trackHeaderInfo = std::make_unique<TrackHeaderInfo>());
437 info.discardBuffer = m_tkhdAtom->
buffer() ==
nullptr;
438 if (info.discardBuffer) {
443 switch (info.version =
static_cast<std::uint8_t
>(m_tkhdAtom->
buffer()[m_tkhdAtom->
headerSize()])) {
445 info.additionalDataOffset = 32;
448 info.additionalDataOffset = 44;
451 info.additionalDataOffset = 44;
452 info.versionUnknown =
true;
456 if (info.additionalDataOffset + 48u <= m_tkhdAtom->
dataSize()) {
457 info.canUseExisting =
true;
459 info.truncated =
true;
460 info.canUseExisting = info.additionalDataOffset < m_tkhdAtom->
dataSize();
461 if (!info.canUseExisting && info.discardBuffer) {
467 info.requiredSize = m_tkhdAtom->
dataSize() + 8;
468 info.timings = computeTimings();
469 info.timingsVersion = info.timings.requiredTkhdVersion();
470 if (info.version == 0) {
471 info.writeVersion = info.timingsVersion;
473 if (info.writeVersion != 0) {
474 info.requiredSize += 12;
477 info.writeVersion = info.version;
480 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
481 info.requiredSize += 8;
489Mp4Timings Mp4Track::computeTimings()
const
491 auto timings = Mp4Timings();
493 timings.tkhdCreationTime = m_rawTkhdCreationTime;
494 timings.tkhdModificationTime = m_rawTkhdModificationTime;
495 timings.tkhdDuration = m_rawTkhdDuration;
496 timings.mdhdCreationTime = m_rawMdhdCreationTime;
497 timings.mdhdModificationTime = m_rawMdhdModificationTime;
498 timings.mdhdDuration = m_rawMdhdDuration;
501 timings.tkhdModificationTime = timings.mdhdModificationTime
503 timings.tkhdDuration = timings.mdhdDuration =
static_cast<std::uint64_t
>(
m_duration.totalTicks() *
m_timeScale / TimeSpan::ticksPerSecond);
517 static const string context(
"reading sample to chunk table of MP4 track");
519 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
523 std::uint64_t actualTableSize = m_stscAtom->
dataSize();
524 if (actualTableSize < 20) {
525 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. There are no \"sample to chunk\" entries present.", context);
528 actualTableSize -= 8;
531 std::uint64_t calculatedTableSize = actualSampleToChunkEntryCount * 12;
532 if (calculatedTableSize < actualTableSize) {
533 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom stores more entries as denoted. The additional entries will be ignored.", context);
534 }
else if (calculatedTableSize > actualTableSize) {
535 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. It stores less entries as denoted.", context);
536 actualSampleToChunkEntryCount = actualTableSize / 12;
539 vector<tuple<std::uint32_t, std::uint32_t, std::uint32_t>> sampleToChunkTable;
540 sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
542 for (std::uint32_t i = 0; i < actualSampleToChunkEntryCount; ++i) {
544 std::uint32_t firstChunk =
reader().readUInt32BE();
545 std::uint32_t samplesPerChunk =
reader().readUInt32BE();
546 std::uint32_t sampleDescriptionIndex =
reader().readUInt32BE();
547 sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
549 return sampleToChunkTable;
566 static const string context(
"reading chunk sizes of MP4 track");
568 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
574 vector<std::uint64_t> chunkSizes;
575 if (!sampleToChunkTable.empty()) {
577 auto tableIterator = sampleToChunkTable.cbegin();
578 chunkSizes.reserve(m_chunkCount);
580 size_t sampleIndex = 0;
581 std::uint32_t previousChunkIndex = get<0>(*tableIterator);
582 if (previousChunkIndex != 1) {
583 diag.emplace_back(
DiagLevel::Critical,
"The first chunk of the first \"sample to chunk\" entry must be 1.", context);
584 previousChunkIndex = 1;
586 std::uint32_t samplesPerChunk = get<1>(*tableIterator);
589 for (
const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
590 std::uint32_t firstChunkIndex = get<0>(*tableIterator);
591 if (firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
592 addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
595 "The first chunk index of a \"sample to chunk\" entry must be greater than the first chunk of the previous entry and not "
596 "greater than the chunk count.",
600 previousChunkIndex = firstChunkIndex;
601 samplesPerChunk = get<1>(*tableIterator);
603 if (m_chunkCount >= previousChunkIndex) {
604 addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
617 static const string context(
"parsing MPEG-4 elementary stream descriptor");
618 using namespace Mpeg4ElementaryStreamObjectIds;
619 unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
623 if (
reader.readUInt32BE() != 0) {
637 esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
638 esInfo->id =
reader.readUInt16BE();
639 esInfo->esDescFlags =
reader.readByte();
640 if (esInfo->dependencyFlag()) {
641 esInfo->dependsOnId =
reader.readUInt16BE();
643 if (esInfo->urlFlag()) {
646 if (esInfo->ocrFlag()) {
647 esInfo->ocrId =
reader.readUInt16BE();
651 esDescChild; esDescChild = esDescChild->
nextSibling()) {
652 esDescChild->parse(diag);
653 switch (esDescChild->id()) {
656 reader.stream()->seekg(
static_cast<streamoff
>(esDescChild->dataOffset()));
657 esInfo->objectTypeId =
reader.readByte();
658 esInfo->decCfgDescFlags =
reader.readByte();
659 esInfo->bufferSize =
reader.readUInt24BE();
660 esInfo->maxBitrate =
reader.readUInt32BE();
661 esInfo->averageBitrate =
reader.readUInt32BE();
663 decCfgDescChild = decCfgDescChild->
nextSibling()) {
664 decCfgDescChild->parse(diag);
665 switch (decCfgDescChild->id()) {
668 switch (esInfo->objectTypeId) {
670 case Mpeg2AacMainProfile:
671 case Mpeg2AacLowComplexityProfile:
672 case Mpeg2AacScaleableSamplingRateProfile:
675 esInfo->audioSpecificConfig
679 esInfo->videoSpecificConfig
694 diag.emplace_back(
DiagLevel::Critical,
"The MPEG-4 descriptor element structure is invalid.", context);
697 diag.emplace_back(
DiagLevel::Warning,
"Elementary stream descriptor atom (esds) is truncated.", context);
707 istream &stream, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
709 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
710 using namespace Mpeg4AudioObjectIds;
713 auto buff = make_unique<char[]>(
size);
714 stream.read(buff.get(),
static_cast<streamoff
>(
size));
715 BitReader bitReader(buff.get(),
size);
716 auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
719 auto getAudioObjectType = [&bitReader] {
720 std::uint8_t objType = bitReader.readBits<std::uint8_t>(5);
722 objType = 32 + bitReader.readBits<std::uint8_t>(6);
726 audioCfg->audioObjectType = getAudioObjectType();
728 if ((audioCfg->sampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
729 audioCfg->sampleFrequency = bitReader.readBits<std::uint32_t>(24);
732 audioCfg->channelConfiguration = bitReader.readBits<std::uint8_t>(4);
734 switch (audioCfg->audioObjectType) {
737 audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
738 audioCfg->sbrPresent =
true;
739 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
740 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
742 if ((audioCfg->audioObjectType = getAudioObjectType()) == ErBsac) {
743 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
747 switch (audioCfg->extensionAudioObjectType) {
749 audioCfg->psPresent =
true;
754 switch (audioCfg->audioObjectType) {
766 audioCfg->frameLengthFlag = bitReader.readBits<std::uint8_t>(1);
767 if ((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
768 audioCfg->coreCoderDelay = bitReader.readBits<std::uint8_t>(14);
770 audioCfg->extensionFlag = bitReader.readBit();
771 if (audioCfg->channelConfiguration == 0) {
774 switch (audioCfg->audioObjectType) {
777 audioCfg->layerNr = bitReader.readBits<std::uint8_t>(3);
781 if (audioCfg->extensionFlag == 1) {
782 switch (audioCfg->audioObjectType) {
784 audioCfg->numOfSubFrame = bitReader.readBits<std::uint8_t>(5);
785 audioCfg->layerLength = bitReader.readBits<std::uint16_t>(11);
791 audioCfg->resilienceFlags = bitReader.readBits<std::uint8_t>(3);
795 if (bitReader.readBit() == 1) {
804 switch (audioCfg->audioObjectType) {
816 switch (audioCfg->epConfig = bitReader.readBits<std::uint8_t>(2)) {
820 bitReader.skipBits(1);
827 if (audioCfg->extensionAudioObjectType != Sbr && audioCfg->extensionAudioObjectType != Ps && bitReader.bitsAvailable() >= 16) {
828 std::uint16_t syncExtensionType = bitReader.readBits<std::uint16_t>(11);
829 if (syncExtensionType == 0x2B7) {
830 if ((audioCfg->extensionAudioObjectType = getAudioObjectType()) == Sbr) {
831 if ((audioCfg->sbrPresent = bitReader.readBit())) {
832 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
833 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
835 if (bitReader.bitsAvailable() >= 12) {
836 if ((syncExtensionType = bitReader.readBits<std::uint16_t>(11)) == 0x548) {
837 audioCfg->psPresent = bitReader.readBits<std::uint8_t>(1);
841 }
else if (audioCfg->extensionAudioObjectType == ErBsac) {
842 if ((audioCfg->sbrPresent = bitReader.readBit())) {
843 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
844 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
847 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
849 }
else if (syncExtensionType == 0x548) {
850 audioCfg->psPresent = bitReader.readBit();
855 }
catch (
const std::ios_base::failure &) {
861 diag.emplace_back(
DiagLevel::Critical,
"Audio specific configuration is truncated.", context);
872 BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
874 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
875 using namespace Mpeg4AudioObjectIds;
876 auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
879 if (
size > 3 && (
reader.readUInt24BE() == 1)) {
884 switch (
reader.readByte()) {
887 videoCfg->profile =
reader.readByte();
897 if ((buff1 =
reader.readUInt24BE()) != 1) {
898 reader.stream()->seekg(-2, ios_base::cur);
899 videoCfg->userData.push_back(
static_cast<char>(buff1 >> 16));
906 if (buff1 != 1 &&
size > 0) {
907 videoCfg->userData +=
reader.readString(
size);
915 if (
reader.readUInt24BE() != 1) {
916 reader.stream()->seekg(-2, ios_base::cur);
925 diag.emplace_back(
DiagLevel::Critical,
"\"Visual Object Sequence Header\" not found.", context);
952 if (oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
955 static const unsigned int stcoDataBegin = 8;
956 std::uint64_t startPos = m_stcoAtom->
dataOffset() + stcoDataBegin;
957 std::uint64_t endPos = startPos + m_stcoAtom->
dataSize() - stcoDataBegin;
958 m_istream->seekg(
static_cast<streamoff
>(startPos));
959 m_ostream->seekp(
static_cast<streamoff
>(startPos));
960 vector<std::int64_t>::size_type i;
961 vector<std::int64_t>::size_type
size;
962 auto currentPos =
static_cast<std::uint64_t
>(
m_istream->tellg());
963 switch (m_stcoAtom->
id()) {
966 while ((currentPos + 4) <= endPos) {
968 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
969 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
970 off +=
static_cast<std::uint32_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
974 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
976 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
982 while ((currentPos + 8) <= endPos) {
984 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
985 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
986 off +=
static_cast<std::uint64_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
990 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
992 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
1023 switch (m_stcoAtom->
id()) {
1025 for (
auto offset : chunkOffsets) {
1026 m_writer.writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1030 for (
auto offset : chunkOffsets) {
1058 m_ostream->seekp(
static_cast<streamoff
>(m_stcoAtom->
dataOffset() + 8 + offsetSize * chunkIndex));
1059 switch (m_stcoAtom->
id()) {
1061 writer().writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1064 writer().writeUInt64BE(offset);
1110 CPP_UTILITIES_UNUSED(av1Config)
1111 CPP_UTILITIES_UNUSED(track)
1123 CPP_UTILITIES_UNUSED(diag)
1132 trakChild->makeBuffer();
1139 childAtom->makeBuffer();
1144 childAtom->makeBuffer();
1154 CPP_UTILITIES_UNUSED(diag)
1165std::tuple<std::uint64_t, std::uint64_t> Mp4Track::calculateSampleTableSize(
Diagnostics &diag)
const
1167 auto stblSize = std::uint64_t();
1168 auto stcoSize = std::uint64_t();
1169 auto writeChunkOffsetTableManually =
false;
1172 switch (stblChildAtom->id()) {
1174 if (m_chunkOffsetSize != 4) {
1175 writeChunkOffsetTableManually =
true;
1180 if (m_chunkOffsetSize != 8) {
1181 writeChunkOffsetTableManually =
true;
1186 stblSize += stblChildAtom->totalSize();
1189 if (writeChunkOffsetTableManually) {
1193 return std::make_tuple(stblSize, stcoSize);
1201 CPP_UTILITIES_UNUSED(diag)
1203 const auto &info = verifyPresentTrackHeader();
1206 std::uint64_t
size = 8;
1208 size += info.requiredSize;
1214 size += trakChild->totalSize();
1217 if (info.timingsVersion == 0) {
1227 auto dinfAtomWritten =
false;
1230 switch (childAtom->id()) {
1232 size += std::get<0>(calculateSampleTableSize(diag));
1236 dinfAtomWritten =
true;
1239 size += childAtom->totalSize();
1242 if (!dinfAtomWritten) {
1260 ostream::pos_type trakStartOffset =
outputStream().tellp();
1272 trakChild->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1289 const auto &info = verifyPresentTrackHeader();
1292 if (info.versionUnknown) {
1294 argsToString(
"The version of the present \"tkhd\"-atom (", info.version,
") is unknown. Assuming version 1."),
1295 argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1297 if (info.truncated) {
1299 DiagLevel::Critical, argsToString(
"The present \"tkhd\"-atom is truncated."), argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1303 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
1304 writer().writeUInt32BE(1);
1306 writer().writeUInt64BE(info.requiredSize);
1308 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.requiredSize));
1313 writer().writeByte(info.writeVersion);
1314 std::uint32_t
flags = 0;
1327 if (info.writeVersion != 0) {
1328 writer().writeUInt64BE(info.timings.tkhdCreationTime);
1329 writer().writeUInt64BE(info.timings.tkhdModificationTime);
1331 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdCreationTime));
1332 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdModificationTime));
1336 writer().writeUInt32BE(
static_cast<std::uint32_t
>(
m_id));
1337 writer().writeUInt32BE(0);
1338 if (info.writeVersion != 0) {
1339 writer().writeUInt64BE(info.timings.tkhdDuration);
1341 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdDuration));
1343 writer().writeUInt32BE(0);
1344 writer().writeUInt32BE(0);
1347 if (info.canUseExisting) {
1350 static_cast<streamoff
>(m_tkhdAtom->
dataSize() - info.additionalDataOffset));
1352 if (info.discardBuffer) {
1357 diag.emplace_back(
DiagLevel::Warning,
"Writing some default values because the existing tkhd atom is truncated.",
"making tkhd atom");
1358 writer().writeInt16BE(0);
1359 writer().writeInt16BE(0);
1360 writer().writeFixed8BE(1.0);
1361 writer().writeUInt16BE(0);
1362 for (
const std::int32_t value : { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }) {
1363 writer().writeInt32BE(value);
1365 writer().writeFixed16BE(1.0);
1366 writer().writeFixed16BE(1.0);
1376 ostream::pos_type mdiaStartOffset =
outputStream().tellp();
1377 writer().writeUInt32BE(0);
1380 const auto &info = verifyPresentTrackHeader();
1381 const auto &timings = info.timings;
1382 const auto timingsVersion = timings.requiredMdhdVersion();
1383 writer().writeUInt32BE(timingsVersion != 0 ? 44 : 32);
1385 writer().writeByte(timingsVersion);
1386 writer().writeUInt24BE(0);
1387 if (timingsVersion != 0) {
1388 writer().writeUInt64BE(timings.mdhdCreationTime);
1389 writer().writeUInt64BE(timings.mdhdModificationTime);
1391 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdCreationTime));
1392 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdModificationTime));
1395 if (timingsVersion != 0) {
1396 writer().writeUInt64BE(timings.mdhdDuration);
1398 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdDuration));
1403 for (
const auto &detail :
m_locale) {
1409 auto codedLanguage =
static_cast<std::uint16_t
>(0u);
1410 for (
auto charIndex =
static_cast<std::size_t
>(0); charIndex != 3; ++charIndex) {
1411 const char langChar = charIndex < language->size() ? (*language)[charIndex] : 0;
1412 if (langChar >=
'a' && langChar <=
'z') {
1413 codedLanguage |=
static_cast<std::uint16_t
>((langChar - 0x60) << (0xA - charIndex * 0x5));
1418 if (language->empty()) {
1423 diag.emplace_back(
DiagLevel::Warning,
"Assigned language \"" % *language +
"\" is of an invalid format. Setting language to undefined.",
1424 "making mdhd atom");
1425 codedLanguage = 0x55C4;
1428 if (language->size() > 3) {
1430 DiagLevel::Warning,
"Assigned language \"" % *language +
"\" is longer than 3 byte and hence will be truncated.",
"making mdhd atom");
1432 writer().writeUInt16BE(codedLanguage);
1433 writer().writeUInt16BE(0);
1435 writer().writeUInt32BE(33 +
static_cast<std::uint32_t
>(
m_name.size()));
1437 writer().writeUInt64BE(0);
1456 diag.emplace_back(
DiagLevel::Critical,
"Media type is invalid; keeping media type as-is.",
"making hdlr atom");
1458 writer().writeUInt32BE(m_rawMediaType);
1461 for (
int i = 0; i < 3; ++i)
1462 writer().writeUInt32BE(0);
1476 ostream::pos_type minfStartOffset =
outputStream().tellp();
1477 writer().writeUInt32BE(0);
1479 auto dinfAtomWritten =
false;
1483 switch (childAtom->id()) {
1488 dinfAtomWritten =
true;
1491 childAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1495 if (!dinfAtomWritten) {
1496 writer().writeUInt32BE(36);
1499 writer().writeUInt32BE(28);
1501 writer().writeUInt32BE(0);
1502 writer().writeUInt32BE(1);
1504 writer().writeUInt32BE(12);
1507 writer().writeUInt24BE(0x000001);
1522 "Source track does not contain mandatory stbl atom and the tagparser lib is unable to make one from scratch.",
"making stbl atom");
1527 auto [stblSize, stcoSize] = calculateSampleTableSize(diag);
1531 for (
auto *stblChildAtom = m_stblAtom->
firstChild(); stblChildAtom; stblChildAtom = stblChildAtom->
nextSibling()) {
1532 switch (stblChildAtom->id()) {
1539 stblChildAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1549 writer().writeUInt32BE(0);
1550 writer().writeUInt32BE(
static_cast<std::uint32_t
>(chunkOffsets.size()));
1553 for (
const auto chunk : chunkOffsets) {
1554 writer().writeUInt32BE(
static_cast<std::uint32_t
>(chunk));
1558 for (
const auto chunk : chunkOffsets) {
1559 writer().writeUInt64BE(chunk);
1567 CPP_UTILITIES_UNUSED(progress)
1569 static const string context(
"parsing MP4 track");
1570 using namespace Mp4AtomIds;
1578 if (!(m_tkhdAtom = m_trakAtom->
childById(TrackHeader, diag))) {
1582 if (!(m_mdiaAtom = m_trakAtom->
childById(Media, diag))) {
1586 if (!(m_mdhdAtom = m_mdiaAtom->
childById(MediaHeader, diag))) {
1590 if (!(m_hdlrAtom = m_mdiaAtom->
childById(HandlerReference, diag))) {
1594 if (!(m_minfAtom = m_mdiaAtom->
childById(MediaInformation, diag))) {
1598 if (!(m_stblAtom = m_minfAtom->
childById(SampleTable, diag))) {
1602 if (!(m_stsdAtom = m_stblAtom->
childById(SampleDescription, diag))) {
1606 if (!(m_stcoAtom = m_stblAtom->
childById(ChunkOffset, diag)) && !(m_stcoAtom = m_stblAtom->
childById(ChunkOffset64, diag))) {
1610 if (!(m_stscAtom = m_stblAtom->
childById(SampleToChunk, diag))) {
1614 if (!(m_stszAtom = m_stblAtom->
childById(SampleSize, diag)) && !(m_stszAtom = m_stblAtom->
childById(CompactSampleSize, diag))) {
1627 auto atomVersion =
reader.readByte();
1632 switch (atomVersion) {
1634 m_rawTkhdCreationTime =
reader.readUInt32BE();
1635 m_rawTkhdModificationTime =
reader.readUInt32BE();
1637 m_istream->seekg(4, std::ios_base::cur);
1638 m_rawTkhdDuration =
reader.readUInt32BE();
1641 m_rawTkhdCreationTime =
reader.readUInt64BE();
1642 m_rawTkhdModificationTime =
reader.readUInt64BE();
1644 m_istream->seekg(4, std::ios_base::cur);
1645 m_rawTkhdDuration =
reader.readUInt64BE();
1649 "Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
1651 m_rawTkhdCreationTime = m_rawTkhdModificationTime = m_rawTkhdDuration = 0;
1659 atomVersion =
reader.readByte();
1661 switch (atomVersion) {
1663 m_rawMdhdCreationTime =
reader.readUInt32BE();
1664 m_rawMdhdModificationTime =
reader.readUInt32BE();
1666 m_rawMdhdDuration =
reader.readUInt32BE();
1669 m_rawMdhdCreationTime =
reader.readUInt64BE();
1670 m_rawMdhdModificationTime =
reader.readUInt64BE();
1672 m_rawMdhdDuration =
reader.readUInt64BE();
1676 "Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
1679 m_rawMdhdCreationTime = m_rawMdhdModificationTime = m_rawMdhdDuration = 0;
1685 m_duration = TimeSpan::fromSeconds(
static_cast<TimeSpan::TickType
>(m_rawMdhdDuration)) /
static_cast<TimeSpan::TickType
>(
m_timeScale);
1687 std::uint16_t tmp =
reader.readUInt16BE();
1689 const char buff[] = {
1690 static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1691 static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1692 static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1703 switch (m_rawMediaType =
reader.readUInt32BE()) {
1724 if (
static_cast<std::uint64_t
>(tmp =
static_cast<std::uint8_t
>(
m_istream->peek())) == m_hdlrAtom->
dataSize() - 12 - 4 - 8 - 1) {
1736 m_chunkCount =
reader.readUInt32BE();
1740 const auto entryCount =
reader.readUInt32BE();
1741 Mp4Atom *esDescParentAtom =
nullptr;
1744 for (
Mp4Atom *codecConfigContainerAtom = m_stsdAtom->
firstChild(); codecConfigContainerAtom;
1745 codecConfigContainerAtom = codecConfigContainerAtom->
nextSibling()) {
1746 codecConfigContainerAtom->parse(diag);
1749 m_formatId = interpretIntegerAsString<std::uint32_t>(codecConfigContainerAtom->id());
1753 m_istream->seekg(
static_cast<streamoff
>(codecConfigContainerAtom->dataOffset()));
1754 switch (codecConfigContainerAtom->id()) {
1770 tmp =
reader.readUInt16BE();
1786 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1789 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1792 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1794 if (!esDescParentAtom) {
1795 esDescParentAtom = codecConfigContainerAtom;
1812 m_istream->seekg(6 + 2 + 16, ios_base::cur);
1818 m_framesPerSample =
reader.readUInt16BE();
1823 }
else if (tmp < 32) {
1827 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1828 if (!esDescParentAtom) {
1829 esDescParentAtom = codecConfigContainerAtom;
1834 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1835 if (!esDescParentAtom) {
1836 esDescParentAtom = codecConfigContainerAtom;
1847 if (esDescParentAtom) {
1850 m_istream->seekg(
static_cast<streamoff
>(avcConfigAtom->dataOffset()));
1851 m_avcConfig = make_unique<TagParser::AvcConfiguration>();
1853 m_avcConfig->parse(
reader, avcConfigAtom->dataSize(), diag);
1864 m_istream->seekg(
static_cast<streamoff
>(av1ConfigAtom->dataOffset()));
1865 m_av1Config = make_unique<TagParser::Av1Configuration>();
1867 m_av1Config->parse(
reader, av1ConfigAtom->dataSize(), diag);
1887 m_bitrate =
static_cast<double>(m_esInfo->averageBitrate) / 1000;
1888 m_maxBitrate =
static_cast<double>(m_esInfo->maxBitrate) / 1000;
1889 if (m_esInfo->audioSpecificConfig) {
1892 m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
1893 if (m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1898 diag.emplace_back(
DiagLevel::Warning,
"Audio specific config has invalid sample frequency index.", context);
1900 if (m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1907 DiagLevel::Warning,
"Audio specific config has invalid extension sample frequency index.", context);
1909 m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1912 if (m_esInfo->videoSpecificConfig) {
1915 m_format.
sub = m_esInfo->videoSpecificConfig->profile;
1916 if (!m_esInfo->videoSpecificConfig->userData.empty()) {
1918 m_formatId += m_esInfo->videoSpecificConfig->userData;
1928 m_istream->seekg(
static_cast<streamoff
>(m_chunkOffsetSize == 8 ?
reader.readUInt64BE() :
reader.readUInt32BE()));
1941 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse child atoms of \"stsd\"-atom.", context);
1946 m_sampleSizes.clear();
1948 std::uint64_t actualSampleSizeTableSize = m_stszAtom->
dataSize();
1949 if (actualSampleSizeTableSize < 12) {
1951 "The stsz atom is truncated. There are no sample sizes present. The size of the track can not be determined.", context);
1953 actualSampleSizeTableSize -= 12;
1955 std::uint32_t fieldSize;
1956 std::uint32_t constantSize;
1960 fieldSize =
reader.readByte();
1963 constantSize =
reader.readUInt32BE();
1968 m_sampleSizes.push_back(constantSize);
1972 const auto calculatedSampleSizeTableSize
1973 =
static_cast<std::uint64_t
>(std::ceil((0.125 * fieldSize) *
static_cast<double>(
m_sampleCount)));
1974 if (calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1976 DiagLevel::Critical,
"The stsz atom stores more entries as denoted. The additional entries will be ignored.", context);
1977 }
else if (calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1978 diag.emplace_back(
DiagLevel::Critical,
"The stsz atom is truncated. It stores less entries as denoted.", context);
1979 actualSampleCount =
static_cast<std::uint64_t
>(floor(
static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize)));
1981 m_sampleSizes.reserve(actualSampleCount);
1982 std::uint32_t i = 1;
1983 switch (fieldSize) {
1985 for (; i <= actualSampleCount; i += 2) {
1986 std::uint8_t val =
reader.readByte();
1987 m_sampleSizes.push_back(val >> 4);
1988 m_sampleSizes.push_back(val & 0xF0);
1989 m_size += (val >> 4) + (val & 0xF0);
1991 if (i <= actualSampleCount + 1) {
1992 m_sampleSizes.push_back(
reader.readByte() >> 4);
1993 m_size += m_sampleSizes.back();
1997 for (; i <= actualSampleCount; ++i) {
1998 m_sampleSizes.push_back(
reader.readByte());
1999 m_size += m_sampleSizes.back();
2003 for (; i <= actualSampleCount; ++i) {
2004 m_sampleSizes.push_back(
reader.readUInt16BE());
2005 m_size += m_sampleSizes.back();
2009 for (; i <= actualSampleCount; ++i) {
2010 m_sampleSizes.push_back(
reader.readUInt32BE());
2011 m_size += m_sampleSizes.back();
2016 "The fieldsize used to store the sample sizes is not supported. The sample count and size of the track can not be determined.",
2023 std::uint64_t totalDuration = 0;
2024 for (
Mp4Atom *moofAtom = m_trakAtom->
container().firstElement()->siblingByIdIncludingThis(MovieFragment, diag); moofAtom;
2025 moofAtom = moofAtom->siblingById(MovieFragment, diag)) {
2026 moofAtom->parse(diag);
2027 for (
Mp4Atom *trafAtom = moofAtom->
childById(TrackFragment, diag); trafAtom; trafAtom = trafAtom->
siblingById(TrackFragment, diag)) {
2028 trafAtom->parse(diag);
2029 for (
Mp4Atom *tfhdAtom = trafAtom->
childById(TrackFragmentHeader, diag); tfhdAtom;
2030 tfhdAtom = tfhdAtom->
siblingById(TrackFragmentHeader, diag)) {
2031 tfhdAtom->parse(diag);
2032 std::uint32_t calculatedDataSize = 0;
2033 if (tfhdAtom->dataSize() < calculatedDataSize) {
2036 m_istream->seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
2037 std::uint32_t tfhdFlags =
reader.readUInt24BE();
2039 if (tfhdFlags & 0x000001) {
2040 calculatedDataSize += 8;
2042 if (tfhdFlags & 0x000002) {
2043 calculatedDataSize += 4;
2045 if (tfhdFlags & 0x000008) {
2046 calculatedDataSize += 4;
2048 if (tfhdFlags & 0x000010) {
2049 calculatedDataSize += 4;
2051 if (tfhdFlags & 0x000020) {
2052 calculatedDataSize += 4;
2056 std::uint32_t defaultSampleDuration = 0;
2057 std::uint32_t defaultSampleSize = 0;
2059 if (tfhdAtom->dataSize() < calculatedDataSize) {
2060 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
2062 if (tfhdFlags & 0x000001) {
2066 if (tfhdFlags & 0x000002) {
2070 if (tfhdFlags & 0x000008) {
2071 defaultSampleDuration =
reader.readUInt32BE();
2074 if (tfhdFlags & 0x000010) {
2075 defaultSampleSize =
reader.readUInt32BE();
2077 if (tfhdFlags & 0x000020) {
2082 for (
Mp4Atom *trunAtom = trafAtom->
childById(TrackFragmentRun, diag); trunAtom;
2083 trunAtom = trunAtom->
siblingById(TrackFragmentRun, diag)) {
2084 std::uint32_t trunCalculatedDataSize = 8;
2085 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2088 m_istream->seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
2089 std::uint32_t trunFlags =
reader.readUInt24BE();
2092 if (trunFlags & 0x000001) {
2093 trunCalculatedDataSize += 4;
2095 if (trunFlags & 0x000004) {
2096 trunCalculatedDataSize += 4;
2098 std::uint32_t entrySize = 0;
2099 if (trunFlags & 0x000100) {
2102 if (trunFlags & 0x000200) {
2105 if (trunFlags & 0x000400) {
2108 if (trunFlags & 0x000800) {
2111 trunCalculatedDataSize += entrySize *
sampleCount;
2112 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2113 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
2115 if (trunFlags & 0x000001) {
2119 if (trunFlags & 0x000004) {
2123 if (trunFlags & 0x000100) {
2124 totalDuration +=
reader.readUInt32BE();
2126 totalDuration += defaultSampleDuration;
2128 if (trunFlags & 0x000200) {
2129 m_sampleSizes.push_back(
reader.readUInt32BE());
2130 m_size += m_sampleSizes.back();
2132 m_size += defaultSampleSize;
2134 if (trunFlags & 0x000400) {
2137 if (trunFlags & 0x000800) {
2144 if (m_sampleSizes.empty() && defaultSampleSize) {
2145 m_sampleSizes.push_back(defaultSampleSize);
2160 m_duration = TimeSpan::fromSeconds(
static_cast<double>(totalDuration) /
static_cast<double>(
timeScale));
2171 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...
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 constexpr void addHeaderSize(std::uint64_t &dataSize)
Adds the header size to the specified data size.
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 void makeHeader(std::uint64_t size, std::uint32_t id, CppUtilities::BinaryWriter &writer)
Writes an MP4 atom header to the specified 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/co64 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::uint64_t chunkOffsetAtomSize(Diagnostics &diag) const
Returns the size of the stco/co64 atom for this track based on the parsed/assigned chunkOffsetSize() ...
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/co64 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)
@ 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