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> 40 Mpeg4AudioSpecificConfig::Mpeg4AudioSpecificConfig() :
42 sampleFrequencyIndex(0xF),
44 channelConfiguration(0),
45 extensionAudioObjectType(0),
48 extensionSampleFrequencyIndex(0xF),
49 extensionSampleFrequency(0),
50 extensionChannelConfiguration(0),
51 frameLengthFlag(false),
52 dependsOnCoreCoder(false),
92 m_trakAtom(&trakAtom),
105 m_framesPerSample(1),
106 m_chunkOffsetSize(4),
108 m_sampleToChunkEntryCount(0)
150 static const string context(
"reading chunk offset table of MP4 track");
155 vector<uint64> offsets;
158 uint64 actualTableSize = m_stcoAtom->
dataSize();
163 actualTableSize -= 8;
167 if(calculatedTableSize < actualTableSize) {
169 }
else if(calculatedTableSize > actualTableSize) {
171 actualChunkCount = floor(static_cast<double>(actualTableSize) / static_cast<double>(
chunkOffsetSize()));
174 offsets.reserve(actualChunkCount);
178 for(uint32 i = 0; i < actualChunkCount; ++i) {
179 offsets.push_back(
reader().readUInt32BE());
183 for(uint32 i = 0; i < actualChunkCount; ++i) {
184 offsets.push_back(
reader().readUInt64BE());
194 uint64 totalDuration = 0;
201 uint32 calculatedDataSize = 0;
202 if(tfhdAtom->dataSize() < calculatedDataSize) {
206 const uint32 flags =
reader().readUInt24BE();
208 if(flags & 0x000001) {
209 calculatedDataSize += 8;
211 if(flags & 0x000002) {
212 calculatedDataSize += 4;
214 if(flags & 0x000008) {
215 calculatedDataSize += 4;
217 if(flags & 0x000010) {
218 calculatedDataSize += 4;
220 if(flags & 0x000020) {
221 calculatedDataSize += 4;
225 uint32 defaultSampleDuration = 0;
226 uint32 defaultSampleSize = 0;
227 uint32 defaultSampleFlags = 0;
228 if(tfhdAtom->dataSize() < calculatedDataSize) {
231 if(flags & 0x000001) {
235 if(flags & 0x000002) {
239 if(flags & 0x000008) {
240 defaultSampleDuration =
reader().readUInt32BE();
243 if(flags & 0x000010) {
244 defaultSampleSize =
reader().readUInt32BE();
246 if(flags & 0x000020) {
247 defaultSampleFlags =
reader().readUInt32BE();
252 uint32 calculatedDataSize = 8;
253 if(trunAtom->dataSize() < calculatedDataSize) {
257 uint32 flags =
reader().readUInt24BE();
260 if(flags & 0x000001) {
261 calculatedDataSize += 4;
263 if(flags & 0x000004) {
264 calculatedDataSize += 4;
266 uint32 entrySize = 0;
267 if(flags & 0x000100) {
270 if(flags & 0x000200) {
273 if(flags & 0x000400) {
276 if(flags & 0x000800) {
280 if(trunAtom->dataSize() < calculatedDataSize) {
283 if(flags & 0x000001) {
287 if(flags & 0x000004) {
291 if(flags & 0x000100) {
292 totalDuration +=
reader().readUInt32BE();
294 totalDuration += defaultSampleDuration;
296 if(flags & 0x000200) {
297 m_sampleSizes.push_back(
reader().readUInt32BE());
298 m_size += m_sampleSizes.back();
300 m_size += defaultSampleSize;
302 if(flags & 0x000400) {
305 if(flags & 0x000800) {
312 if(m_sampleSizes.empty() && defaultSampleSize) {
313 m_sampleSizes.push_back(defaultSampleSize);
328 uint64 Mp4Track::accumulateSampleSizes(
size_t &sampleIndex,
size_t count)
330 if(sampleIndex + count <= m_sampleSizes.size()) {
332 for(
size_t end = sampleIndex + count; sampleIndex < end; ++sampleIndex) {
333 sum += m_sampleSizes[sampleIndex];
336 }
else if(m_sampleSizes.size() == 1) {
337 sampleIndex += count;
338 return static_cast<uint64
>(m_sampleSizes.front()) * count;
353 void Mp4Track::addChunkSizeEntries(std::vector<uint64> &chunkSizeTable,
size_t count,
size_t &sampleIndex, uint32
sampleCount)
355 for(
size_t i = 0; i < count; ++i) {
356 chunkSizeTable.push_back(accumulateSampleSizes(sampleIndex, sampleCount));
369 static const string context(
"reading sample to chunk table of MP4 track");
375 uint64 actualTableSize = m_stscAtom->
dataSize();
376 if(actualTableSize < 20) {
380 actualTableSize -= 8;
383 uint64 calculatedTableSize = actualSampleToChunkEntryCount * 12;
384 if(calculatedTableSize < actualTableSize) {
386 }
else if(calculatedTableSize > actualTableSize) {
388 actualSampleToChunkEntryCount = floor(static_cast<double>(actualTableSize) / 12.0);
391 vector<tuple<uint32, uint32, uint32> > sampleToChunkTable;
392 sampleToChunkTable.reserve(actualSampleToChunkEntryCount);
394 for(uint32 i = 0; i < actualSampleToChunkEntryCount; ++i) {
396 uint32 firstChunk =
reader().readUInt32BE();
397 uint32 samplesPerChunk =
reader().readUInt32BE();
398 uint32 sampleDescriptionIndex =
reader().readUInt32BE();
399 sampleToChunkTable.emplace_back(firstChunk, samplesPerChunk, sampleDescriptionIndex);
401 return sampleToChunkTable;
418 static const string context(
"reading chunk sizes of MP4 track");
426 vector<uint64> chunkSizes;
427 if(!sampleToChunkTable.empty()) {
429 auto tableIterator = sampleToChunkTable.cbegin();
430 chunkSizes.reserve(m_chunkCount);
432 size_t sampleIndex = 0;
433 uint32 previousChunkIndex = get<0>(*tableIterator);
434 if(previousChunkIndex != 1) {
436 previousChunkIndex = 1;
438 uint32 samplesPerChunk = get<1>(*tableIterator);
441 for(
const auto tableEnd = sampleToChunkTable.cend(); tableIterator != tableEnd; ++tableIterator) {
442 uint32 firstChunkIndex = get<0>(*tableIterator);
443 if(firstChunkIndex > previousChunkIndex && firstChunkIndex <= m_chunkCount) {
444 addChunkSizeEntries(chunkSizes, firstChunkIndex - previousChunkIndex, sampleIndex, samplesPerChunk);
447 "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);
450 previousChunkIndex = firstChunkIndex;
451 samplesPerChunk = get<1>(*tableIterator);
453 if(m_chunkCount >= previousChunkIndex) {
454 addChunkSizeEntries(chunkSizes, m_chunkCount + 1 - previousChunkIndex, sampleIndex, samplesPerChunk);
468 static const string context(
"parsing MPEG-4 elementary stream descriptor");
469 using namespace Mpeg4ElementaryStreamObjectIds;
470 unique_ptr<Mpeg4ElementaryStreamInfo> esInfo;
472 reader.stream()->seekg(esDescAtom->
dataOffset());
474 if(reader.readUInt32BE() != 0) {
487 reader.stream()->seekg(esDesc.dataOffset());
488 esInfo = make_unique<Mpeg4ElementaryStreamInfo>();
489 esInfo->id = reader.readUInt16BE();
490 esInfo->esDescFlags = reader.readByte();
491 if(esInfo->dependencyFlag()) {
492 esInfo->dependsOnId = reader.readUInt16BE();
494 if(esInfo->urlFlag()) {
495 esInfo->url = reader.readString(reader.readByte());
497 if(esInfo->ocrFlag()) {
498 esInfo->ocrId = reader.readUInt16BE();
500 for(
Mpeg4Descriptor *esDescChild = esDesc.
denoteFirstChild(static_cast<uint64>(reader.stream()->tellg()) - esDesc.startOffset()); esDescChild; esDescChild = esDescChild->nextSibling()) {
501 esDescChild->parse();
502 switch(esDescChild->id()) {
505 reader.stream()->seekg(esDescChild->dataOffset());
506 esInfo->objectTypeId = reader.readByte();
507 esInfo->decCfgDescFlags = reader.readByte();
508 esInfo->bufferSize = reader.readUInt24BE();
509 esInfo->maxBitrate = reader.readUInt32BE();
510 esInfo->averageBitrate = reader.readUInt32BE();
511 for(
Mpeg4Descriptor *decCfgDescChild = esDescChild->
denoteFirstChild(esDescChild->headerSize() + 13); decCfgDescChild; decCfgDescChild = decCfgDescChild->nextSibling()) {
512 decCfgDescChild->parse();
513 switch(decCfgDescChild->id()) {
516 switch(esInfo->objectTypeId) {
519 esInfo->audioSpecificConfig =
parseAudioSpecificConfig(statusProvider, *reader.stream(), decCfgDescChild->dataOffset(), decCfgDescChild->dataSize());
522 esInfo->videoSpecificConfig =
parseVideoSpecificConfig(statusProvider, reader, decCfgDescChild->dataOffset(), decCfgDescChild->dataSize());
553 static const string context(
"parsing MPEG-4 audio specific config from elementary stream descriptor");
554 using namespace Mpeg4AudioObjectIds;
556 stream.seekg(startOffset);
557 auto buff = make_unique<char []>(
size);
558 stream.read(buff.get(),
size);
559 BitReader bitReader(buff.get(),
size);
560 auto audioCfg = make_unique<Mpeg4AudioSpecificConfig>();
563 auto getAudioObjectType = [&audioCfg, &bitReader] {
564 byte objType = bitReader.readBits<byte>(5);
566 objType = 32 + bitReader.readBits<byte>(6);
570 audioCfg->audioObjectType = getAudioObjectType();
572 if((audioCfg->sampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
573 audioCfg->sampleFrequency = bitReader.readBits<uint32>(24);
576 audioCfg->channelConfiguration = bitReader.readBits<byte>(4);
578 switch(audioCfg->audioObjectType) {
581 audioCfg->extensionAudioObjectType = audioCfg->audioObjectType;
582 audioCfg->sbrPresent =
true;
583 if((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
584 audioCfg->extensionSampleFrequency = bitReader.readBits<uint32>(24);
586 if((audioCfg->audioObjectType = getAudioObjectType()) ==
ErBsac) {
587 audioCfg->extensionChannelConfiguration = bitReader.readBits<byte>(4);
591 switch(audioCfg->extensionAudioObjectType) {
593 audioCfg->psPresent =
true;
598 switch(audioCfg->audioObjectType) {
602 audioCfg->frameLengthFlag = bitReader.readBits<byte>(1);
603 if((audioCfg->dependsOnCoreCoder = bitReader.readBit())) {
604 audioCfg->coreCoderDelay = bitReader.readBits<byte>(14);
606 audioCfg->extensionFlag = bitReader.readBit();
607 if(audioCfg->channelConfiguration == 0) {
610 switch(audioCfg->audioObjectType) {
612 audioCfg->layerNr = bitReader.readBits<byte>(3);
617 if(audioCfg->extensionFlag == 1) {
618 switch(audioCfg->audioObjectType) {
620 audioCfg->numOfSubFrame = bitReader.readBits<byte>(5);
621 audioCfg->layerLength = bitReader.readBits<uint16>(11);
624 audioCfg->resilienceFlags = bitReader.readBits<byte>(3);
629 if(bitReader.readBit() == 1) {
638 switch(audioCfg->audioObjectType) {
642 switch(audioCfg->epConfig = bitReader.readBits<byte>(2)) {
646 bitReader.skipBits(1);
653 if(audioCfg->extensionAudioObjectType !=
Sbr && audioCfg->extensionAudioObjectType !=
Ps && bitReader.bitsAvailable() >= 16) {
654 uint16 syncExtensionType = bitReader.readBits<uint16>(11);
655 if(syncExtensionType == 0x2B7) {
656 if((audioCfg->extensionAudioObjectType = getAudioObjectType()) ==
Sbr) {
657 if((audioCfg->sbrPresent = bitReader.readBit())) {
658 if((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
659 audioCfg->extensionSampleFrequency = bitReader.readBits<uint32>(24);
661 if(bitReader.bitsAvailable() >= 12) {
662 if((syncExtensionType = bitReader.readBits<uint16>(11)) == 0x548) {
663 audioCfg->psPresent = bitReader.readBits<byte>(1);
667 }
else if(audioCfg->extensionAudioObjectType ==
ErBsac) {
668 if((audioCfg->sbrPresent = bitReader.readBit())) {
669 if((audioCfg->extensionSampleFrequencyIndex = bitReader.readBits<byte>(4)) == 0xF) {
670 audioCfg->extensionSampleFrequency = bitReader.readBits<uint32>(24);
673 audioCfg->extensionChannelConfiguration = bitReader.readBits<byte>(4);
675 }
else if (syncExtensionType == 0x548) {
676 audioCfg->psPresent = bitReader.readBit();
682 const char *what = catchIoFailure();
685 throwIoFailure(what);
702 static const string context(
"parsing MPEG-4 video specific config from elementary stream descriptor");
703 using namespace Mpeg4AudioObjectIds;
704 auto videoCfg = make_unique<Mpeg4VideoSpecificConfig>();
706 reader.stream()->seekg(startOffset);
707 if(size > 3 && (reader.readUInt24BE() == 1)) {
712 switch(reader.readByte()) {
715 videoCfg->profile = reader.readByte();
725 if((buff1 = reader.readUInt24BE()) != 1) {
726 reader.stream()->seekg(-2, ios_base::cur);
727 videoCfg->userData.push_back(buff1 >> 16);
734 if(buff1 != 1 && size > 0) {
735 videoCfg->userData += reader.readString(size);
744 if(reader.readUInt24BE() != 1) {
745 reader.stream()->seekg(-2, ios_base::cur);
781 if(oldMdatOffsets.size() == 0 || oldMdatOffsets.size() != newMdatOffsets.size()) {
784 static const unsigned int stcoDataBegin = 8;
785 uint64 startPos = m_stcoAtom->dataOffset() + stcoDataBegin;
786 uint64 endPos = startPos + m_stcoAtom->dataSize() - stcoDataBegin;
789 vector<int64>::size_type i;
790 vector<int64>::size_type
size;
792 switch(m_stcoAtom->id()) {
795 while((currentPos + 4) <= endPos) {
797 for(i = 0, size = oldMdatOffsets.size(); i <
size; ++i) {
798 if(off > static_cast<uint64>(oldMdatOffsets[i])) {
799 off += (newMdatOffsets[i] - oldMdatOffsets[i]);
810 while((currentPos + 8) <= endPos) {
812 for(i = 0, size = oldMdatOffsets.size(); i <
size; ++i) {
813 if(off > static_cast<uint64>(oldMdatOffsets[i])) {
814 off += (newMdatOffsets[i] - oldMdatOffsets[i]);
849 m_ostream->seekp(m_stcoAtom->dataOffset() + 8);
850 switch(m_stcoAtom->id()) {
852 for(
auto offset : chunkOffsets) {
857 for(
auto offset : chunkOffsets) {
885 writer().writeUInt32BE(offset);
888 writer().writeUInt64BE(offset);
939 uint64
size = 8 + 100;
942 size += trefAtom->totalSize();
946 size += edtsAtom->totalSize();
949 size += 8 + 44 + (33 +
m_name.size()) + 8;
951 bool dinfAtomWritten =
false;
954 size += vmhdAtom->totalSize();
957 size += smhdAtom->totalSize();
960 size += hmhdAtom->totalSize();
963 size += nmhdAtom->totalSize();
966 size += dinfAtom->totalSize();
967 dinfAtomWritten =
true;
970 size += stblAtom->totalSize();
973 if(!dinfAtomWritten) {
987 ostream::pos_type trakStartOffset =
outputStream().tellp();
1012 writer().writeUInt32BE(100);
1025 writer().writeUInt24BE(flags);
1029 writer().writeUInt32BE(0);
1031 writer().writeUInt32BE(0);
1032 writer().writeUInt32BE(0);
1036 m_istream->seekg(m_tkhdAtom->startOffset() + 52);
1037 m_istream->read(buffer,
sizeof(buffer));
1038 m_ostream->write(buffer,
sizeof(buffer));
1041 writer().writeInt16BE(0);
1042 writer().writeInt16BE(0);
1043 writer().writeFixed8BE(1.0);
1044 writer().writeUInt16BE(0);
1045 for(int32 value : {0x00010000,0,0,0,0x00010000,0,0,0,0x40000000}) {
1046 writer().writeInt32BE(value);
1048 writer().writeFixed16BE(1.0);
1049 writer().writeFixed16BE(1.0);
1059 ostream::pos_type mdiaStartOffset =
outputStream().tellp();
1060 writer().writeUInt32BE(0);
1063 writer().writeUInt32BE(44);
1066 writer().writeUInt24BE(0);
1073 for(
size_t charIndex = 0; charIndex <
m_language.size() && charIndex != 3; ++charIndex) {
1075 if(langChar >=
'a' && langChar <=
'z') {
1076 language |=
static_cast<uint16
>(langChar - 0x60) << (0xA - charIndex * 0x5);
1083 writer().writeUInt16BE(language);
1084 writer().writeUInt16BE(0);
1088 writer().writeUInt64BE(0);
1107 for(
int i = 0; i < 3; ++i)
writer().writeUInt32BE(0);
1121 ostream::pos_type minfStartOffset =
outputStream().tellp();
1122 writer().writeUInt32BE(0);
1124 bool dinfAtomWritten =
false;
1145 dinfAtomWritten =
true;
1149 if(!dinfAtomWritten) {
1150 writer().writeUInt32BE(36);
1153 writer().writeUInt32BE(28);
1155 writer().writeUInt32BE(0);
1156 writer().writeUInt32BE(1);
1158 writer().writeUInt32BE(12);
1161 writer().writeUInt24BE(0x000001);
1165 bool stblAtomWritten =
false;
1169 stblAtomWritten =
true;
1172 if(!stblAtomWritten) {
1187 ostream::pos_type stblStartOffset =
outputStream().tellp();
1188 writer().writeUInt32BE(0);
1246 static const string context(
"parsing MP4 track");
1247 using namespace Mp4AtomIds;
1255 if(!(m_tkhdAtom = m_trakAtom->childById(
TrackHeader))) {
1259 if(!(m_mdiaAtom = m_trakAtom->childById(
Media))) {
1263 if(!(m_mdhdAtom = m_mdiaAtom->childById(
MediaHeader))) {
1275 if(!(m_stblAtom = m_minfAtom->childById(
SampleTable))) {
1300 BinaryReader &
reader = m_trakAtom->reader();
1303 m_istream->seekg(m_tkhdAtom->startOffset() + 8);
1304 byte atomVersion = reader.readByte();
1305 uint32 flags = reader.readUInt24BE();
1309 switch(atomVersion) {
1311 m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
1313 m_id = reader.readUInt32BE();
1316 m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1317 m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1318 m_id = reader.readUInt32BE();
1328 m_istream->seekg(m_mdhdAtom->dataOffset());
1329 atomVersion = reader.readByte();
1331 switch(atomVersion) {
1333 m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt32BE());
1336 m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt32BE()) / static_cast<double>(m_timeScale));
1339 m_creationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1340 m_modificationTime = startDate + TimeSpan::fromSeconds(reader.readUInt64BE());
1341 m_timeScale = reader.readUInt32BE();
1342 m_duration = TimeSpan::fromSeconds(static_cast<double>(reader.readUInt64BE()) / static_cast<double>(m_timeScale));
1345 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);
1349 uint16 tmp = reader.readUInt16BE();
1351 const char buff[] = {
1352 static_cast<char>(((tmp & 0x7C00) >> 0xA) + 0x60),
1353 static_cast<char>(((tmp & 0x03E0) >> 0x5) + 0x60),
1354 static_cast<char>(((tmp & 0x001F) >> 0x0) + 0x60),
1363 m_istream->seekg(m_hdlrAtom->dataOffset() + 8);
1365 switch(reader.readUInt32BE()) {
1375 case 0x6D657461:
case 0x74657874:
1383 if((tmp =
m_istream->peek()) == m_hdlrAtom->dataSize() - 12 - 4 - 8 - 1) {
1386 m_name = reader.readString(tmp);
1389 m_name = reader.readTerminatedString(m_hdlrAtom->dataSize() - 12 - 4 - 8, 0);
1394 m_istream->seekg(m_stcoAtom->dataOffset() + 4);
1395 m_chunkCount = reader.readUInt32BE();
1398 m_istream->seekg(m_stsdAtom->dataOffset() + 4);
1399 uint32 entryCount = reader.readUInt32BE();
1400 Mp4Atom *esDescParentAtom =
nullptr;
1401 if(entryCount > 0) {
1403 for(
Mp4Atom *codecConfigContainerAtom = m_stsdAtom->
firstChild(); codecConfigContainerAtom; codecConfigContainerAtom = codecConfigContainerAtom->nextSibling()) {
1404 codecConfigContainerAtom->
parse();
1406 m_formatId = interpretIntegerAsString<uint32>(codecConfigContainerAtom->id());
1409 m_istream->seekg(codecConfigContainerAtom->dataOffset());
1410 switch(codecConfigContainerAtom->id()) {
1416 tmp = reader.readUInt16BE();
1432 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 16);
1435 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28 + 32);
1438 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 28);
1440 if(!esDescParentAtom) {
1441 esDescParentAtom = codecConfigContainerAtom;
1448 m_istream->seekg(6 + 2 + 16, ios_base::cur);
1454 m_framesPerSample = reader.readUInt16BE();
1455 tmp = reader.readByte();
1459 }
else if(tmp < 32) {
1462 m_depth = reader.readUInt16BE();
1463 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 78);
1464 if(!esDescParentAtom) {
1465 esDescParentAtom = codecConfigContainerAtom;
1470 codecConfigContainerAtom->denoteFirstChild(codecConfigContainerAtom->headerSize() + 8);
1471 if(!esDescParentAtom) {
1472 esDescParentAtom = codecConfigContainerAtom;
1484 if(esDescParentAtom) {
1487 m_istream->seekg(avcConfigAtom->dataOffset());
1488 m_avcConfig = make_unique<Media::AvcConfiguration>();
1490 m_avcConfig->parse(reader, avcConfigAtom->dataSize());
1508 m_bitrate =
static_cast<double>(m_esInfo->averageBitrate) / 1000;
1509 m_maxBitrate =
static_cast<double>(m_esInfo->maxBitrate) / 1000;
1510 if(m_esInfo->audioSpecificConfig) {
1513 if(m_esInfo->audioSpecificConfig->sampleFrequencyIndex == 0xF) {
1520 if(m_esInfo->audioSpecificConfig->extensionSampleFrequencyIndex == 0xF) {
1527 m_channelConfig = m_esInfo->audioSpecificConfig->channelConfiguration;
1530 if(m_esInfo->videoSpecificConfig) {
1533 m_format.
sub = m_esInfo->videoSpecificConfig->profile;
1534 if(!m_esInfo->videoSpecificConfig->userData.empty()) {
1536 m_formatId += m_esInfo->videoSpecificConfig->userData;
1544 m_istream->seekg(m_stcoAtom->dataOffset() + 8);
1545 m_istream->seekg(m_chunkOffsetSize == 8 ? reader.readUInt64BE() : reader.readUInt32BE());
1563 m_sampleSizes.clear();
1565 uint64 actualSampleSizeTableSize = m_stszAtom->dataSize();
1566 if(actualSampleSizeTableSize < 12) {
1569 actualSampleSizeTableSize -= 12;
1570 m_istream->seekg(m_stszAtom->dataOffset() + 4);
1572 uint32 constantSize;
1576 fieldSize = reader.readByte();
1579 constantSize = reader.readUInt32BE();
1584 m_sampleSizes.push_back(constantSize);
1588 uint64 calculatedSampleSizeTableSize = ceil((0.125 * fieldSize) *
m_sampleCount);
1589 if(calculatedSampleSizeTableSize < actualSampleSizeTableSize) {
1591 }
else if(calculatedSampleSizeTableSize > actualSampleSizeTableSize) {
1593 actualSampleCount = floor(static_cast<double>(actualSampleSizeTableSize) / (0.125 * fieldSize));
1595 m_sampleSizes.reserve(actualSampleCount);
1599 for(; i <= actualSampleCount; i += 2) {
1600 byte val = reader.readByte();
1601 m_sampleSizes.push_back(val >> 4);
1602 m_sampleSizes.push_back(val & 0xF0);
1603 m_size += (val >> 4) + (val & 0xF0);
1605 if(i <= actualSampleCount + 1) {
1606 m_sampleSizes.push_back(reader.readByte() >> 4);
1607 m_size += m_sampleSizes.back();
1611 for(; i <= actualSampleCount; ++i) {
1612 m_sampleSizes.push_back(reader.readByte());
1613 m_size += m_sampleSizes.back();
1617 for(; i <= actualSampleCount; ++i) {
1618 m_sampleSizes.push_back(reader.readUInt16BE());
1619 m_size += m_sampleSizes.back();
1623 for(; i <= actualSampleCount; ++i) {
1624 m_sampleSizes.push_back(reader.readUInt32BE());
1625 m_size += m_sampleSizes.back();
1635 uint64 totalDuration = 0;
1642 uint32 calculatedDataSize = 0;
1643 if(tfhdAtom->dataSize() < calculatedDataSize) {
1646 m_istream->seekg(tfhdAtom->dataOffset() + 1);
1647 uint32 flags = reader.readUInt24BE();
1648 if(
m_id == reader.readUInt32BE()) {
1649 if(flags & 0x000001) {
1650 calculatedDataSize += 8;
1652 if(flags & 0x000002) {
1653 calculatedDataSize += 4;
1655 if(flags & 0x000008) {
1656 calculatedDataSize += 4;
1658 if(flags & 0x000010) {
1659 calculatedDataSize += 4;
1661 if(flags & 0x000020) {
1662 calculatedDataSize += 4;
1666 uint32 defaultSampleDuration = 0;
1667 uint32 defaultSampleSize = 0;
1669 if(tfhdAtom->dataSize() < calculatedDataSize) {
1672 if(flags & 0x000001) {
1676 if(flags & 0x000002) {
1680 if(flags & 0x000008) {
1681 defaultSampleDuration = reader.readUInt32BE();
1684 if(flags & 0x000010) {
1685 defaultSampleSize = reader.readUInt32BE();
1687 if(flags & 0x000020) {
1693 uint32 calculatedDataSize = 8;
1694 if(trunAtom->dataSize() < calculatedDataSize) {
1697 m_istream->seekg(trunAtom->dataOffset() + 1);
1698 uint32 flags = reader.readUInt24BE();
1699 uint32 sampleCount = reader.readUInt32BE();
1701 if(flags & 0x000001) {
1702 calculatedDataSize += 4;
1704 if(flags & 0x000004) {
1705 calculatedDataSize += 4;
1707 uint32 entrySize = 0;
1708 if(flags & 0x000100) {
1711 if(flags & 0x000200) {
1714 if(flags & 0x000400) {
1717 if(flags & 0x000800) {
1721 if(trunAtom->dataSize() < calculatedDataSize) {
1724 if(flags & 0x000001) {
1728 if(flags & 0x000004) {
1732 if(flags & 0x000100) {
1733 totalDuration += reader.readUInt32BE();
1735 totalDuration += defaultSampleDuration;
1737 if(flags & 0x000200) {
1738 m_sampleSizes.push_back(reader.readUInt32BE());
1739 m_size += m_sampleSizes.back();
1741 m_size += defaultSampleSize;
1743 if(flags & 0x000400) {
1746 if(flags & 0x000800) {
1753 if(m_sampleSizes.empty() && defaultSampleSize) {
1754 m_sampleSizes.push_back(defaultSampleSize);
1769 m_duration = TimeSpan::fromSeconds(static_cast<double>(totalDuration) / static_cast<double>(timeScale));
1774 if(m_bitrate < 0.01 && m_bitrate > -0.01) {
1779 m_istream->seekg(m_stscAtom->dataOffset() + 4);
1780 m_sampleToChunkEntryCount = reader.readUInt32BE();
Contains utility classes helping to read and write streams.