Tag Parser 12.3.1
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
matroskaseekinfo.cpp
Go to the documentation of this file.
2#include "./matroskaid.h"
3
4#include "../diagnostics.h"
5#include "../exceptions.h"
6
7#include <c++utilities/conversion/binaryconversion.h>
8#include <c++utilities/conversion/stringbuilder.h>
9
10#include <string>
11
12using namespace std;
13using namespace CppUtilities;
14
15namespace TagParser {
16
25void MatroskaSeekInfo::shift(std::uint64_t start, std::int64_t amount)
26{
27 for (auto &info : m_info) {
28 if (get<1>(info) >= start) {
29 if (amount > 0) {
30 get<1>(info) += static_cast<std::uint64_t>(amount);
31 } else {
32 get<1>(info) -= static_cast<std::uint64_t>(-amount);
33 }
34 }
35 }
36}
37
51void MatroskaSeekInfo::parse(EbmlElement *seekHeadElement, Diagnostics &diag, size_t maxIndirection)
52{
53 static const string context("parsing \"SeekHead\"-element");
54
55 m_seekHeadElements.emplace_back(seekHeadElement);
56
57 for (EbmlElement *seekElement = seekHeadElement->firstChild(), *seekIdElement, *seekPositionElement; seekElement;
58 seekElement = seekElement->nextSibling()) {
59 seekElement->parse(diag);
60 switch (seekElement->id()) {
62 seekIdElement = seekPositionElement = nullptr;
63 for (auto *seekElementChild = seekElement->firstChild(); seekElementChild; seekElementChild = seekElementChild->nextSibling()) {
64 seekElementChild->parse(diag);
65 switch (seekElementChild->id()) {
67 if (seekIdElement) {
68 diag.emplace_back(DiagLevel::Warning,
69 "The \"Seek\"-element contains multiple \"SeekID\"-elements. Surplus elements will be ignored.", context);
70 }
71 seekIdElement = seekElementChild;
72 break;
74 if (seekPositionElement) {
75 diag.emplace_back(DiagLevel::Warning,
76 "The \"Seek\"-element contains multiple \"SeekPosition\"-elements. Surplus elements will be ignored.", context);
77 }
78 seekPositionElement = seekElementChild;
79 break;
80 case EbmlIds::Crc32:
81 case EbmlIds::Void:
82 break;
83 default:
84 diag.emplace_back(DiagLevel::Warning,
85 "The element \"" % seekElementChild->idToString()
86 + "\" within the \"Seek\" element is not a \"SeekID\"-element nor a \"SeekPosition\"-element and will be ignored.",
87 context);
88 }
89 }
90
91 if (!seekIdElement || !seekPositionElement) {
92 diag.emplace_back(DiagLevel::Warning, "The \"Seek\"-element does not contain a \"SeekID\"- and a \"SeekPosition\"-element.", context);
93 break;
94 }
95
96 m_info.emplace_back(seekIdElement->readUInteger(), seekPositionElement->readUInteger());
97
98 // follow possibly referenced seek head element
99 if (m_info.back().first == MatroskaIds::SeekHead) {
100 const auto startOffset = m_info.back().second;
101 if (!maxIndirection) {
102 diag.emplace_back(DiagLevel::Warning,
103 argsToString("Not following reference by \"Seek\"-element at ", seekElement->startOffset(),
104 " which points to another \"SeekHead\"-element at ", startOffset, '.'),
105 context);
106 break;
107 }
108
109 auto visited = false;
110 for (const auto *const visitedSeekHeadElement : m_seekHeadElements) {
111 if (visitedSeekHeadElement->startOffset() == startOffset) {
112 diag.emplace_back(DiagLevel::Warning,
113 argsToString("The \"Seek\"-element at ", seekElement->startOffset(), " contains a loop to the \"SeekHead\"-element at ",
114 visitedSeekHeadElement->startOffset(), '.'),
115 context);
116 visited = true;
117 break;
118 }
119 }
120 if (visited) {
121 break;
122 }
123 m_additionalSeekHeadElements.emplace_back(make_unique<EbmlElement>(seekHeadElement->container(), startOffset));
124 parse(m_additionalSeekHeadElements.back().get(), diag, maxIndirection - 1);
125 }
126
127 break;
128 case EbmlIds::Crc32:
129 case EbmlIds::Void:
130 break;
131 default:
132 diag.emplace_back(
133 DiagLevel::Warning, "The element " % seekElement->idToString() + " is not a seek element and will be ignored.", context);
134 }
135 }
136 if (m_info.empty()) {
137 diag.emplace_back(DiagLevel::Warning, "No seek information found.", context);
138 }
139}
140
147void MatroskaSeekInfo::make(ostream &stream, Diagnostics &diag)
148{
149 CPP_UTILITIES_UNUSED(diag)
150
151 std::uint64_t totalSize = 0;
152 char buff0[8];
153 char buff1[8];
154 char buff2[2];
155 std::uint8_t sizeLength0, sizeLength1;
156 // calculate size
157 for (const auto &info : m_info) {
158 // "Seek" element + "SeekID" element + "SeekPosition" element
159 totalSize += 2u + 1u + (2u + 1u + EbmlElement::calculateIdLength(info.first)) + (2u + 1u + EbmlElement::calculateUIntegerLength(info.second));
160 }
161 // write ID and size
162 BE::getBytes(static_cast<std::uint32_t>(MatroskaIds::SeekHead), buff0);
163 stream.write(buff0, 4);
164 sizeLength0 = EbmlElement::makeSizeDenotation(totalSize, buff0);
165 stream.write(buff0, sizeLength0);
166 // write entries
167 for (const auto &info : m_info) {
168 // make values
169 sizeLength0 = EbmlElement::makeId(info.first, buff0);
170 sizeLength1 = EbmlElement::makeUInteger(info.second, buff1);
171 // "Seek" header
172 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Seek), buff2);
173 stream.write(buff2, 2);
174 stream.put(static_cast<char>(0x80 | (2 + 1 + sizeLength0 + 2 + 1 + sizeLength1)));
175 // "SeekID" element
176 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::SeekID), buff2);
177 stream.write(buff2, 2);
178 stream.put(static_cast<char>(0x80 | sizeLength0));
179 stream.write(buff0, sizeLength0);
180 // "SeekPosition" element
181 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::SeekPosition), buff2);
182 stream.write(buff2, 2);
183 stream.put(static_cast<char>(0x80 | sizeLength1));
184 stream.write(buff1, sizeLength1);
185 }
186}
187
192std::uint64_t MatroskaSeekInfo::minSize() const
193{
194 std::uint64_t maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 1 + 2 + 1 + 1);
195 return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
196}
197
202std::uint64_t MatroskaSeekInfo::maxSize() const
203{
204 std::uint64_t maxTotalSize = m_info.size() * (2 + 1 + 2 + 1 + 4 + 2 + 1 + 8);
205 return 4 + EbmlElement::calculateSizeDenotationLength(maxTotalSize) + maxTotalSize;
206}
207
212std::uint64_t MatroskaSeekInfo::actualSize() const
213{
214 std::uint64_t totalSize = 0;
215 for (const auto &info : m_info) {
216 // "Seek" element + "SeekID" element + "SeekPosition" element
217 totalSize
218 += 2u + 1u + (2 + 1 + EbmlElement::calculateIdLength(get<0>(info))) + (2u + 1u + EbmlElement::calculateUIntegerLength(get<1>(info)));
219 }
220 return totalSize += 4u + EbmlElement::calculateSizeDenotationLength(totalSize);
221}
222
231bool MatroskaSeekInfo::push(unsigned int index, EbmlElement::IdentifierType id, std::uint64_t offset)
232{
233 unsigned int currentIndex = 0;
234 for (auto &entry : info()) {
235 if (get<0>(entry) == id) {
236 if (index == currentIndex) {
237 bool sizeUpdated = EbmlElement::calculateUIntegerLength(get<1>(entry)) != EbmlElement::calculateUIntegerLength(offset);
238 get<1>(entry) = offset;
239 return sizeUpdated;
240 }
241 ++currentIndex;
242 }
243 }
244 info().emplace_back(id, offset);
245 return true;
246}
247
252{
253 m_seekHeadElements.clear();
254 m_additionalSeekHeadElements.clear();
255 m_info.clear();
256}
257
258} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
The EbmlElement class helps to parse EBML files such as Matroska files.
static std::uint8_t makeUInteger(std::uint64_t value, char *buff)
Writes value to buff.
static std::uint8_t calculateIdLength(IdentifierType id)
Returns the length of the specified id in byte.
static std::uint8_t calculateSizeDenotationLength(std::uint64_t size)
Returns the length of the size denotation for the specified size in byte.
static std::uint8_t makeId(IdentifierType id, char *buff)
Stores the specified id in the specified buffer which must be at least 8 bytes long.
static std::uint8_t makeSizeDenotation(std::uint64_t size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
static std::uint8_t calculateUIntegerLength(std::uint64_t integer)
Returns the length of the specified unsigned integer in byte.
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
typename FileElementTraits< EbmlElement >::IdentifierType IdentifierType
ContainerType & container()
Returns the related container.
void parse(EbmlElement *seekHeadElements, Diagnostics &diag, std::size_t maxIndirection=1)
Parses the specified seekHeadElement and populates info() with the gathered information.
void shift(std::uint64_t start, std::int64_t amount)
Shifts all offsets greater or equal than start by amount bytes.
void clear()
Resets the object to its initial state.
bool push(unsigned int index, EbmlElement::IdentifierType id, std::uint64_t offset)
Pushes the specified offset of an element with the specified id to the info.
void make(std::ostream &stream, Diagnostics &diag)
Writes a "SeekHead" element for the current instance to the specified stream.
std::vector< std::pair< EbmlElement::IdentifierType, std::uint64_t > > & info()
Returns a mutable version of the seek information gathered when the parse() method was called.
std::uint64_t maxSize() const
Returns the maximal number of bytes written when calling the make() method.
std::uint64_t minSize() const
Returns the minimal number of bytes written when calling the make() method.
std::uint64_t actualSize() const
Returns the number of bytes which will be written when calling the make() method.
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10