7 #include "../avc/avcconfiguration.h" 9 #include "../mpegaudio/mpegaudioframe.h" 10 #include "../mpegaudio/mpegaudioframestream.h" 12 #include "../exceptions.h" 13 #include "../mediaformat.h" 15 #include <c++utilities/conversion/stringbuilder.h> 16 #include <c++utilities/io/binaryreader.h> 17 #include <c++utilities/io/binarywriter.h> 18 #include <c++utilities/io/bitreader.h> 19 #include <c++utilities/io/catchiofailure.h> 55 byte additionalDataOffset;
60 inline TrackHeaderInfo::TrackHeaderInfo() :
62 canUseExisting(false),
65 versionUnknown(false),
80 sampleFrequencyIndex(0xF),
82 channelConfiguration(0),
83 extensionAudioObjectType(0),
86 extensionSampleFrequencyIndex(0xF),
87 extensionSampleFrequency(0),
88 extensionChannelConfiguration(0),
89 frameLengthFlag(false),
90 dependsOnCoreCoder(false),
130 m_trakAtom(&trakAtom),
143 m_framesPerSample(1),
144 m_chunkOffsetSize(4),
146 m_sampleToChunkEntryCount(0)
188 static const string context(
"reading chunk offset table of MP4 track");
193 vector<uint64> offsets;
196 uint64 actualTableSize = m_stcoAtom->
dataSize();
201 actualTableSize -= 8;
205 if(calculatedTableSize < actualTableSize) {
207 }
else if(calculatedTableSize > actualTableSize) {
209 actualChunkCount = floor(static_cast<double>(actualTableSize) / static_cast<double>(
chunkOffsetSize()));
212 offsets.reserve(actualChunkCount);
216 for(uint32 i = 0; i < actualChunkCount; ++i) {
217 offsets.push_back(
reader().readUInt32BE());
221 for(uint32 i = 0; i < actualChunkCount; ++i) {
222 offsets.push_back(
reader().readUInt64BE());
232 uint64 totalDuration = 0;
239 uint32 calculatedDataSize = 0;
240 if(tfhdAtom->dataSize() < calculatedDataSize) {
244 const uint32 flags =
reader().readUInt24BE();
246 if(flags & 0x000001) {
247 calculatedDataSize += 8;
249 if(flags & 0x000002) {
250 calculatedDataSize += 4;
252 if(flags & 0x000008) {
253 calculatedDataSize += 4;
255 if(flags & 0x000010) {
256 calculatedDataSize += 4;
258 if(flags & 0x000020) {
259 calculatedDataSize += 4;
264 uint32 defaultSampleDuration = 0;
265 uint32 defaultSampleSize = 0;
267 if(tfhdAtom->dataSize() < calculatedDataSize) {
270 if(flags & 0x000001) {
274 if(flags & 0x000002) {
278 if(flags & 0x000008) {
279 defaultSampleDuration =
reader().readUInt32BE();
282 if(flags & 0x000010) {
283 defaultSampleSize =
reader().readUInt32BE();
285 if(flags & 0x000020) {
291 uint32 calculatedDataSize = 8;
292 if(trunAtom->dataSize() < calculatedDataSize) {
296 uint32 flags =
reader().readUInt24BE();
299 if(flags & 0x000001) {
300 calculatedDataSize += 4;
302 if(flags & 0x000004) {
303 calculatedDataSize += 4;
305 uint32 entrySize = 0;
306 if(flags & 0x000100) {
309 if(flags & 0x000200) {
312 if(flags & 0x000400) {
315 if(flags & 0x000800) {
319 if(trunAtom->dataSize() < calculatedDataSize) {
322 if(flags & 0x000001) {
326 if(flags & 0x000004) {
330 if(flags & 0x000100) {
331 totalDuration +=
reader().readUInt32BE();
333 totalDuration += defaultSampleDuration;
335 if(flags & 0x000200) {
336 m_sampleSizes.push_back(
reader().readUInt32BE());
337 m_size += m_sampleSizes.back();
339 m_size += defaultSampleSize;
341 if(flags & 0x000400) {
344 if(flags & 0x000800) {
351 if(m_sampleSizes.empty() && defaultSampleSize) {
352 m_sampleSizes.push_back(defaultSampleSize);
367 uint64 Mp4Track::accumulateSampleSizes(
size_t &sampleIndex,
size_t count)
369 if(sampleIndex + count <= m_sampleSizes.size()) {
371 for(
size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
372 sum += m_sampleSizes[sampleIndex];
375 }
else if(m_sampleSizes.size() == 1) {
376 sampleIndex += count;
377 return static_cast<uint64
>(m_sampleSizes.front()) * count;
380 throw InvalidDataException();
392 void Mp4Track::addChunkSizeEntries(std::vector<uint64> &chunkSizeTable,
size_t count,
size_t &sampleIndex, uint32 sampleCount)
394 for(
size_t i = 0; i < count; ++i) {
395 chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex,
sampleCount));
403 TrackHeaderInfo Mp4Track::verifyPresentTrackHeader()
const 405 TrackHeaderInfo info;
413 info.discardBuffer = m_tkhdAtom->
buffer() ==
nullptr;
414 if(info.discardBuffer) {
419 switch(info.version = static_cast<byte>(m_tkhdAtom->
buffer()[m_tkhdAtom->
headerSize()])) {
421 info.additionalDataOffset = 32;
424 info.additionalDataOffset = 44;
427 info.additionalDataOffset = 44;
428 info.versionUnknown =
true;
432 if(info.additionalDataOffset + 48u <= m_tkhdAtom->dataSize()) {
433 info.canUseExisting =
true;
435 info.truncated =
true;
436 info.canUseExisting = info.additionalDataOffset < m_tkhdAtom->
dataSize();
437 if(!info.canUseExisting && info.discardBuffer) {
443 info.requiredSize = m_tkhdAtom->
dataSize() + 8;
444 if(info.version == 0) {
446 info.requiredSize += 12;
448 if(info.requiredSize > numeric_limits<uint32>::max()) {
450 info.requiredSize += 8;
464 static const string context(
"reading sample to chunk table of MP4 track");
470 uint64 actualTableSize = m_stscAtom->
dataSize();
471 if(actualTableSize < 20) {
475 actualTableSize -= 8;
478 uint64 calculatedTableSize = actualSampleToChunkEntryCount * 12;
479 if(calculatedTableSize < actualTableSize) {
481 }
else if(calculatedTableSize > actualTableSize) {
483 actualSampleToChunkEntryCount = floor(static_cast<double>(actualTableSize) / 12.0);
486 vector<tuple<uint32, uint32, uint32> > sampleToChunkTable;
487 sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
489 for(uint32 i = 0; i < actualSampleToChunkEntryCount; ++i) {
491 uint32 firstChunk =
reader().readUInt32BE();
492 uint32 samplesPerChunk =
reader().readUInt32BE();
493 uint32 sampleDescriptionIndex =
reader().readUInt32BE();
494 sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
496 return sampleToChunkTable;
513 static const string context(
"reading chunk sizes of MP4 track");
521 vector<uint64> chunkSizes;
522 if(!sampleToChunkTable.empty()) {
524 auto tableIterator = sampleToChunkTable.cbegin();
525 chunkSizes.reserve(m_chunkCount);
527 size_t sampleIndex = 0;
528 uint32 previousChunkIndex = get<0>(*tableIterator);
529 if(previousChunkIndex != 1) {
531 previousChunkIndex = 1;
533 uint32 samplesPerChunk = get<1>(*tableIterator);
536 for(
const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
537 uint32 firstChunkIndex = get<0>(*tableIterator);
538 if(firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
539 addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk);
542 "The first chunk index of a \"sample to chunk\" entry must be greather than the first chunk of the previous entry and not greather than the chunk count.", context);
545 previousChunkIndex = firstChunkIndex;
546 samplesPerChunk = get<1>(*tableIterator);
548 if(m_chunkCount >= previousChunkIndex) {
549 addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk);
563 static const string context(
"parsing MPEG-4 elementary stream descriptor");
564 using namespace Mpeg4ElementaryStreamObjectIds;
565 unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
569 if(
reader.readUInt32BE() != 0) {
582 reader.stream()->seekg(esDesc.dataOffset());
583 esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
584 esInfo->id =
reader.readUInt16BE();
585 esInfo->esDescFlags =
reader.readByte();
586 if(esInfo->dependencyFlag()) {
587 esInfo->dependsOnId =
reader.readUInt16BE();
589 if(esInfo->urlFlag()) {
592 if(esInfo->ocrFlag()) {
593 esInfo->ocrId =
reader.readUInt16BE();
595 for(
Mpeg4Descriptor *esDescChild = esDesc.
denoteFirstChild(static_cast<uint64>(
reader.stream()->tellg()) - esDesc.startOffset()); esDescChild; esDescChild = esDescChild->nextSibling()) {
596 esDescChild->parse();
597 switch(esDescChild->id()) {
600 reader.stream()->seekg(esDescChild->dataOffset());
601 esInfo->objectTypeId =
reader.readByte();
602 esInfo->decCfgDescFlags =
reader.readByte();
603 esInfo->bufferSize =
reader.readUInt24BE();
604 esInfo->maxBitrate =
reader.readUInt32BE();
605 esInfo->averageBitrate =
reader.readUInt32BE();
606 for(
Mpeg4Descriptor *decCfgDescChild = esDescChild->
denoteFirstChild(esDescChild->headerSize() + 13); decCfgDescChild; decCfgDescChild = decCfgDescChild->nextSibling()) {
607 decCfgDescChild->parse();
608 switch(decCfgDescChild->id()) {
611 switch(esInfo->objectTypeId) {
614 esInfo->audioSpecificConfig =
parseAudioSpecificConfig(statusProvider, *
reader.stream(), decCfgDescChild->dataOffset(), decCfgDescChild->dataSize());
648 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
649 using namespace Mpeg4AudioObjectIds;
652 auto buff = make_unique<char []>(
size);
653 stream.read(buff.get(),
size);
654 BitReader bitReader(buff.get(),
size);
655 auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
658 auto getAudioObjectType = [&bitReader] {
659 byte objType = bitReader.readBits<byte>(5);
661 objType = 32 + bitReader.readBits<byte>(6);
665 audioCfg->audioObjectType = getAudioObjectType();
667 if((audioCfg->sampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
668 audioCfg->sampleFrequency = bitReader.readBits<uint32>(24);
671 audioCfg->channelConfiguration = bitReader.readBits<byte>(4);
673 switch(audioCfg->audioObjectType) {
676 audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
677 audioCfg->sbrPresent =
true;
678 if((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
679 audioCfg->extensionSampleFrequency = bitReader.readBits<uint32>(24);
681 if((audioCfg->audioObjectType = getAudioObjectType()) ==
ErBsac) {
682 audioCfg->extensionChannelConfiguration = bitReader.readBits<byte>(4);
686 switch(audioCfg->extensionAudioObjectType) {
688 audioCfg->psPresent =
true;
693 switch(audioCfg->audioObjectType) {
697 audioCfg->frameLengthFlag = bitReader.readBits<byte>(1);
698 if((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
699 audioCfg->coreCoderDelay = bitReader.readBits<byte>(14);
701 audioCfg->extensionFlag = bitReader.readBit();
702 if(audioCfg->channelConfiguration == 0) {
705 switch(audioCfg->audioObjectType) {
707 audioCfg->layerNr = bitReader.readBits<byte>(3);
712 if(audioCfg->extensionFlag == 1) {
713 switch(audioCfg->audioObjectType) {
715 audioCfg->numOfSubFrame = bitReader.readBits<byte>(5);
716 audioCfg->layerLength = bitReader.readBits<uint16>(11);
719 audioCfg->resilienceFlags = bitReader.readBits<byte>(3);
724 if(bitReader.readBit() == 1) {
733 switch(audioCfg->audioObjectType) {
737 switch(audioCfg->epConfig = bitReader.readBits<byte>(2)) {
741 bitReader.skipBits(1);
748 if(audioCfg->extensionAudioObjectType !=
Sbr && audioCfg->extensionAudioObjectType !=
Ps && bitReader.bitsAvailable() >= 16) {
749 uint16 syncExtensionType = bitReader.readBits<uint16>(11);
750 if(syncExtensionType == 0x2B7) {
751 if((audioCfg->extensionAudioObjectType = getAudioObjectType()) ==
Sbr) {
752 if((audioCfg->sbrPresent = bitReader.readBit())) {
753 if((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
754 audioCfg->extensionSampleFrequency = bitReader.readBits<uint32>(24);
756 if(bitReader.bitsAvailable() >= 12) {
757 if((syncExtensionType = bitReader.readBits<uint16>(11)) == 0x548) {
758 audioCfg->psPresent = bitReader.readBits<byte>(1);
762 }
else if(audioCfg->extensionAudioObjectType ==
ErBsac) {
763 if((audioCfg->sbrPresent = bitReader.readBit())) {
764 if((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
765 audioCfg->extensionSampleFrequency = bitReader.readBits<uint32>(24);
768 audioCfg->extensionChannelConfiguration = bitReader.readBits<byte>(4);
770 }
else if (syncExtensionType == 0x548) {
771 audioCfg->psPresent = bitReader.readBit();
777 const char *what = catchIoFailure();
780 throwIoFailure(what);
797 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
798 using namespace Mpeg4AudioObjectIds;
799 auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
802 if(
size > 3 && (
reader.readUInt24BE() == 1)) {
807 switch(
reader.readByte()) {
810 videoCfg->profile =
reader.readByte();
820 if((buff1 =
reader.readUInt24BE()) != 1) {
821 reader.stream()->seekg(-2, ios_base::cur);
822 videoCfg->userData.push_back(buff1 >> 16);
829 if(buff1 != 1 &&
size > 0) {
830 videoCfg->userData +=
reader.readString(
size);
839 if(
reader.readUInt24BE() != 1) {
840 reader.stream()->seekg(-2, ios_base::cur);
876 if(oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
879 static const unsigned int stcoDataBegin = 8;
880 uint64 startPos = m_stcoAtom->
dataOffset() + stcoDataBegin;
881 uint64 endPos = startPos + m_stcoAtom->
dataSize() - stcoDataBegin;
884 vector<int64>::size_type i;
885 vector<int64>::size_type
size;
887 switch(m_stcoAtom->
id()) {
890 while((currentPos + 4) <= endPos) {
892 for(i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
893 if(off > static_cast<uint64>(oldMdatOffsets[i])) {
894 off += (newMdatOffsets[i] - oldMdatOffsets[i]);
905 while((currentPos + 8) <= endPos) {
907 for(i = 0,
size = oldMdatOffsets.size(); i <
size; ++i) {
908 if(off > static_cast<uint64>(oldMdatOffsets[i])) {
909 off += (newMdatOffsets[i] - oldMdatOffsets[i]);
945 switch(m_stcoAtom->
id()) {
947 for(
auto offset : chunkOffsets) {
952 for(
auto offset : chunkOffsets) {
980 writer().writeUInt32BE(offset);
983 writer().writeUInt64BE(offset);
1037 trefAtom->makeBuffer();
1040 edtsAtom->makeBuffer();
1044 vmhdAtom->makeBuffer();
1047 smhdAtom->makeBuffer();
1050 hmhdAtom->makeBuffer();
1053 nmhdAtom->makeBuffer();
1056 dinfAtom->makeBuffer();
1059 stblAtom->makeBuffer();
1073 size += verifyPresentTrackHeader().requiredSize;
1076 size += trefAtom->totalSize();
1080 size += edtsAtom->totalSize();
1085 bool dinfAtomWritten =
false;
1088 size += vmhdAtom->totalSize();
1091 size += smhdAtom->totalSize();
1094 size += hmhdAtom->totalSize();
1097 size += nmhdAtom->totalSize();
1100 size += dinfAtom->totalSize();
1101 dinfAtomWritten =
true;
1104 size += stblAtom->totalSize();
1107 if(!dinfAtomWritten) {
1124 ostream::pos_type trakStartOffset =
outputStream().tellp();
1153 if(info.versionUnknown) {
1155 argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1157 if(info.truncated) {
1159 argsToString(
"making \"tkhd\"-atom of track ",
m_id));
1163 if(info.requiredSize > numeric_limits<uint32>::max()) {
1164 writer().writeUInt32BE(1);
1166 writer().writeUInt64BE(info.requiredSize);
1168 writer().writeUInt32BE(static_cast<uint32>(info.requiredSize));
1184 writer().writeUInt24BE(flags);
1191 writer().writeUInt32BE(static_cast<uint32>(
m_id));
1192 writer().writeUInt32BE(0);
1194 writer().writeUInt32BE(0);
1195 writer().writeUInt32BE(0);
1198 if(info.canUseExisting) {
1202 if(info.discardBuffer) {
1207 writer().writeInt16BE(0);
1208 writer().writeInt16BE(0);
1209 writer().writeFixed8BE(1.0);
1210 writer().writeUInt16BE(0);
1211 for(
const int32 value : {0x00010000,0,0,0,0x00010000,0,0,0,0x40000000}) {
1212 writer().writeInt32BE(value);
1214 writer().writeFixed16BE(1.0);
1215 writer().writeFixed16BE(1.0);
1225 ostream::pos_type mdiaStartOffset =
outputStream().tellp();
1226 writer().writeUInt32BE(0);
1229 writer().writeUInt32BE(44);
1232 writer().writeUInt24BE(0);
1239 for(
size_t charIndex = 0; charIndex != 3; ++charIndex) {
1241 if(langChar >=
'a' && langChar <=
'z') {
1242 language |=
static_cast<uint16
>(langChar - 0x60) << (0xA - charIndex * 0x5);
1253 writer().writeUInt16BE(0);
1257 writer().writeUInt64BE(0);
1276 for(
int i = 0; i < 3; ++i)
writer().writeUInt32BE(0);
1290 ostream::pos_type minfStartOffset =
outputStream().tellp();
1291 writer().writeUInt32BE(0);
1293 bool dinfAtomWritten =
false;
1314 dinfAtomWritten =
true;
1318 if(!dinfAtomWritten) {
1319 writer().writeUInt32BE(36);
1322 writer().writeUInt32BE(28);
1324 writer().writeUInt32BE(0);
1325 writer().writeUInt32BE(1);
1327 writer().writeUInt32BE(12);
1330 writer().writeUInt24BE(0x000001);
1334 bool stblAtomWritten =
false;
1338 stblAtomWritten =
true;
1341 if(!stblAtomWritten) {
1355 ostream::pos_type stblStartOffset =
outputStream().tellp();
1356 writer().writeUInt32BE(0);
1414 static const string context(
"parsing MP4 track");
1415 using namespace Mp4AtomIds;
1423 if(!(m_tkhdAtom = m_trakAtom->childById(
TrackHeader))) {
1427 if(!(m_mdiaAtom = m_trakAtom->childById(
Media))) {
1431 if(!(m_mdhdAtom = m_mdiaAtom->childById(
MediaHeader))) {
1443 if(!(m_stblAtom = m_minfAtom->childById(
SampleTable))) {
1468 BinaryReader &
reader = m_trakAtom->reader();
1471 m_istream->seekg(m_tkhdAtom->startOffset() + 8);
1472 byte atomVersion =
reader.readByte();
1473 uint32 flags =
reader.readUInt24BE();
1477 switch(atomVersion) {
1496 m_istream->seekg(m_mdhdAtom->dataOffset());
1497 atomVersion =
reader.readByte();
1499 switch(atomVersion) {
1513 addNotification(
NotificationType::Warning,
"Version of \"mdhd\"-atom not supported. It will be ignored. Creation time, modification time, time scale and duration might not be determined.", context);
1517 uint16 tmp =
reader.readUInt16BE();
1519 const char buff[] = {
1520 static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1521 static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1522 static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1531 m_istream->seekg(m_hdlrAtom->dataOffset() + 8);
1533 switch(
reader.readUInt32BE()) {
1543 case 0x6D657461:
case 0x74657874:
1551 if((tmp =
m_istream->peek()) == m_hdlrAtom->dataSize() - 12 - 4 - 8 - 1) {
1557 m_name =
reader.readTerminatedString(m_hdlrAtom->dataSize() - 12 - 4 - 8, 0);
1562 m_istream->seekg(m_stcoAtom->dataOffset() + 4);
1563 m_chunkCount =
reader.readUInt32BE();
1566 m_istream->seekg(m_stsdAtom->dataOffset() + 4);
1567 uint32 entryCount =
reader.readUInt32BE();
1568 Mp4Atom *esDescParentAtom =
nullptr;
1569 if(entryCount > 0) {
1571 for(
Mp4Atom *codecConfigContainerAtom = m_stsdAtom->
firstChild(); codecConfigContainerAtom; codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
1572 codecConfigContainerAtom->
parse();
1574 m_formatId = interpretIntegerAsString<uint32>(codecConfigContainerAtom->id());
1577 m_istream->seekg(codecConfigContainerAtom->dataOffset());
1578 switch(codecConfigContainerAtom->id()) {
1584 tmp =
reader.readUInt16BE();
1600 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1603 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1606 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1608 if(!esDescParentAtom) {
1609 esDescParentAtom = codecConfigContainerAtom;
1616 m_istream->seekg(6 + 2 + 16, ios_base::cur);
1622 m_framesPerSample =
reader.readUInt16BE();
1627 }
else if(tmp < 32) {
1631 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1632 if(!esDescParentAtom) {
1633 esDescParentAtom = codecConfigContainerAtom;
1638 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1639 if(!esDescParentAtom) {
1640 esDescParentAtom = codecConfigContainerAtom;
1652 if(esDescParentAtom) {
1655 m_istream->seekg(avcConfigAtom->dataOffset());
1656 m_avcConfig = make_unique<Media::AvcConfiguration>();
1658 m_avcConfig->parse(
reader, avcConfigAtom->dataSize());
1676 m_bitrate =
static_cast<double>(m_esInfo->averageBitrate) / 1000;
1677 m_maxBitrate =
static_cast<double>(m_esInfo->maxBitrate) / 1000;
1678 if(m_esInfo->audioSpecificConfig) {
1681 if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1688 if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1695 m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1698 if(m_esInfo->videoSpecificConfig) {
1701 m_format.
sub = m_esInfo->videoSpecificConfig->profile;
1702 if(!m_esInfo->videoSpecificConfig->userData.empty()) {
1704 m_formatId += m_esInfo->videoSpecificConfig->userData;
1712 m_istream->seekg(m_stcoAtom->dataOffset() + 8);
1731 m_sampleSizes.clear();
1733 uint64 actualSampleSizeTableSize = m_stszAtom->dataSize();
1734 if(actualSampleSizeTableSize < 12) {
1737 actualSampleSizeTableSize -= 12;
1738 m_istream->seekg(m_stszAtom->dataOffset() + 4);
1740 uint32 constantSize;
1744 fieldSize =
reader.readByte();
1747 constantSize =
reader.readUInt32BE();
1752 m_sampleSizes.push_back(constantSize);
1756 uint64 calculatedSampleSizeTableSize = ceil((0.125 * fieldSize) *
m_sampleCount);
1757 if(calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1759 }
else if(calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1761 actualSampleCount = floor(static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize));
1763 m_sampleSizes.reserve(actualSampleCount);
1767 for(; i <= actualSampleCount; i += 2) {
1768 byte val =
reader.readByte();
1769 m_sampleSizes.push_back(val >> 4);
1770 m_sampleSizes.push_back(val & 0xF0);
1771 m_size += (val >> 4) + (val & 0xF0);
1773 if(i <= actualSampleCount + 1) {
1774 m_sampleSizes.push_back(
reader.readByte() >> 4);
1775 m_size += m_sampleSizes.back();
1779 for(; i <= actualSampleCount; ++i) {
1780 m_sampleSizes.push_back(
reader.readByte());
1781 m_size += m_sampleSizes.back();
1785 for(; i <= actualSampleCount; ++i) {
1786 m_sampleSizes.push_back(
reader.readUInt16BE());
1787 m_size += m_sampleSizes.back();
1791 for(; i <= actualSampleCount; ++i) {
1792 m_sampleSizes.push_back(
reader.readUInt32BE());
1793 m_size += m_sampleSizes.back();
1803 uint64 totalDuration = 0;
1810 uint32 calculatedDataSize = 0;
1811 if(tfhdAtom->dataSize() < calculatedDataSize) {
1814 m_istream->seekg(tfhdAtom->dataOffset() + 1);
1815 uint32 flags =
reader.readUInt24BE();
1817 if(flags & 0x000001) {
1818 calculatedDataSize += 8;
1820 if(flags & 0x000002) {
1821 calculatedDataSize += 4;
1823 if(flags & 0x000008) {
1824 calculatedDataSize += 4;
1826 if(flags & 0x000010) {
1827 calculatedDataSize += 4;
1829 if(flags & 0x000020) {
1830 calculatedDataSize += 4;
1834 uint32 defaultSampleDuration = 0;
1835 uint32 defaultSampleSize = 0;
1837 if(tfhdAtom->dataSize() < calculatedDataSize) {
1840 if(flags & 0x000001) {
1844 if(flags & 0x000002) {
1848 if(flags & 0x000008) {
1849 defaultSampleDuration =
reader.readUInt32BE();
1852 if(flags & 0x000010) {
1853 defaultSampleSize =
reader.readUInt32BE();
1855 if(flags & 0x000020) {
1861 uint32 calculatedDataSize = 8;
1862 if(trunAtom->dataSize() < calculatedDataSize) {
1865 m_istream->seekg(trunAtom->dataOffset() + 1);
1866 uint32 flags =
reader.readUInt24BE();
1869 if(flags & 0x000001) {
1870 calculatedDataSize += 4;
1872 if(flags & 0x000004) {
1873 calculatedDataSize += 4;
1875 uint32 entrySize = 0;
1876 if(flags & 0x000100) {
1879 if(flags & 0x000200) {
1882 if(flags & 0x000400) {
1885 if(flags & 0x000800) {
1889 if(trunAtom->dataSize() < calculatedDataSize) {
1892 if(flags & 0x000001) {
1896 if(flags & 0x000004) {
1900 if(flags & 0x000100) {
1901 totalDuration +=
reader.readUInt32BE();
1903 totalDuration += defaultSampleDuration;
1905 if(flags & 0x000200) {
1906 m_sampleSizes.push_back(
reader.readUInt32BE());
1907 m_size += m_sampleSizes.back();
1909 m_size += defaultSampleSize;
1911 if(flags & 0x000400) {
1914 if(flags & 0x000800) {
1921 if(m_sampleSizes.empty() && defaultSampleSize) {
1922 m_sampleSizes.push_back(defaultSampleSize);
1937 m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(
timeScale));
1942 if(m_bitrate < 0.01 && m_bitrate > -0.01) {
1947 m_istream->seekg(m_stscAtom->dataOffset() + 4);
1948 m_sampleToChunkEntryCount =
reader.readUInt32BE();
Contains utility classes helping to read and write streams.