1#define CHRONO_UTILITIES_TIMESPAN_INTEGER_SCALE_OVERLOADS
9#include "../av1/av1configuration.h"
11#include "../avc/avcconfiguration.h"
13#include "../mpegaudio/mpegaudioframe.h"
14#include "../mpegaudio/mpegaudioframestream.h"
16#include "../exceptions.h"
17#include "../mediafileinfo.h"
18#include "../mediaformat.h"
20#include <c++utilities/conversion/stringbuilder.h>
21#include <c++utilities/io/binaryreader.h>
22#include <c++utilities/io/binarywriter.h>
23#include <c++utilities/io/bitreader.h>
55 std::uint64_t requiredSize = 100;
57 bool canUseExisting =
false;
59 bool truncated =
false;
63 std::uint8_t writeVersion = 0;
65 bool versionUnknown =
false;
69 std::uint8_t timingsVersion = 0;
71 std::uint8_t additionalDataOffset = 0;
73 bool discardBuffer =
false;
79 ||
tkhdDuration > std::numeric_limits<std::uint32_t>::max())
87 ||
mdhdDuration > std::numeric_limits<std::uint32_t>::max())
100 , sampleFrequencyIndex(0xF)
102 , channelConfiguration(0)
103 , extensionAudioObjectType(0)
106 , extensionSampleFrequencyIndex(0xF)
107 , extensionSampleFrequency(0)
108 , extensionChannelConfiguration(0)
109 , frameLengthFlag(false)
110 , dependsOnCoreCoder(false)
152 , m_trakAtom(&trakAtom)
153 , m_tkhdAtom(nullptr)
154 , m_mdiaAtom(nullptr)
155 , m_mdhdAtom(nullptr)
156 , m_hdlrAtom(nullptr)
157 , m_minfAtom(nullptr)
158 , m_stblAtom(nullptr)
159 , m_stsdAtom(nullptr)
160 , m_stscAtom(nullptr)
161 , m_stcoAtom(nullptr)
162 , m_stszAtom(nullptr)
164 , m_framesPerSample(1)
165 , m_chunkOffsetSize(4)
167 , m_sampleToChunkEntryCount(0)
168 , m_rawTkhdCreationTime(0)
169 , m_rawMdhdCreationTime(0)
170 , m_rawTkhdModificationTime(0)
171 , m_rawMdhdModificationTime(0)
172 , m_rawTkhdDuration(0)
173 , m_rawMdhdDuration(0)
201 static const string context(
"reading chunk offset table of MP4 track");
206 vector<std::uint64_t> offsets;
209 std::uint64_t actualTableSize = m_stcoAtom->
dataSize();
211 diag.emplace_back(
DiagLevel::Critical,
"The stco atom is truncated. There are no chunk offsets present.", context);
214 actualTableSize -= 8;
216 std::uint32_t actualChunkCount =
chunkCount();
218 if (calculatedTableSize < actualTableSize) {
220 DiagLevel::Critical,
"The stco atom stores more chunk offsets as denoted. The additional chunk offsets will be ignored.", context);
221 }
else if (calculatedTableSize > actualTableSize) {
222 diag.emplace_back(
DiagLevel::Critical,
"The stco atom is truncated. It stores less chunk offsets as denoted.", context);
223 actualChunkCount =
static_cast<std::uint32_t
>(floor(
static_cast<double>(actualTableSize) /
static_cast<double>(
chunkOffsetSize())));
226 offsets.reserve(actualChunkCount);
230 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
231 offsets.push_back(
reader().readUInt32BE());
235 for (std::uint32_t i = 0; i < actualChunkCount; ++i) {
236 offsets.push_back(
reader().readUInt64BE());
240 diag.emplace_back(
DiagLevel::Critical,
"The determined chunk offset size is invalid.", context);
245 if (parseFragments) {
249 moofAtom->parse(diag);
252 trafAtom->parse(diag);
255 tfhdAtom->parse(diag);
256 std::uint32_t calculatedDataSize = 0;
257 if (tfhdAtom->dataSize() < calculatedDataSize) {
260 inputStream().seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
261 const std::uint32_t
flags =
reader().readUInt24BE();
263 if (
flags & 0x000001) {
264 calculatedDataSize += 8;
266 if (
flags & 0x000002) {
267 calculatedDataSize += 4;
269 if (
flags & 0x000008) {
270 calculatedDataSize += 4;
272 if (
flags & 0x000010) {
273 calculatedDataSize += 4;
275 if (
flags & 0x000020) {
276 calculatedDataSize += 4;
282 std::uint32_t defaultSampleSize = 0;
284 if (tfhdAtom->dataSize() < calculatedDataSize) {
285 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
287 if (
flags & 0x000001) {
291 if (
flags & 0x000002) {
295 if (
flags & 0x000008) {
299 if (
flags & 0x000010) {
300 defaultSampleSize =
reader().readUInt32BE();
302 if (
flags & 0x000020) {
309 std::uint32_t trunCalculatedDataSize = 8;
310 if (trunAtom->dataSize() < trunCalculatedDataSize) {
313 inputStream().seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
314 std::uint32_t trunFlags =
reader().readUInt24BE();
317 if (trunFlags & 0x000001) {
318 trunCalculatedDataSize += 4;
320 if (trunFlags & 0x000004) {
321 trunCalculatedDataSize += 4;
323 std::uint32_t entrySize = 0;
324 if (trunFlags & 0x000100) {
327 if (trunFlags & 0x000200) {
330 if (trunFlags & 0x000400) {
333 if (trunFlags & 0x000800) {
337 if (trunAtom->dataSize() < trunCalculatedDataSize) {
338 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
340 if (trunFlags & 0x000001) {
344 if (trunFlags & 0x000004) {
348 if (trunFlags & 0x000100) {
354 if (trunFlags & 0x000200) {
355 m_sampleSizes.push_back(
reader().readUInt32BE());
356 m_size += m_sampleSizes.back();
358 m_size += defaultSampleSize;
360 if (trunFlags & 0x000400) {
363 if (trunFlags & 0x000800) {
370 if (m_sampleSizes.empty() && defaultSampleSize) {
371 m_sampleSizes.push_back(defaultSampleSize);
386std::uint64_t Mp4Track::accumulateSampleSizes(
size_t &sampleIndex,
size_t count,
Diagnostics &diag)
388 if (sampleIndex + count <= m_sampleSizes.size()) {
389 std::uint64_t sum = 0;
390 for (
size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
391 sum += m_sampleSizes[sampleIndex];
394 }
else if (m_sampleSizes.size() == 1) {
395 sampleIndex += count;
396 return static_cast<std::uint64_t
>(m_sampleSizes.front()) * count;
398 diag.emplace_back(
DiagLevel::Critical,
"There are not as many sample size entries as samples.",
"reading chunk sizes of MP4 track");
399 throw InvalidDataException();
411void Mp4Track::addChunkSizeEntries(
412 std::vector<std::uint64_t> &chunkSizeTable,
size_t count,
size_t &sampleIndex, std::uint32_t sampleCount, Diagnostics &diag)
414 for (
size_t i = 0; i < count; ++i) {
415 chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex,
sampleCount, diag));
423const TrackHeaderInfo &Mp4Track::verifyPresentTrackHeader()
const
425 if (m_trackHeaderInfo) {
426 return *m_trackHeaderInfo;
430 auto &info = *(m_trackHeaderInfo = std::make_unique<TrackHeaderInfo>());
436 info.discardBuffer = m_tkhdAtom->
buffer() ==
nullptr;
437 if (info.discardBuffer) {
442 switch (info.version =
static_cast<std::uint8_t
>(m_tkhdAtom->
buffer()[m_tkhdAtom->
headerSize()])) {
444 info.additionalDataOffset = 32;
447 info.additionalDataOffset = 44;
450 info.additionalDataOffset = 44;
451 info.versionUnknown =
true;
455 if (info.additionalDataOffset + 48u <= m_tkhdAtom->
dataSize()) {
456 info.canUseExisting =
true;
458 info.truncated =
true;
459 info.canUseExisting = info.additionalDataOffset < m_tkhdAtom->
dataSize();
460 if (!info.canUseExisting && info.discardBuffer) {
466 info.requiredSize = m_tkhdAtom->
dataSize() + 8;
467 info.timings = computeTimings();
468 info.timingsVersion = info.timings.requiredTkhdVersion();
469 if (info.version == 0) {
470 info.writeVersion = info.timingsVersion;
472 if (info.writeVersion != 0) {
473 info.requiredSize += 12;
476 info.writeVersion = info.version;
479 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
480 info.requiredSize += 8;
488Mp4Timings Mp4Track::computeTimings()
const
490 auto timings = Mp4Timings();
492 timings.tkhdCreationTime = m_rawTkhdCreationTime;
493 timings.tkhdModificationTime = m_rawTkhdModificationTime;
494 timings.tkhdDuration = m_rawTkhdDuration;
495 timings.mdhdCreationTime = m_rawMdhdCreationTime;
496 timings.mdhdModificationTime = m_rawMdhdModificationTime;
497 timings.mdhdDuration = m_rawMdhdDuration;
500 timings.tkhdModificationTime = timings.mdhdModificationTime
502 timings.tkhdDuration = timings.mdhdDuration =
static_cast<std::uint64_t
>(
m_duration.totalTicks() *
m_timeScale / TimeSpan::ticksPerSecond);
516 static const string context(
"reading sample to chunk table of MP4 track");
518 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
522 std::uint64_t actualTableSize = m_stscAtom->
dataSize();
523 if (actualTableSize < 20) {
524 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. There are no \"sample to chunk\" entries present.", context);
527 actualTableSize -= 8;
530 std::uint64_t calculatedTableSize = actualSampleToChunkEntryCount * 12;
531 if (calculatedTableSize < actualTableSize) {
532 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom stores more entries as denoted. The additional entries will be ignored.", context);
533 }
else if (calculatedTableSize > actualTableSize) {
534 diag.emplace_back(
DiagLevel::Critical,
"The stsc atom is truncated. It stores less entries as denoted.", context);
535 actualSampleToChunkEntryCount = actualTableSize / 12;
538 vector<tuple<std::uint32_t, std::uint32_t, std::uint32_t>> sampleToChunkTable;
539 sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
541 for (std::uint32_t i = 0; i < actualSampleToChunkEntryCount; ++i) {
543 std::uint32_t firstChunk =
reader().readUInt32BE();
544 std::uint32_t samplesPerChunk =
reader().readUInt32BE();
545 std::uint32_t sampleDescriptionIndex =
reader().readUInt32BE();
546 sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
548 return sampleToChunkTable;
565 static const string context(
"reading chunk sizes of MP4 track");
567 diag.emplace_back(
DiagLevel::Critical,
"Track has not been parsed or is invalid.", context);
573 vector<std::uint64_t> chunkSizes;
574 if (!sampleToChunkTable.empty()) {
576 auto tableIterator = sampleToChunkTable.cbegin();
577 chunkSizes.reserve(m_chunkCount);
579 size_t sampleIndex = 0;
580 std::uint32_t previousChunkIndex = get<0>(*tableIterator);
581 if (previousChunkIndex != 1) {
582 diag.emplace_back(
DiagLevel::Critical,
"The first chunk of the first \"sample to chunk\" entry must be 1.", context);
583 previousChunkIndex = 1;
585 std::uint32_t samplesPerChunk = get<1>(*tableIterator);
588 for (
const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
589 std::uint32_t firstChunkIndex = get<0>(*tableIterator);
590 if (firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
591 addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
594 "The first chunk index of a \"sample to chunk\" entry must be greater than the first chunk of the previous entry and not "
595 "greater than the chunk count.",
599 previousChunkIndex = firstChunkIndex;
600 samplesPerChunk = get<1>(*tableIterator);
602 if (m_chunkCount >= previousChunkIndex) {
603 addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk, diag);
616 static const string context(
"parsing MPEG-4 elementary stream descriptor");
617 using namespace Mpeg4ElementaryStreamObjectIds;
618 unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
622 if (
reader.readUInt32BE() != 0) {
636 esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
637 esInfo->id =
reader.readUInt16BE();
638 esInfo->esDescFlags =
reader.readByte();
639 if (esInfo->dependencyFlag()) {
640 esInfo->dependsOnId =
reader.readUInt16BE();
642 if (esInfo->urlFlag()) {
645 if (esInfo->ocrFlag()) {
646 esInfo->ocrId =
reader.readUInt16BE();
650 esDescChild; esDescChild = esDescChild->
nextSibling()) {
651 esDescChild->parse(diag);
652 switch (esDescChild->id()) {
655 reader.stream()->seekg(
static_cast<streamoff
>(esDescChild->dataOffset()));
656 esInfo->objectTypeId =
reader.readByte();
657 esInfo->decCfgDescFlags =
reader.readByte();
658 esInfo->bufferSize =
reader.readUInt24BE();
659 esInfo->maxBitrate =
reader.readUInt32BE();
660 esInfo->averageBitrate =
reader.readUInt32BE();
662 decCfgDescChild = decCfgDescChild->
nextSibling()) {
663 decCfgDescChild->parse(diag);
664 switch (decCfgDescChild->id()) {
667 switch (esInfo->objectTypeId) {
674 esInfo->audioSpecificConfig
678 esInfo->videoSpecificConfig
693 diag.emplace_back(
DiagLevel::Critical,
"The MPEG-4 descriptor element structure is invalid.", context);
696 diag.emplace_back(
DiagLevel::Warning,
"Elementary stream descriptor atom (esds) is truncated.", context);
706 istream &stream, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
708 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
709 using namespace Mpeg4AudioObjectIds;
712 auto buff = make_unique<char[]>(
size);
713 stream.read(buff.get(),
static_cast<streamoff
>(
size));
714 BitReader bitReader(buff.get(),
size);
715 auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
718 auto getAudioObjectType = [&bitReader] {
719 std::uint8_t objType = bitReader.readBits<std::uint8_t>(5);
721 objType = 32 + bitReader.readBits<std::uint8_t>(6);
725 audioCfg->audioObjectType = getAudioObjectType();
727 if ((audioCfg->sampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
728 audioCfg->sampleFrequency = bitReader.readBits<std::uint32_t>(24);
731 audioCfg->channelConfiguration = bitReader.readBits<std::uint8_t>(4);
733 switch (audioCfg->audioObjectType) {
736 audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
737 audioCfg->sbrPresent =
true;
738 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
739 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
741 if ((audioCfg->audioObjectType = getAudioObjectType()) ==
ErBsac) {
742 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
746 switch (audioCfg->extensionAudioObjectType) {
748 audioCfg->psPresent =
true;
753 switch (audioCfg->audioObjectType) {
765 audioCfg->frameLengthFlag = bitReader.readBits<std::uint8_t>(1);
766 if ((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
767 audioCfg->coreCoderDelay = bitReader.readBits<std::uint8_t>(14);
769 audioCfg->extensionFlag = bitReader.readBit();
770 if (audioCfg->channelConfiguration == 0) {
773 switch (audioCfg->audioObjectType) {
776 audioCfg->layerNr = bitReader.readBits<std::uint8_t>(3);
780 if (audioCfg->extensionFlag == 1) {
781 switch (audioCfg->audioObjectType) {
783 audioCfg->numOfSubFrame = bitReader.readBits<std::uint8_t>(5);
784 audioCfg->layerLength = bitReader.readBits<std::uint16_t>(11);
790 audioCfg->resilienceFlags = bitReader.readBits<std::uint8_t>(3);
794 if (bitReader.readBit() == 1) {
803 switch (audioCfg->audioObjectType) {
815 switch (audioCfg->epConfig = bitReader.readBits<std::uint8_t>(2)) {
819 bitReader.skipBits(1);
826 if (audioCfg->extensionAudioObjectType !=
Sbr && audioCfg->extensionAudioObjectType !=
Ps && bitReader.bitsAvailable() >= 16) {
827 std::uint16_t syncExtensionType = bitReader.readBits<std::uint16_t>(11);
828 if (syncExtensionType == 0x2B7) {
829 if ((audioCfg->extensionAudioObjectType = getAudioObjectType()) ==
Sbr) {
830 if ((audioCfg->sbrPresent = bitReader.readBit())) {
831 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
832 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
834 if (bitReader.bitsAvailable() >= 12) {
835 if ((syncExtensionType = bitReader.readBits<std::uint16_t>(11)) == 0x548) {
836 audioCfg->psPresent = bitReader.readBits<std::uint8_t>(1);
840 }
else if (audioCfg->extensionAudioObjectType ==
ErBsac) {
841 if ((audioCfg->sbrPresent = bitReader.readBit())) {
842 if ((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<std::uint8_t>(4)) == 0xF) {
843 audioCfg->extensionSampleFrequency = bitReader.readBits<std::uint32_t>(24);
846 audioCfg->extensionChannelConfiguration = bitReader.readBits<std::uint8_t>(4);
848 }
else if (syncExtensionType == 0x548) {
849 audioCfg->psPresent = bitReader.readBit();
854 }
catch (
const std::ios_base::failure &) {
860 diag.emplace_back(
DiagLevel::Critical,
"Audio specific configuration is truncated.", context);
871 BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size,
Diagnostics &diag)
873 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
874 using namespace Mpeg4AudioObjectIds;
875 auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
878 if (
size > 3 && (
reader.readUInt24BE() == 1)) {
883 switch (
reader.readByte()) {
886 videoCfg->profile =
reader.readByte();
896 if ((buff1 =
reader.readUInt24BE()) != 1) {
897 reader.stream()->seekg(-2, ios_base::cur);
898 videoCfg->userData.push_back(
static_cast<char>(buff1 >> 16));
905 if (buff1 != 1 &&
size > 0) {
906 videoCfg->userData +=
reader.readString(
size);
914 if (
reader.readUInt24BE() != 1) {
915 reader.stream()->seekg(-2, ios_base::cur);
924 diag.emplace_back(
DiagLevel::Critical,
"\"Visual Object Sequence Header\" not found.", context);
951 if (oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
954 static const unsigned int stcoDataBegin = 8;
955 std::uint64_t startPos = m_stcoAtom->
dataOffset() + stcoDataBegin;
956 std::uint64_t endPos = startPos + m_stcoAtom->
dataSize() - stcoDataBegin;
957 m_istream->seekg(
static_cast<streamoff
>(startPos));
958 m_ostream->seekp(
static_cast<streamoff
>(startPos));
959 vector<std::int64_t>::size_type i;
960 vector<std::int64_t>::size_type
size;
961 auto currentPos =
static_cast<std::uint64_t
>(
m_istream->tellg());
962 switch (m_stcoAtom->
id()) {
965 while ((currentPos + 4) <= endPos) {
967 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
968 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
969 off +=
static_cast<std::uint32_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
973 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
975 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
981 while ((currentPos + 8) <= endPos) {
983 for (i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
984 if (off >
static_cast<std::uint64_t
>(oldMdatOffsets[i])) {
985 off +=
static_cast<std::uint64_t
>(newMdatOffsets[i] - oldMdatOffsets[i]);
989 m_ostream->seekp(
static_cast<streamoff
>(currentPos));
991 currentPos +=
static_cast<std::uint64_t
>(
m_istream->gcount());
1022 switch (m_stcoAtom->
id()) {
1024 for (
auto offset : chunkOffsets) {
1025 m_writer.writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1029 for (
auto offset : chunkOffsets) {
1059 writer().writeUInt32BE(
static_cast<std::uint32_t
>(offset));
1062 writer().writeUInt64BE(offset);
1108 CPP_UTILITIES_UNUSED(av1Config)
1109 CPP_UTILITIES_UNUSED(track)
1121 CPP_UTILITIES_UNUSED(diag)
1130 trakChild->makeBuffer();
1134 childAtom->makeBuffer();
1144 CPP_UTILITIES_UNUSED(diag)
1146 const auto &info = verifyPresentTrackHeader();
1149 std::uint64_t
size = 8;
1151 size += info.requiredSize;
1157 size += trakChild->totalSize();
1160 if (info.timingsVersion == 0) {
1170 bool dinfAtomWritten =
false;
1174 dinfAtomWritten =
true;
1176 size += childAtom->totalSize();
1179 if (!dinfAtomWritten) {
1197 ostream::pos_type trakStartOffset =
outputStream().tellp();
1209 trakChild->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1226 const auto &info = verifyPresentTrackHeader();
1229 if (info.versionUnknown) {
1231 argsToString(
"The version of the present \"tkhd\"-atom (", info.version,
") is unknown. Assuming version 1."),
1232 argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1234 if (info.truncated) {
1236 DiagLevel::Critical, argsToString(
"The present \"tkhd\"-atom is truncated."), argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1240 if (info.requiredSize > numeric_limits<std::uint32_t>::max()) {
1241 writer().writeUInt32BE(1);
1243 writer().writeUInt64BE(info.requiredSize);
1245 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.requiredSize));
1250 writer().writeByte(info.writeVersion);
1251 std::uint32_t
flags = 0;
1264 if (info.writeVersion != 0) {
1265 writer().writeUInt64BE(info.timings.tkhdCreationTime);
1266 writer().writeUInt64BE(info.timings.tkhdModificationTime);
1268 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdCreationTime));
1269 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdModificationTime));
1273 writer().writeUInt32BE(
static_cast<std::uint32_t
>(
m_id));
1274 writer().writeUInt32BE(0);
1275 if (info.writeVersion != 0) {
1276 writer().writeUInt64BE(info.timings.tkhdDuration);
1278 writer().writeUInt32BE(
static_cast<std::uint32_t
>(info.timings.tkhdDuration));
1280 writer().writeUInt32BE(0);
1281 writer().writeUInt32BE(0);
1284 if (info.canUseExisting) {
1287 static_cast<streamoff
>(m_tkhdAtom->
dataSize() - info.additionalDataOffset));
1289 if (info.discardBuffer) {
1294 diag.emplace_back(
DiagLevel::Warning,
"Writing some default values because the existing tkhd atom is truncated.",
"making tkhd atom");
1295 writer().writeInt16BE(0);
1296 writer().writeInt16BE(0);
1297 writer().writeFixed8BE(1.0);
1298 writer().writeUInt16BE(0);
1299 for (
const std::int32_t value : { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 }) {
1300 writer().writeInt32BE(value);
1302 writer().writeFixed16BE(1.0);
1303 writer().writeFixed16BE(1.0);
1313 ostream::pos_type mdiaStartOffset =
outputStream().tellp();
1314 writer().writeUInt32BE(0);
1317 const auto &info = verifyPresentTrackHeader();
1318 const auto &timings = info.timings;
1319 const auto timingsVersion = timings.requiredMdhdVersion();
1320 writer().writeUInt32BE(timingsVersion != 0 ? 44 : 32);
1322 writer().writeByte(timingsVersion);
1323 writer().writeUInt24BE(0);
1324 if (timingsVersion != 0) {
1325 writer().writeUInt64BE(timings.mdhdCreationTime);
1326 writer().writeUInt64BE(timings.mdhdModificationTime);
1328 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdCreationTime));
1329 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdModificationTime));
1332 if (timingsVersion != 0) {
1333 writer().writeUInt64BE(timings.mdhdDuration);
1335 writer().writeUInt32BE(
static_cast<std::uint32_t
>(timings.mdhdDuration));
1339 std::uint16_t codedLanguage = 0;
1340 for (
size_t charIndex = 0; charIndex != 3; ++charIndex) {
1341 const char langChar = charIndex <
language.size() ?
language[charIndex] : 0;
1342 if (langChar >=
'a' && langChar <=
'z') {
1343 codedLanguage |=
static_cast<std::uint16_t
>((langChar - 0x60) << (0xA - charIndex * 0x5));
1354 DiagLevel::Warning,
"Assigned language \"" %
language +
"\" is of an invalid format. Setting language to undefined.",
"making mdhd atom");
1355 codedLanguage = 0x55C4;
1360 DiagLevel::Warning,
"Assigned language \"" %
language +
"\" is longer than 3 byte and hence will be truncated.",
"making mdhd atom");
1362 writer().writeUInt16BE(codedLanguage);
1363 writer().writeUInt16BE(0);
1365 writer().writeUInt32BE(33 +
static_cast<std::uint32_t
>(
m_name.size()));
1367 writer().writeUInt64BE(0);
1386 diag.emplace_back(
DiagLevel::Critical,
"Media type is invalid; keeping media type as-is.",
"making hdlr atom");
1388 writer().writeUInt32BE(m_rawMediaType);
1391 for (
int i = 0; i < 3; ++i)
1392 writer().writeUInt32BE(0);
1406 ostream::pos_type minfStartOffset =
outputStream().tellp();
1407 writer().writeUInt32BE(0);
1409 bool dinfAtomWritten =
false;
1417 dinfAtomWritten =
true;
1419 childAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1423 if (!dinfAtomWritten) {
1424 writer().writeUInt32BE(36);
1427 writer().writeUInt32BE(28);
1429 writer().writeUInt32BE(0);
1430 writer().writeUInt32BE(1);
1432 writer().writeUInt32BE(12);
1435 writer().writeUInt24BE(0x000001);
1439 bool stblAtomWritten =
false;
1442 stblAtom->copyPreferablyFromBuffer(
outputStream(), diag,
nullptr);
1443 stblAtomWritten =
true;
1446 if (!stblAtomWritten) {
1448 "Source track does not contain mandatory stbl atom and the tagparser lib is unable to make one from scratch.",
"making stbl atom");
1463 writer().writeUInt32BE(0);
1471 diag.emplace_back(
DiagLevel::Critical,
"Unable to make stsd atom from scratch.",
"making stsd atom");
1480 diag.emplace_back(
DiagLevel::Critical,
"Unable to make stts atom from scratch.",
"making stts atom");
1521 CPP_UTILITIES_UNUSED(progress)
1523 static const string context(
"parsing MP4 track");
1524 using namespace Mp4AtomIds;
1581 auto atomVersion =
reader.readByte();
1586 switch (atomVersion) {
1588 m_rawTkhdCreationTime =
reader.readUInt32BE();
1589 m_rawTkhdModificationTime =
reader.readUInt32BE();
1591 m_rawTkhdDuration =
reader.readUInt32BE();
1594 m_rawTkhdCreationTime =
reader.readUInt64BE();
1595 m_rawTkhdModificationTime =
reader.readUInt64BE();
1597 m_rawTkhdDuration =
reader.readUInt64BE();
1601 "Version of \"tkhd\"-atom not supported. It will be ignored. Track ID, creation time and modification time might not be be determined.",
1603 m_rawTkhdCreationTime = m_rawTkhdModificationTime = m_rawTkhdDuration = 0;
1611 atomVersion =
reader.readByte();
1613 switch (atomVersion) {
1615 m_rawMdhdCreationTime =
reader.readUInt32BE();
1616 m_rawMdhdModificationTime =
reader.readUInt32BE();
1618 m_rawMdhdDuration =
reader.readUInt32BE();
1621 m_rawMdhdCreationTime =
reader.readUInt64BE();
1622 m_rawMdhdModificationTime =
reader.readUInt64BE();
1624 m_rawMdhdDuration =
reader.readUInt64BE();
1628 "Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be "
1631 m_rawMdhdCreationTime = m_rawMdhdModificationTime = m_rawMdhdDuration = 0;
1637 m_duration = TimeSpan::fromSeconds(
static_cast<TimeSpan::TickType
>(m_rawMdhdDuration)) /
static_cast<TimeSpan::TickType
>(
m_timeScale);
1639 std::uint16_t tmp =
reader.readUInt16BE();
1641 const char buff[] = {
1642 static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1643 static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1644 static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1655 switch (m_rawMediaType =
reader.readUInt32BE()) {
1676 if (
static_cast<std::uint64_t
>(tmp =
static_cast<std::uint8_t
>(
m_istream->peek())) == m_hdlrAtom->
dataSize() - 12 - 4 - 8 - 1) {
1688 m_chunkCount =
reader.readUInt32BE();
1692 const auto entryCount =
reader.readUInt32BE();
1693 Mp4Atom *esDescParentAtom =
nullptr;
1696 for (
Mp4Atom *codecConfigContainerAtom = m_stsdAtom->
firstChild(); codecConfigContainerAtom;
1697 codecConfigContainerAtom = codecConfigContainerAtom->
nextSibling()) {
1698 codecConfigContainerAtom->
parse(diag);
1701 m_formatId = interpretIntegerAsString<std::uint32_t>(codecConfigContainerAtom->id());
1705 m_istream->seekg(
static_cast<streamoff
>(codecConfigContainerAtom->dataOffset()));
1706 switch (codecConfigContainerAtom->id()) {
1722 tmp =
reader.readUInt16BE();
1738 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1741 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1744 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1746 if (!esDescParentAtom) {
1747 esDescParentAtom = codecConfigContainerAtom;
1764 m_istream->seekg(6 + 2 + 16, ios_base::cur);
1770 m_framesPerSample =
reader.readUInt16BE();
1775 }
else if (tmp < 32) {
1779 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1780 if (!esDescParentAtom) {
1781 esDescParentAtom = codecConfigContainerAtom;
1786 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1787 if (!esDescParentAtom) {
1788 esDescParentAtom = codecConfigContainerAtom;
1799 if (esDescParentAtom) {
1802 m_istream->seekg(
static_cast<streamoff
>(avcConfigAtom->dataOffset()));
1803 m_avcConfig = make_unique<TagParser::AvcConfiguration>();
1805 m_avcConfig->parse(
reader, avcConfigAtom->dataSize(), diag);
1816 m_istream->seekg(
static_cast<streamoff
>(av1ConfigAtom->dataOffset()));
1817 m_av1Config = make_unique<TagParser::Av1Configuration>();
1819 m_av1Config->parse(
reader, av1ConfigAtom->dataSize(), diag);
1839 m_bitrate =
static_cast<double>(m_esInfo->averageBitrate) / 1000;
1840 m_maxBitrate =
static_cast<double>(m_esInfo->maxBitrate) / 1000;
1841 if (m_esInfo->audioSpecificConfig) {
1844 m_esInfo->audioSpecificConfig->sbrPresent, m_esInfo->audioSpecificConfig->psPresent);
1845 if (m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1850 diag.emplace_back(
DiagLevel::Warning,
"Audio specific config has invalid sample frequency index.", context);
1852 if (m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1859 DiagLevel::Warning,
"Audio specific config has invalid extension sample frequency index.", context);
1861 m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1864 if (m_esInfo->videoSpecificConfig) {
1867 m_format.
sub = m_esInfo->videoSpecificConfig->profile;
1868 if (!m_esInfo->videoSpecificConfig->userData.empty()) {
1870 m_formatId += m_esInfo->videoSpecificConfig->userData;
1880 m_istream->seekg(
static_cast<streamoff
>(m_chunkOffsetSize == 8 ?
reader.readUInt64BE() :
reader.readUInt32BE()));
1893 diag.emplace_back(
DiagLevel::Critical,
"Unable to parse child atoms of \"stsd\"-atom.", context);
1898 m_sampleSizes.clear();
1900 std::uint64_t actualSampleSizeTableSize = m_stszAtom->
dataSize();
1901 if (actualSampleSizeTableSize < 12) {
1903 "The stsz atom is truncated. There are no sample sizes present. The size of the track can not be determined.", context);
1905 actualSampleSizeTableSize -= 12;
1907 std::uint32_t fieldSize;
1908 std::uint32_t constantSize;
1912 fieldSize =
reader.readByte();
1915 constantSize =
reader.readUInt32BE();
1920 m_sampleSizes.push_back(constantSize);
1924 const auto calculatedSampleSizeTableSize
1925 =
static_cast<std::uint64_t
>(std::ceil((0.125 * fieldSize) *
static_cast<double>(
m_sampleCount)));
1926 if (calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1928 DiagLevel::Critical,
"The stsz atom stores more entries as denoted. The additional entries will be ignored.", context);
1929 }
else if (calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1930 diag.emplace_back(
DiagLevel::Critical,
"The stsz atom is truncated. It stores less entries as denoted.", context);
1931 actualSampleCount =
static_cast<std::uint64_t
>(floor(
static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize)));
1933 m_sampleSizes.reserve(actualSampleCount);
1934 std::uint32_t i = 1;
1935 switch (fieldSize) {
1937 for (; i <= actualSampleCount; i += 2) {
1938 std::uint8_t val =
reader.readByte();
1939 m_sampleSizes.push_back(val >> 4);
1940 m_sampleSizes.push_back(val & 0xF0);
1941 m_size += (val >> 4) + (val & 0xF0);
1943 if (i <= actualSampleCount + 1) {
1944 m_sampleSizes.push_back(
reader.readByte() >> 4);
1945 m_size += m_sampleSizes.back();
1949 for (; i <= actualSampleCount; ++i) {
1950 m_sampleSizes.push_back(
reader.readByte());
1951 m_size += m_sampleSizes.back();
1955 for (; i <= actualSampleCount; ++i) {
1956 m_sampleSizes.push_back(
reader.readUInt16BE());
1957 m_size += m_sampleSizes.back();
1961 for (; i <= actualSampleCount; ++i) {
1962 m_sampleSizes.push_back(
reader.readUInt32BE());
1963 m_size += m_sampleSizes.back();
1968 "The fieldsize used to store the sample sizes is not supported. The sample count and size of the track can not be determined.",
1975 std::uint64_t totalDuration = 0;
1978 moofAtom->parse(diag);
1980 trafAtom->parse(diag);
1983 tfhdAtom->parse(diag);
1984 std::uint32_t calculatedDataSize = 0;
1985 if (tfhdAtom->dataSize() < calculatedDataSize) {
1988 m_istream->seekg(
static_cast<streamoff
>(tfhdAtom->dataOffset() + 1));
1989 std::uint32_t tfhdFlags =
reader.readUInt24BE();
1991 if (tfhdFlags & 0x000001) {
1992 calculatedDataSize += 8;
1994 if (tfhdFlags & 0x000002) {
1995 calculatedDataSize += 4;
1997 if (tfhdFlags & 0x000008) {
1998 calculatedDataSize += 4;
2000 if (tfhdFlags & 0x000010) {
2001 calculatedDataSize += 4;
2003 if (tfhdFlags & 0x000020) {
2004 calculatedDataSize += 4;
2008 std::uint32_t defaultSampleDuration = 0;
2009 std::uint32_t defaultSampleSize = 0;
2011 if (tfhdAtom->dataSize() < calculatedDataSize) {
2012 diag.emplace_back(
DiagLevel::Critical,
"tfhd atom is truncated (presence of fields denoted).", context);
2014 if (tfhdFlags & 0x000001) {
2018 if (tfhdFlags & 0x000002) {
2022 if (tfhdFlags & 0x000008) {
2023 defaultSampleDuration =
reader.readUInt32BE();
2026 if (tfhdFlags & 0x000010) {
2027 defaultSampleSize =
reader.readUInt32BE();
2029 if (tfhdFlags & 0x000020) {
2036 std::uint32_t trunCalculatedDataSize = 8;
2037 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2040 m_istream->seekg(
static_cast<streamoff
>(trunAtom->dataOffset() + 1));
2041 std::uint32_t trunFlags =
reader.readUInt24BE();
2044 if (trunFlags & 0x000001) {
2045 trunCalculatedDataSize += 4;
2047 if (trunFlags & 0x000004) {
2048 trunCalculatedDataSize += 4;
2050 std::uint32_t entrySize = 0;
2051 if (trunFlags & 0x000100) {
2054 if (trunFlags & 0x000200) {
2057 if (trunFlags & 0x000400) {
2060 if (trunFlags & 0x000800) {
2063 trunCalculatedDataSize += entrySize *
sampleCount;
2064 if (trunAtom->dataSize() < trunCalculatedDataSize) {
2065 diag.emplace_back(
DiagLevel::Critical,
"trun atom is truncated (presence of fields denoted).", context);
2067 if (trunFlags & 0x000001) {
2071 if (trunFlags & 0x000004) {
2075 if (trunFlags & 0x000100) {
2076 totalDuration +=
reader.readUInt32BE();
2078 totalDuration += defaultSampleDuration;
2080 if (trunFlags & 0x000200) {
2081 m_sampleSizes.push_back(
reader.readUInt32BE());
2082 m_size += m_sampleSizes.back();
2084 m_size += defaultSampleSize;
2086 if (trunFlags & 0x000400) {
2089 if (trunFlags & 0x000800) {
2096 if (m_sampleSizes.empty() && defaultSampleSize) {
2097 m_sampleSizes.push_back(defaultSampleSize);
2112 m_duration = TimeSpan::fromSeconds(
static_cast<double>(totalDuration) /
static_cast<double>(
timeScale));
2117 if (m_bitrate < 0.01 && m_bitrate > -0.01) {
2123 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.
void copyEntirely(std::ostream &targetStream, Diagnostics &diag, AbortableProgressFeedback *progress)
Writes the entire element including all children to the specified targetStream.
const std::unique_ptr< char[]> & buffer()
Returns buffered data.
std::uint32_t headerSize() const
Returns the header size of the element in byte.
const IdentifierType & id() const
Returns the element ID.
ImplementationType * childById(const IdentifierType &id, Diagnostics &diag)
Returns the first child with the specified id.
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * denoteFirstChild(std::uint32_t offset)
Denotes the first child to start at the specified offset (relative to the start offset of this descri...
ImplementationType * firstChild()
Returns the first child of the element.
DataSizeType dataSize() const
Returns the data size of the element in byte.
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
std::uint64_t dataOffset() const
Returns the data offset of the element in the related stream.
ContainerType & container()
Returns the related container.
CppUtilities::BinaryReader & reader()
Returns the related BinaryReader.
void makeBuffer()
Buffers the element (header and data).
ImplementationType * siblingById(const IdentifierType &id, Diagnostics &diag)
Returns the first sibling with the specified id.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
The Mp4Atom class helps to parse MP4 files.
static void seekBackAndWriteAtomSize(std::ostream &stream, const std::ostream::pos_type &startOffset, Diagnostics &diag)
This function helps to write the atom size after writing an atom to a stream.
static const CppUtilities::DateTime epoch
Dates within MP4 tracks are expressed as the number of seconds since this date.
Implementation of TagParser::AbstractTrack for the MP4 container.
static std::unique_ptr< Mpeg4VideoSpecificConfig > parseVideoSpecificConfig(CppUtilities::BinaryReader &reader, std::uint64_t startOffset, std::uint64_t size, Diagnostics &diag)
Parses the video specific configuration for the track.
static std::unique_ptr< Mpeg4ElementaryStreamInfo > parseMpeg4ElementaryStreamInfo(CppUtilities::BinaryReader &reader, Mp4Atom *esDescAtom, Diagnostics &diag)
Reads the MPEG-4 elementary stream descriptor for the track.
std::uint32_t chunkCount() const
Returns the number of chunks denoted by the stco atom.
std::vector< std::tuple< std::uint32_t, std::uint32_t, std::uint32_t > > readSampleToChunkTable(Diagnostics &diag)
Reads the sample to chunk table.
static void addInfo(const AvcConfiguration &avcConfig, AbstractTrack &track)
Adds the information from the specified avcConfig to the specified track.
std::vector< std::uint64_t > readChunkSizes(TagParser::Diagnostics &diag)
Reads the chunk sizes from the stsz (sample sizes) and stsc (samples per chunk) atom.
void updateChunkOffsets(const std::vector< std::int64_t > &oldMdatOffsets, const std::vector< std::int64_t > &newMdatOffsets)
Updates the chunk offsets of the track.
void internalParseHeader(Diagnostics &diag, AbortableProgressFeedback &progress) override
This method is internally called to parse header information.
void makeSampleTable(Diagnostics &diag)
Makes the sample table (stbl atom) for the track.
std::uint64_t requiredSize(Diagnostics &diag) const
Returns the number of bytes written when calling makeTrack().
TrackType type() const override
Returns the type of the track if known; otherwise returns TrackType::Unspecified.
std::uint32_t sampleToChunkEntryCount() const
Returns the number of "sample to chunk" entries within the stsc atom.
void makeMedia(Diagnostics &diag)
Makes the media information (mdia atom) for the track.
std::vector< std::uint64_t > readChunkOffsets(bool parseFragments, Diagnostics &diag)
Reads the chunk offsets from the stco atom and fragments if parseFragments is true.
unsigned int chunkOffsetSize() const
Returns the size of a single chunk offset denotation within the stco atom.
~Mp4Track() override
Destroys the track.
void makeTrackHeader(Diagnostics &diag)
Makes the track header (tkhd atom) for the track.
void bufferTrackAtoms(Diagnostics &diag)
Buffers all atoms required by the makeTrack() method.
void makeMediaInfo(Diagnostics &diag)
Makes a media information (minf atom) for the track.
Mp4Atom & trakAtom()
Returns the trak atom for the current instance.
void makeTrack(Diagnostics &diag)
Makes the track entry ("trak"-atom) for the track.
static std::unique_ptr< Mpeg4AudioSpecificConfig > parseAudioSpecificConfig(std::istream &stream, std::uint64_t startOffset, std::uint64_t size, Diagnostics &diag)
Parses the audio specific configuration for the track.
void updateChunkOffset(std::uint32_t chunkIndex, std::uint64_t offset)
Updates a particular chunk offset.
Mpeg4AudioSpecificConfig()
The Mpeg4Descriptor class helps to parse MPEG-4 descriptors.
Mpeg4VideoSpecificConfig()
static void addInfo(const MpegAudioFrame &frame, AbstractTrack &track)
Adds the information from the specified frame to the specified track.
The MpegAudioFrame class is used to parse MPEG audio frames.
void parseHeader(CppUtilities::BinaryReader &reader, Diagnostics &diag)
Parses the header read using the specified reader.
This exception is thrown when the an operation is invoked that has not been implemented yet.
void setWidth(std::uint32_t value)
Sets the width.
void setHeight(std::uint32_t value)
Sets the height.
The exception that is thrown when the data to be parsed is truncated and therefore can not be parsed ...
TAG_PARSER_EXPORT MediaFormat fourccToMediaFormat(std::uint32_t fourccId)
constexpr TAG_PARSER_EXPORT std::string_view language()
@ CompositionTimeToSample
@ Mpeg4ElementaryStreamDescriptor
@ Mpeg4ElementaryStreamDescriptor2
TAG_PARSER_EXPORT MediaFormat idToMediaFormat(std::uint8_t mpeg4AudioObjectId, bool sbrPresent=false, bool psPresent=false)
@ Mpeg2AacLowComplexityProfile
@ Mpeg2AacScaleableSamplingRateProfile
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
const LocaleDetail & abbreviatedName(LocaleFormat format) const
Returns the abbreviated name of the specified format.
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