8 #include "../mediafileinfo.h" 9 #include "../exceptions.h" 10 #include "../backuphelper.h" 12 #include "resources/config.h" 14 #include <c++utilities/conversion/stringconversion.h> 15 #include <c++utilities/conversion/stringbuilder.h> 16 #include <c++utilities/io/catchiofailure.h> 21 #include <initializer_list> 22 #include <unordered_set> 34 constexpr
const char appInfo[] = APP_NAME
" v" APP_VERSION;
43 uint64 MatroskaContainer::m_maxFullParseSize = 0x3200000;
48 MatroskaContainer::MatroskaContainer(
MediaFileInfo &fileInfo, uint64 startOffset) :
74 m_tracksElements.clear();
75 m_segmentInfoElements.clear();
76 m_tagsElements.clear();
77 m_chaptersElements.clear();
78 m_attachmentsElements.clear();
80 m_editionEntries.clear();
81 m_attachments.clear();
91 static const string context(
"validating Matroska file index (cues)");
92 bool cuesElementsFound =
false;
94 unordered_set<int> ids;
95 bool cueTimeFound =
false, cueTrackPositionsFound =
false;
96 unique_ptr<EbmlElement> clusterElement;
97 uint64 pos, prevClusterSize = 0, currentOffset = 0;
100 segmentElement->parse();
102 for(
EbmlElement *segmentChildElement = segmentElement->
firstChild(); segmentChildElement; segmentChildElement = segmentChildElement->nextSibling()) {
103 segmentChildElement->parse();
104 switch(segmentChildElement->id()) {
109 cuesElementsFound =
true;
111 for(
EbmlElement *cuePointElement = segmentChildElement->
firstChild(); cuePointElement; cuePointElement = cuePointElement->nextSibling()) {
112 cuePointElement->parse();
113 cueTimeFound = cueTrackPositionsFound =
false;
114 switch(cuePointElement->id()) {
120 for(
EbmlElement *cuePointChildElement = cuePointElement->
firstChild(); cuePointChildElement; cuePointChildElement = cuePointChildElement->nextSibling()) {
121 cuePointChildElement->parse();
122 switch(cuePointChildElement->id()) {
132 cueTrackPositionsFound =
true;
134 clusterElement.reset();
135 for(
EbmlElement *subElement = cuePointChildElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
137 switch(subElement->id()) {
145 if(ids.count(subElement->id())) {
148 ids.insert(subElement->id());
158 switch(subElement->id()) {
165 clusterElement = make_unique<EbmlElement>(*
this, segmentElement->dataOffset() + subElement->readUInteger() - currentOffset);
167 clusterElement->parse();
169 addNotification(
NotificationType::Critical,
"\"CueClusterPosition\" element at " % numberToString(subElement->startOffset()) +
" does not point to \"Cluster\"-element (points to " + numberToString(clusterElement->startOffset()) +
").", context);
177 pos = subElement->readUInteger();
195 if(!clusterElement) {
200 EbmlElement referenceElement(*
this, clusterElement->dataOffset() + pos);
202 referenceElement.
parse();
203 switch(referenceElement.id()) {
209 addNotification(
NotificationType::Critical,
"\"CueRelativePosition\" element does not point to \"Block\"-, \"BlockGroup\", or \"SimpleBlock\"-element (points to " % numberToString(referenceElement.startOffset()) +
").", context);
228 if(!cueTrackPositionsFound) {
239 for(
EbmlElement *clusterElementChild = segmentChildElement->
firstChild(); clusterElementChild; clusterElementChild = clusterElementChild->nextSibling()) {
240 clusterElementChild->parse();
241 switch(clusterElementChild->id()) {
247 if((pos = clusterElementChild->readUInteger()) > 0 && (segmentChildElement->startOffset() - segmentElement->dataOffset() + currentOffset) != pos) {
248 addNotification(
NotificationType::Critical, argsToString(
"\"Position\"-element at ", clusterElementChild->startOffset(),
" points to ", pos,
" which is not the offset of the containing \"Cluster\"-element."), context);
253 if((pos = clusterElementChild->readUInteger()) != prevClusterSize) {
261 prevClusterSize = segmentChildElement->totalSize();
267 currentOffset += segmentElement->totalSize();
271 if(!cuesElementsFound) {
289 return find_if(elements.cbegin(), elements.cend(), std::bind(
sameOffset, offset, _1)) == elements.cend();
294 for(
const auto &entry : m_editionEntries) {
295 const auto &chapters = entry->chapters();
296 if(index < chapters.size()) {
297 return chapters[index].get();
299 index -= chapters.size();
308 for(
const auto &entry : m_editionEntries) {
309 count += entry->chapters().size();
317 srand(time(
nullptr));
321 attachmentId = rand();
326 goto generateRandomId;
331 m_attachments.emplace_back(make_unique<MatroskaAttachment>());
345 for(
const EbmlElement *childElement = segmentElement->
firstChild(); childElement; childElement = childElement->nextSibling()) {
346 if(childElement->id() == elementId) {
349 for(
const auto &seekInfo : m_seekInfos) {
350 for(
const auto &info : seekInfo->info()) {
351 if(info.first == elementId) {
376 static const string context(
"parsing header of Matroska container");
380 m_tracksElements.clear();
381 m_segmentInfoElements.clear();
382 m_tagsElements.clear();
385 uint64 currentOffset = 0;
386 vector<MatroskaSeekInfo>::size_type seekInfosIndex = 0;
388 for(
EbmlElement *topLevelElement =
m_firstElement.get(); topLevelElement; topLevelElement = topLevelElement->nextSibling()) {
390 topLevelElement->parse();
391 switch(topLevelElement->id()) {
393 for(
EbmlElement *subElement = topLevelElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
396 switch(subElement->id()) {
413 m_maxIdLength = subElement->readUInteger();
422 m_maxSizeLength = subElement->readUInteger();
441 for(
EbmlElement *subElement = topLevelElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
444 switch(subElement->id()) {
446 m_seekInfos.emplace_back(make_unique<MatroskaSeekInfo>());
447 m_seekInfos.back()->parse(subElement);
452 m_tracksElements.push_back(subElement);
456 if(
excludesOffset(m_segmentInfoElements, subElement->startOffset())) {
457 m_segmentInfoElements.push_back(subElement);
462 m_tagsElements.push_back(subElement);
466 if(
excludesOffset(m_chaptersElements, subElement->startOffset())) {
467 m_chaptersElements.push_back(subElement);
471 if(
excludesOffset(m_attachmentsElements, subElement->startOffset())) {
472 m_attachmentsElements.push_back(subElement);
478 for(
auto i = m_seekInfos.cbegin() + seekInfosIndex, end = m_seekInfos.cend(); i != end; ++i, ++seekInfosIndex) {
479 for(
const auto &infoPair : (*i)->info()) {
480 uint64 offset = currentOffset + topLevelElement->dataOffset() + infoPair.second;
484 auto element = make_unique<EbmlElement>(*
this, offset);
487 if(element->id() != infoPair.first) {
488 addNotification(
NotificationType::Critical, argsToString(
"ID of element ", element->idToString(),
" at ", offset,
" does not match the ID denoted in the \"SeekHead\" element (0x", numberToString(infoPair.first, 16),
")."), context);
490 switch(element->id()) {
533 if(((!m_tracksElements.empty() && !m_tagsElements.empty()) ||
fileInfo().
size() > m_maxFullParseSize) && !m_segmentInfoElements.empty()) {
545 currentOffset += topLevelElement->totalSize();
575 void MatroskaContainer::parseSegmentInfo()
577 if(m_segmentInfoElements.empty()) {
581 for(
EbmlElement *element : m_segmentInfoElements) {
584 float64 rawDuration = 0.0;
586 bool hasTitle =
false;
589 switch(subElement->
id()) {
608 if(rawDuration > 0.0 && timeScale > 0) {
609 m_duration += TimeSpan::fromSeconds(rawDuration * timeScale / 1000000000);
619 void MatroskaContainer::readTrackStatisticsFromTags()
631 static const string context(
"parsing tags of Matroska container");
635 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
637 switch(subElement->id()) {
639 m_tags.emplace_back(make_unique<MatroskaTag>());
641 m_tags.back()->parse(*subElement);
657 readTrackStatisticsFromTags();
661 readTrackStatisticsFromTags();
667 static const string context(
"parsing tracks of Matroska container");
671 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
673 switch(subElement->id()) {
675 m_tracks.emplace_back(make_unique<MatroskaTrack>(*subElement));
693 readTrackStatisticsFromTags();
697 readTrackStatisticsFromTags();
703 static const string context(
"parsing editions/chapters of Matroska container");
707 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
709 switch(subElement->id()) {
711 m_editionEntries.emplace_back(make_unique<MatroskaEditionEntry>(subElement));
713 m_editionEntries.back()->parseNested();
715 m_editionEntries.pop_back();
737 static const string context(
"parsing attachments of Matroska container");
738 for(
EbmlElement *element : m_attachmentsElements) {
741 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
743 switch(subElement->id()) {
745 m_attachments.emplace_back(make_unique<MatroskaAttachment>());
747 m_attachments.back()->parse(subElement);
749 m_attachments.pop_back();
774 cuesElement(nullptr),
776 firstClusterElement(nullptr),
780 sizeDenotationLength(0),
820 static const string context(
"making Matroska container");
838 vector<MatroskaTagMaker> tagMaker;
839 tagMaker.reserve(
tags().size());
840 uint64 tagElementsSize = 0;
842 vector<MatroskaAttachmentMaker> attachmentMaker;
843 attachmentMaker.reserve(m_attachments.size());
844 uint64 attachedFileElementsSize = 0;
845 uint64 attachmentsSize;
846 vector<MatroskaTrackHeaderMaker> trackHeaderMaker;
847 trackHeaderMaker.reserve(
tracks().size());
848 uint64 trackHeaderElementsSize = 0;
849 uint64 trackHeaderSize;
853 unsigned int segmentIndex = 0;
855 vector<SegmentData> segmentData;
861 uint64 currentPosition = 0;
863 vector<tuple<uint64, uint64> > crc32Offsets;
867 uint64 clusterSize, clusterReadSize, clusterReadOffset;
879 unsigned int lastSegmentIndex = numeric_limits<unsigned int>::max();
887 uint64 ebmlHeaderDataSize = 2 * 7;
904 if(tagMaker.back().requiredSize() > 3) {
906 tagElementsSize += tagMaker.back().requiredSize();
921 if(attachmentMaker.back().requiredSize() > 3) {
923 attachedFileElementsSize += attachmentMaker.back().requiredSize();
938 if(trackHeaderMaker.back().requiredSize() > 3) {
940 trackHeaderElementsSize += trackHeaderMaker.back().requiredSize();
954 for(
bool firstClusterFound =
false, firstTagFound =
false; level0Element; level0Element = level0Element->
nextSibling()) {
955 level0Element->
parse();
956 switch(level0Element->
id()) {
959 for(level1Element = level0Element->
firstChild(); level1Element && !firstClusterFound && !firstTagFound; level1Element = level1Element->
nextSibling()) {
960 level1Element->
parse();
961 switch(level1Element->
id()) {
964 firstTagFound =
true;
967 firstClusterFound =
true;
972 }
else if(firstClusterFound) {
979 segmentData.resize(lastSegmentIndex + 1);
993 updateStatus(
"Calculating offsets of elements before cluster ...", 0.0);
994 calculateSegmentData:
997 uint64 currentOffset = ebmlHeaderSize;
999 uint64 readOffset = 0;
1004 if(rewriteRequired) {
1015 for(level0Element =
firstElement(), currentPosition = newPadding = segmentIndex = 0; level0Element; level0Element = level0Element->
nextSibling()) {
1016 switch(level0Element->
id()) {
1052 newCuesPos = currentCuesPos;
1065 calculateSegmentSize:
1077 goto calculateSegmentSize;
1083 if(segmentIndex <
m_titles.size()) {
1085 if(!
title.empty()) {
1090 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1091 level2Element->
parse();
1092 switch(level2Element->
id()) {
1110 if(trackHeaderSize) {
1113 goto calculateSegmentSize;
1124 goto calculateSegmentSize;
1140 goto calculateSegmentSize;
1147 if(attachmentsSize) {
1150 goto calculateSegmentSize;
1164 goto calculateSegmentSize;
1167 updateStatus(
"Calculating cluster offsets and index size ...", 0.0);
1176 if(!rewriteRequired) {
1183 if(totalOffset <= segment.firstClusterElement->
startOffset()) {
1197 nonRewriteCalculations:
1201 goto calculateSegmentSize;
1204 bool cuesInvalidated =
false;
1209 cuesInvalidated =
true;
1216 if(index % 50 == 0) {
1220 if(cuesInvalidated) {
1222 goto addCuesElementSize;
1226 updateStatus(
"Calculating offsets of elements after cluster ...", 0.0);
1232 goto calculateSegmentSize;
1244 goto calculateSegmentSize;
1251 if(attachmentsSize) {
1254 goto calculateSegmentSize;
1268 goto nonRewriteCalculations;
1271 totalOffset = currentOffset + 4 + sizeLength + offset;
1276 if(totalOffset <= segment.firstClusterElement->
startOffset()) {
1282 rewriteRequired =
true;
1285 rewriteRequired =
true;
1288 rewriteRequired =
true;
1294 if(rewriteRequired) {
1298 rewriteRequired =
false;
1302 rewriteRequired =
false;
1305 goto calculateSegmentData;
1318 bool cuesInvalidated =
false;
1323 cuesInvalidated =
true;
1326 goto calculateSegmentSize;
1329 clusterSize = clusterReadSize = 0;
1330 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1331 level2Element->
parse();
1333 cuesInvalidated =
true;
1335 switch(level2Element->
id()) {
1343 clusterSize += level2Element->
totalSize();
1345 clusterReadSize += level2Element->
totalSize();
1357 if((index % 50 == 0) &&
fileInfo().size()) {
1363 if(cuesInvalidated) {
1366 goto addCuesElementSize;
1369 updateStatus(
"Calculating offsets of elements after cluster ...", 0.0);
1375 goto calculateSegmentSize;
1390 goto calculateSegmentSize;
1397 if(attachmentsSize) {
1400 goto calculateSegmentSize;
1418 readOffset += level0Element->
totalSize();
1425 currentOffset += level0Element->
totalSize();
1426 readOffset += level0Element->
totalSize();
1430 if(!rewriteRequired) {
1432 if((rewriteRequired = (newPadding >
fileInfo().maxPadding() || newPadding <
fileInfo().minPadding()))) {
1434 goto calculateSegmentData;
1442 const char *what = catchIoFailure();
1444 throwIoFailure(what);
1458 NativeFileStream backupStream;
1459 BinaryWriter outputWriter(&outputStream);
1462 if(rewriteRequired) {
1463 if(
fileInfo().saveFilePath().empty()) {
1468 outputStream.open(
fileInfo().path(), ios_base::out | ios_base::binary | ios_base::trunc);
1470 const char *what = catchIoFailure();
1472 throwIoFailure(what);
1477 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
1478 backupStream.open(
fileInfo().path(), ios_base::in | ios_base::binary);
1480 outputStream.open(
fileInfo().saveFilePath(), ios_base::out | ios_base::binary | ios_base::trunc);
1482 const char *what = catchIoFailure();
1484 throwIoFailure(what);
1495 for(
auto &maker : attachmentMaker) {
1496 maker.bufferCurrentAttachments();
1502 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1504 const char *what = catchIoFailure();
1506 throwIoFailure(what);
1516 outputStream.write(buff, sizeLength);
1526 for(level0Element =
firstElement(), segmentIndex = 0, currentPosition = 0; level0Element; level0Element = level0Element->
nextSibling()) {
1529 switch(level0Element->
id()) {
1547 outputStream.write(buff, sizeLength);
1556 crc32Offsets.emplace_back(outputStream.tellp(), segment.
totalDataSize);
1557 outputStream.write(buff, 6);
1570 outputStream.write(buff, sizeLength);
1572 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1573 switch(level2Element->
id()) {
1586 if(segmentIndex <
m_titles.size()) {
1588 if(!
title.empty()) {
1598 if(trackHeaderElementsSize) {
1601 outputStream.write(buff, sizeLength);
1602 for(
auto &maker : trackHeaderMaker) {
1603 maker.make(outputStream);
1619 outputStream.write(buff, sizeLength);
1620 for(
auto &maker : tagMaker) {
1621 maker.make(outputStream);
1626 if(attachmentsSize) {
1629 outputStream.write(buff, sizeLength);
1630 for(
auto &maker : attachmentMaker) {
1631 maker.make(outputStream);
1654 *buff =
static_cast<char>(voidLength = segment.
newPadding - 2) | 0x80;
1657 BE::getBytes(static_cast<uint64>((voidLength = segment.
newPadding - 9) | 0x100000000000000), buff);
1661 outputStream.write(buff, sizeLength);
1663 for(; voidLength; --voidLength) {
1664 outputStream.put(0);
1670 if(rewriteRequired) {
1675 updateStatus(
"Writing cluster ...", static_cast<double>(static_cast<uint64>(outputStream.tellp()) - offset) / segment.
totalDataSize);
1677 auto clusterSizesIterator = segment.
clusterSizes.cbegin();
1678 unsigned int index = 0;
1681 clusterSize = currentPosition + (
static_cast<uint64
>(outputStream.tellp()) - offset);
1685 outputStream.write(buff, sizeLength);
1687 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1688 switch(level2Element->
id()) {
1702 }
else if(index % 50 == 0) {
1708 updateStatus(
"Updateing cluster ...", static_cast<double>(static_cast<uint64>(outputStream.tellp()) - offset) / segment.
totalDataSize);
1709 for(; level1Element; level1Element = level1Element->
nextSibling()) {
1710 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1711 switch(level2Element->
id()) {
1716 if(level2Element->
dataSize() < sizeLength) {
1722 outputStream.seekp(level2Element->
dataOffset());
1723 outputStream.write(buff, sizeLength);
1753 outputStream.write(buff, sizeLength);
1754 for(
auto &maker : tagMaker) {
1755 maker.make(outputStream);
1760 if(attachmentsSize) {
1763 outputStream.write(buff, sizeLength);
1764 for(
auto &maker : attachmentMaker) {
1765 maker.make(outputStream);
1782 currentPosition += level0Element->
totalSize();
1788 if(rewriteRequired) {
1799 outputStream.close();
1800 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1803 const auto newSize =
static_cast<uint64
>(outputStream.tellp());
1807 outputStream.close();
1809 if(truncate(
fileInfo().path().c_str(), newSize) == 0) {
1815 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1830 if(!crc32Offsets.empty()) {
1832 for(
const auto &crc32Offset : crc32Offsets) {
1833 outputStream.seekg(get<0>(crc32Offset) + 6);
1834 outputStream.seekp(get<0>(crc32Offset) + 2);
1835 writer().writeUInt32LE(
reader().readCrc32(get<1>(crc32Offset) - 6));
1842 outputStream.flush();
Contains utility classes helping to read and write streams.