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())
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);
231 m_istream->seekg(
static_cast<streamoff
>(m_stcoAtom->dataOffset() + 8));
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");
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));
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) {
439 m_tkhdAtom->makeBuffer();
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) {
462 m_tkhdAtom->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;
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);
541 m_istream->seekg(
static_cast<streamoff
>(m_stscAtom->dataOffset() + 8));
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");
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);
709 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
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);
874 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
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());
1022 m_ostream->seekp(
static_cast<streamoff
>(m_stcoAtom->dataOffset() + 8));
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)
1126 m_tkhdAtom->makeBuffer();
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) {
1349 m_ostream->write(m_tkhdAtom->buffer().get() + m_tkhdAtom->headerSize() + info.additionalDataOffset,
1350 static_cast<streamoff
>(m_tkhdAtom->dataSize() - info.additionalDataOffset));
1352 if (info.discardBuffer) {
1353 m_tkhdAtom->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");
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))) {
1623 BinaryReader &
reader = m_trakAtom->reader();
1626 m_istream->seekg(
static_cast<streamoff
>(m_tkhdAtom->startOffset() + 8));
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;
1658 m_istream->seekg(
static_cast<streamoff
>(m_mdhdAtom->dataOffset()));
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),
1701 m_istream->seekg(
static_cast<streamoff
>(m_hdlrAtom->dataOffset() + 8));
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) {
1730 m_name =
reader.readTerminatedString(m_hdlrAtom->dataSize() - 12 - 4 - 8, 0);
1735 m_istream->seekg(
static_cast<streamoff
>(m_stcoAtom->dataOffset() + 4));
1736 m_chunkCount =
reader.readUInt32BE();
1739 m_istream->seekg(
static_cast<streamoff
>(m_stsdAtom->dataOffset() + 4));
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;
1927 m_istream->seekg(
static_cast<streamoff
>(m_stcoAtom->dataOffset() + 8));
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;
1954 m_istream->seekg(
static_cast<streamoff
>(m_stszAtom->dataOffset() + 4));
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));
2170 m_istream->seekg(
static_cast<streamoff
>(m_stscAtom->dataOffset() + 4));
2171 m_sampleToChunkEntryCount =
reader.readUInt32BE();
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks.
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.
AbstractTrack(std::istream &inputStream, std::ostream &outputStream, std::uint64_t startOffset)
Constructs a new track.
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...
Failure() noexcept
Constructs a new exception.
std::uint64_t startOffset() const
Returns the start offset in the related stream.
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.
void clear()
Clears the status of the element.
ImplementationType * siblingById(const IdentifierType &id, Diagnostics &diag)
Returns the first sibling with the specified id.
InvalidDataException() noexcept
Constructs a new exception.
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.
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.
std::uint32_t sampleFrequency
std::uint16_t layerLength
std::uint32_t extensionSampleFrequency
std::uint8_t resilienceFlags
std::uint8_t extensionChannelConfiguration
std::uint16_t coreCoderDelay
std::uint8_t extensionSampleFrequencyIndex
std::uint8_t channelConfiguration
std::uint8_t extensionFlag
Mpeg4AudioSpecificConfig()
std::uint8_t sampleFrequencyIndex
std::uint8_t audioObjectType
std::uint8_t extensionAudioObjectType
std::uint8_t numOfSubFrame
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.
NotImplementedException() noexcept
Constructs a new exception.
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)
Encapsulates the most common MP4 atom IDs.
@ Mpeg4ElementaryStreamDescriptor
@ Mpeg4ElementaryStreamDescriptor2
Encapsulates all supported MPEG-4 audio object type IDs.
TAG_PARSER_EXPORT MediaFormat idToMediaFormat(std::uint8_t mpeg4AudioObjectId, bool sbrPresent=false, bool psPresent=false)
Encapsulates all supported MPEG-4 elementary stream object IDs.
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