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> 33 constexpr
const char appInfo[] = APP_NAME
" v" APP_VERSION;
42 uint64 MatroskaContainer::m_maxFullParseSize = 0x3200000;
47 MatroskaContainer::MatroskaContainer(
MediaFileInfo &fileInfo, uint64 startOffset) :
73 m_tracksElements.clear();
74 m_segmentInfoElements.clear();
75 m_tagsElements.clear();
76 m_chaptersElements.clear();
77 m_attachmentsElements.clear();
79 m_editionEntries.clear();
80 m_attachments.clear();
90 static const string context(
"validating Matroska file index (cues)");
91 bool cuesElementsFound =
false;
93 unordered_set<int> ids;
94 bool cueTimeFound =
false, cueTrackPositionsFound =
false;
95 unique_ptr<EbmlElement> clusterElement;
96 uint64 pos, prevClusterSize = 0, currentOffset = 0;
99 segmentElement->parse();
101 for(
EbmlElement *segmentChildElement = segmentElement->
firstChild(); segmentChildElement; segmentChildElement = segmentChildElement->nextSibling()) {
102 segmentChildElement->parse();
103 switch(segmentChildElement->id()) {
108 cuesElementsFound =
true;
110 for(
EbmlElement *cuePointElement = segmentChildElement->
firstChild(); cuePointElement; cuePointElement = cuePointElement->nextSibling()) {
111 cuePointElement->parse();
112 cueTimeFound = cueTrackPositionsFound =
false;
113 switch(cuePointElement->id()) {
119 for(
EbmlElement *cuePointChildElement = cuePointElement->
firstChild(); cuePointChildElement; cuePointChildElement = cuePointChildElement->nextSibling()) {
120 cuePointChildElement->parse();
121 switch(cuePointChildElement->id()) {
131 cueTrackPositionsFound =
true;
133 clusterElement.reset();
134 for(
EbmlElement *subElement = cuePointChildElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
136 switch(subElement->id()) {
144 if(ids.count(subElement->id())) {
147 ids.insert(subElement->id());
157 switch(subElement->id()) {
164 clusterElement = make_unique<EbmlElement>(*
this, segmentElement->dataOffset() + subElement->readUInteger() - currentOffset);
166 clusterElement->parse();
168 addNotification(
NotificationType::Critical,
"\"CueClusterPosition\" element at " % numberToString(subElement->startOffset()) +
" does not point to \"Cluster\"-element (points to " + numberToString(clusterElement->startOffset()) +
").", context);
176 pos = subElement->readUInteger();
194 if(!clusterElement) {
199 EbmlElement referenceElement(*
this, clusterElement->dataOffset() + pos);
201 referenceElement.
parse();
202 switch(referenceElement.id()) {
208 addNotification(
NotificationType::Critical,
"\"CueRelativePosition\" element does not point to \"Block\"-, \"BlockGroup\", or \"SimpleBlock\"-element (points to " % numberToString(referenceElement.startOffset()) +
").", context);
227 if(!cueTrackPositionsFound) {
238 for(
EbmlElement *clusterElementChild = segmentChildElement->
firstChild(); clusterElementChild; clusterElementChild = clusterElementChild->nextSibling()) {
239 clusterElementChild->parse();
240 switch(clusterElementChild->id()) {
246 if((pos = clusterElementChild->readUInteger()) > 0 && (segmentChildElement->startOffset() - segmentElement->dataOffset() + currentOffset) != pos) {
247 addNotification(
NotificationType::Critical, argsToString(
"\"Position\"-element at ", clusterElementChild->startOffset(),
" points to ", pos,
" which is not the offset of the containing \"Cluster\"-element."), context);
252 if((pos = clusterElementChild->readUInteger()) != prevClusterSize) {
260 prevClusterSize = segmentChildElement->totalSize();
266 currentOffset += segmentElement->totalSize();
270 if(!cuesElementsFound) {
288 return find_if(elements.cbegin(), elements.cend(), std::bind(
sameOffset, offset, _1)) == elements.cend();
293 for(
const auto &entry : m_editionEntries) {
294 const auto &chapters = entry->chapters();
295 if(index < chapters.size()) {
296 return chapters[index].get();
298 index -= chapters.size();
307 for(
const auto &entry : m_editionEntries) {
308 count += entry->chapters().size();
316 srand(time(
nullptr));
320 attachmentId = rand();
325 goto generateRandomId;
330 m_attachments.emplace_back(make_unique<MatroskaAttachment>());
344 for(
const EbmlElement *childElement = segmentElement->
firstChild(); childElement; childElement = childElement->nextSibling()) {
345 if(childElement->id() == elementId) {
348 for(
const auto &seekInfo : m_seekInfos) {
349 for(
const auto &info : seekInfo->info()) {
350 if(info.first == elementId) {
375 static const string context(
"parsing header of Matroska container");
379 m_tracksElements.clear();
380 m_segmentInfoElements.clear();
381 m_tagsElements.clear();
384 uint64 currentOffset = 0;
385 vector<MatroskaSeekInfo>::size_type seekInfosIndex = 0;
387 for(
EbmlElement *topLevelElement =
m_firstElement.get(); topLevelElement; topLevelElement = topLevelElement->nextSibling()) {
389 topLevelElement->parse();
390 switch(topLevelElement->id()) {
392 for(
EbmlElement *subElement = topLevelElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
395 switch(subElement->id()) {
412 m_maxIdLength = subElement->readUInteger();
421 m_maxSizeLength = subElement->readUInteger();
440 for(
EbmlElement *subElement = topLevelElement->
firstChild(); subElement; subElement = subElement->nextSibling()) {
443 switch(subElement->id()) {
445 m_seekInfos.emplace_back(make_unique<MatroskaSeekInfo>());
446 m_seekInfos.back()->parse(subElement);
451 m_tracksElements.push_back(subElement);
455 if(
excludesOffset(m_segmentInfoElements, subElement->startOffset())) {
456 m_segmentInfoElements.push_back(subElement);
461 m_tagsElements.push_back(subElement);
465 if(
excludesOffset(m_chaptersElements, subElement->startOffset())) {
466 m_chaptersElements.push_back(subElement);
470 if(
excludesOffset(m_attachmentsElements, subElement->startOffset())) {
471 m_attachmentsElements.push_back(subElement);
477 for(
auto i = m_seekInfos.cbegin() + seekInfosIndex, end = m_seekInfos.cend(); i != end; ++i, ++seekInfosIndex) {
478 for(
const auto &infoPair : (*i)->info()) {
479 uint64 offset = currentOffset + topLevelElement->dataOffset() + infoPair.second;
483 auto element = make_unique<EbmlElement>(*
this, offset);
486 if(element->id() != infoPair.first) {
487 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);
489 switch(element->id()) {
532 if(((!m_tracksElements.empty() && !m_tagsElements.empty()) ||
fileInfo().
size() > m_maxFullParseSize) && !m_segmentInfoElements.empty()) {
544 currentOffset += topLevelElement->totalSize();
574 void MatroskaContainer::parseSegmentInfo()
576 if(m_segmentInfoElements.empty()) {
580 for(
EbmlElement *element : m_segmentInfoElements) {
583 float64 rawDuration = 0.0;
585 bool hasTitle =
false;
588 switch(subElement->
id()) {
607 if(rawDuration > 0.0 && timeScale > 0) {
608 m_duration += TimeSpan::fromSeconds(rawDuration * timeScale / 1000000000);
618 void MatroskaContainer::readTrackStatisticsFromTags()
630 static const string context(
"parsing tags of Matroska container");
634 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
636 switch(subElement->id()) {
638 m_tags.emplace_back(make_unique<MatroskaTag>());
640 m_tags.back()->parse(*subElement);
656 readTrackStatisticsFromTags();
660 readTrackStatisticsFromTags();
666 static const string context(
"parsing tracks of Matroska container");
670 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
672 switch(subElement->id()) {
674 m_tracks.emplace_back(make_unique<MatroskaTrack>(*subElement));
692 readTrackStatisticsFromTags();
696 readTrackStatisticsFromTags();
702 static const string context(
"parsing editions/chapters of Matroska container");
706 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
708 switch(subElement->id()) {
710 m_editionEntries.emplace_back(make_unique<MatroskaEditionEntry>(subElement));
712 m_editionEntries.back()->parseNested();
714 m_editionEntries.pop_back();
736 static const string context(
"parsing attachments of Matroska container");
737 for(
EbmlElement *element : m_attachmentsElements) {
740 for(
EbmlElement *subElement = element->
firstChild(); subElement; subElement = subElement->nextSibling()) {
742 switch(subElement->id()) {
744 m_attachments.emplace_back(make_unique<MatroskaAttachment>());
746 m_attachments.back()->parse(subElement);
748 m_attachments.pop_back();
773 cuesElement(nullptr),
775 firstClusterElement(nullptr),
779 sizeDenotationLength(0),
819 static const string context(
"making Matroska container");
837 vector<MatroskaTagMaker> tagMaker;
838 tagMaker.reserve(
tags().size());
839 uint64 tagElementsSize = 0;
841 vector<MatroskaAttachmentMaker> attachmentMaker;
842 attachmentMaker.reserve(m_attachments.size());
843 uint64 attachedFileElementsSize = 0;
844 uint64 attachmentsSize;
845 vector<MatroskaTrackHeaderMaker> trackHeaderMaker;
846 trackHeaderMaker.reserve(
tracks().size());
847 uint64 trackHeaderElementsSize = 0;
848 uint64 trackHeaderSize;
852 unsigned int segmentIndex = 0;
854 vector<SegmentData> segmentData;
860 uint64 currentPosition = 0;
862 vector<tuple<uint64, uint64> > crc32Offsets;
866 uint64 clusterSize, clusterReadSize, clusterReadOffset;
878 unsigned int lastSegmentIndex =
static_cast<unsigned int>(-1);
886 uint64 ebmlHeaderDataSize = 2 * 7;
903 if(tagMaker.back().requiredSize() > 3) {
905 tagElementsSize += tagMaker.back().requiredSize();
920 if(attachmentMaker.back().requiredSize() > 3) {
922 attachedFileElementsSize += attachmentMaker.back().requiredSize();
937 if(trackHeaderMaker.back().requiredSize() > 3) {
939 trackHeaderElementsSize += trackHeaderMaker.back().requiredSize();
953 for(
bool firstClusterFound =
false, firstTagFound =
false; level0Element; level0Element = level0Element->
nextSibling()) {
954 level0Element->
parse();
955 switch(level0Element->
id()) {
958 for(level1Element = level0Element->
firstChild(); level1Element && !firstClusterFound && !firstTagFound; level1Element = level1Element->
nextSibling()) {
959 level1Element->
parse();
960 switch(level1Element->
id()) {
963 firstTagFound =
true;
966 firstClusterFound =
true;
971 }
else if(firstClusterFound) {
978 segmentData.resize(lastSegmentIndex + 1);
992 calculateSegmentData:
995 uint64 currentOffset = ebmlHeaderSize;
997 uint64 readOffset = 0;
1002 if(rewriteRequired) {
1014 for(level0Element =
firstElement(), currentPosition = newPadding = segmentIndex = 0; level0Element; level0Element = level0Element->
nextSibling()) {
1015 switch(level0Element->
id()) {
1051 newCuesPos = currentCuesPos;
1064 calculateSegmentSize:
1076 goto calculateSegmentSize;
1082 if(segmentIndex <
m_titles.size()) {
1084 if(!
title.empty()) {
1089 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1090 level2Element->
parse();
1091 switch(level2Element->
id()) {
1109 if(trackHeaderSize) {
1112 goto calculateSegmentSize;
1123 goto calculateSegmentSize;
1139 goto calculateSegmentSize;
1146 if(attachmentsSize) {
1149 goto calculateSegmentSize;
1163 goto calculateSegmentSize;
1172 if(!rewriteRequired) {
1181 if(totalOffset <= segment.firstClusterElement->
startOffset()) {
1195 nonRewriteCalculations:
1199 goto calculateSegmentSize;
1207 goto addCuesElementSize;
1216 goto calculateSegmentSize;
1228 goto calculateSegmentSize;
1235 if(attachmentsSize) {
1238 goto calculateSegmentSize;
1252 goto nonRewriteCalculations;
1255 totalOffset = currentOffset + 4 + sizeLength + offset;
1260 if(totalOffset <= segment.firstClusterElement->
startOffset()) {
1266 rewriteRequired =
true;
1269 rewriteRequired =
true;
1272 rewriteRequired =
true;
1284 if(rewriteRequired) {
1288 rewriteRequired =
false;
1292 rewriteRequired =
false;
1295 goto calculateSegmentData;
1313 goto addCuesElementSize;
1316 goto calculateSegmentSize;
1319 clusterSize = clusterReadSize = 0;
1320 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1321 level2Element->
parse();
1324 goto addCuesElementSize;
1326 switch(level2Element->
id()) {
1334 clusterSize += level2Element->
totalSize();
1336 clusterReadSize += level2Element->
totalSize();
1348 goto calculateSegmentSize;
1363 goto calculateSegmentSize;
1370 if(attachmentsSize) {
1373 goto calculateSegmentSize;
1391 readOffset += level0Element->
totalSize();
1398 currentOffset += level0Element->
totalSize();
1399 readOffset += level0Element->
totalSize();
1403 if(!rewriteRequired) {
1405 if((rewriteRequired = (newPadding >
fileInfo().maxPadding() || newPadding <
fileInfo().minPadding()))) {
1407 goto calculateSegmentData;
1415 const char *what = catchIoFailure();
1417 throwIoFailure(what);
1431 NativeFileStream backupStream;
1432 BinaryWriter outputWriter(&outputStream);
1435 if(rewriteRequired) {
1436 if(
fileInfo().saveFilePath().empty()) {
1441 outputStream.open(
fileInfo().path(), ios_base::out | ios_base::binary | ios_base::trunc);
1443 const char *what = catchIoFailure();
1445 throwIoFailure(what);
1450 backupStream.exceptions(ios_base::badbit | ios_base::failbit);
1451 backupStream.open(
fileInfo().path(), ios_base::in | ios_base::binary);
1453 outputStream.open(
fileInfo().saveFilePath(), ios_base::out | ios_base::binary | ios_base::trunc);
1455 const char *what = catchIoFailure();
1457 throwIoFailure(what);
1468 for(
auto &maker : attachmentMaker) {
1469 maker.bufferCurrentAttachments();
1475 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1477 const char *what = catchIoFailure();
1479 throwIoFailure(what);
1489 outputStream.write(buff, sizeLength);
1499 for(level0Element =
firstElement(), segmentIndex = 0, currentPosition = 0; level0Element; level0Element = level0Element->
nextSibling()) {
1502 switch(level0Element->
id()) {
1520 outputStream.write(buff, sizeLength);
1529 crc32Offsets.emplace_back(outputStream.tellp(), segment.
totalDataSize);
1530 outputStream.write(buff, 6);
1543 outputStream.write(buff, sizeLength);
1545 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1546 switch(level2Element->
id()) {
1559 if(segmentIndex <
m_titles.size()) {
1561 if(!
title.empty()) {
1571 if(trackHeaderElementsSize) {
1574 outputStream.write(buff, sizeLength);
1575 for(
auto &maker : trackHeaderMaker) {
1576 maker.make(outputStream);
1592 outputStream.write(buff, sizeLength);
1593 for(
auto &maker : tagMaker) {
1594 maker.make(outputStream);
1599 if(attachmentsSize) {
1602 outputStream.write(buff, sizeLength);
1603 for(
auto &maker : attachmentMaker) {
1604 maker.make(outputStream);
1627 *buff =
static_cast<char>(voidLength = segment.
newPadding - 2) | 0x80;
1630 BE::getBytes(static_cast<uint64>((voidLength = segment.
newPadding - 9) | 0x100000000000000), buff);
1634 outputStream.write(buff, sizeLength);
1636 for(; voidLength; --voidLength) {
1637 outputStream.put(0);
1643 if(rewriteRequired) {
1648 updateStatus(
"Writing clusters ...", static_cast<double>(static_cast<uint64>(outputStream.tellp()) - offset) / segment.
totalDataSize);
1650 for(
auto clusterSizesIterator = segment.
clusterSizes.cbegin();
1653 clusterSize = currentPosition + (
static_cast<uint64
>(outputStream.tellp()) - offset);
1657 outputStream.write(buff, sizeLength);
1659 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1660 switch(level2Element->
id()) {
1680 for(; level1Element; level1Element = level1Element->
nextSibling()) {
1681 for(level2Element = level1Element->
firstChild(); level2Element; level2Element = level2Element->
nextSibling()) {
1682 switch(level2Element->
id()) {
1687 if(level2Element->
dataSize() < sizeLength) {
1693 outputStream.seekp(level2Element->
dataOffset());
1694 outputStream.write(buff, sizeLength);
1722 outputStream.write(buff, sizeLength);
1723 for(
auto &maker : tagMaker) {
1724 maker.make(outputStream);
1729 if(attachmentsSize) {
1732 outputStream.write(buff, sizeLength);
1733 for(
auto &maker : attachmentMaker) {
1734 maker.make(outputStream);
1751 currentPosition += level0Element->
totalSize();
1757 if(rewriteRequired) {
1768 outputStream.close();
1769 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1772 const auto newSize =
static_cast<uint64
>(outputStream.tellp());
1776 outputStream.close();
1778 if(truncate(
fileInfo().path().c_str(), newSize) == 0) {
1784 outputStream.open(
fileInfo().path(), ios_base::in | ios_base::out | ios_base::binary);
1799 if(!crc32Offsets.empty()) {
1801 for(
const auto &crc32Offset : crc32Offsets) {
1802 outputStream.seekg(get<0>(crc32Offset) + 6);
1803 outputStream.seekp(get<0>(crc32Offset) + 2);
1804 writer().writeUInt32LE(
reader().readCrc32(get<1>(crc32Offset) - 6));
1811 outputStream.flush();
Contains utility classes helping to read and write streams.