Tag Parser 12.1.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
matroskacues.cpp
Go to the documentation of this file.
1#include "./matroskacues.h"
3
4#include "../mediafileinfo.h"
5
6#include <c++utilities/conversion/binaryconversion.h>
7
8using namespace std;
9using namespace CppUtilities;
10
11namespace TagParser {
12
41{
42 if (m_cuesElement) {
43 std::uint64_t size = m_sizes.at(m_cuesElement);
44 return 4 + EbmlElement::calculateSizeDenotationLength(size) + size;
45 } else {
46 return 0;
47 }
48}
49
55{
56 static const string context("parsing \"Cues\"-element");
57 clear();
58 std::uint64_t cuesElementSize = 0, cuePointElementSize, cueTrackPositionsElementSize, cueReferenceElementSize, pos, relPos, statePos;
59 EbmlElement *cueRelativePositionElement, *cueClusterPositionElement;
60 for (EbmlElement *cuePointElement = cuesElement->firstChild(); cuePointElement; cuePointElement = cuePointElement->nextSibling()) {
61 // parse children of "Cues"-element which must be "CuePoint"-elements
62 cuePointElement->parse(diag);
63 switch (cuePointElement->id()) {
64 case EbmlIds::Void:
65 case EbmlIds::Crc32:
66 break;
68 cuePointElementSize = 0;
69 for (EbmlElement *cuePointChild = cuePointElement->firstChild(); cuePointChild; cuePointChild = cuePointChild->nextSibling()) {
70 // parse children of "CuePoint"-element
71 cuePointChild->parse(diag);
72 switch (cuePointChild->id()) {
73 case EbmlIds::Void:
74 case EbmlIds::Crc32:
75 break;
77 cuePointChild->makeBuffer();
78 cuePointElementSize += cuePointChild->totalSize();
79 break;
81 cueTrackPositionsElementSize = 0;
82 cueRelativePositionElement = cueClusterPositionElement = nullptr;
83 for (EbmlElement *cueTrackPositionsChild = cuePointChild->firstChild(); cueTrackPositionsChild;
84 cueTrackPositionsChild = cueTrackPositionsChild->nextSibling()) {
85 // parse children of "CueTrackPositions"-element
86 cueTrackPositionsChild->parse(diag);
87 switch (cueTrackPositionsChild->id()) {
91 cueTrackPositionsChild->makeBuffer();
92 cueTrackPositionsElementSize += cueTrackPositionsChild->totalSize();
93 break;
95 relPos = (cueRelativePositionElement = cueTrackPositionsChild)->readUInteger();
96 break;
98 pos = (cueClusterPositionElement = cueTrackPositionsChild)->readUInteger();
99 cueTrackPositionsElementSize += 2u + EbmlElement::calculateUIntegerLength(pos);
100 m_offsets.emplace(cueTrackPositionsChild, pos);
101 m_cueElementByOriginalOffset.emplace(pos, cueTrackPositionsChild);
102 break;
104 statePos = cueTrackPositionsChild->readUInteger();
105 cueTrackPositionsElementSize += 2u + EbmlElement::calculateUIntegerLength(statePos);
106 m_offsets.emplace(cueTrackPositionsChild, statePos);
107 m_cueElementByOriginalOffset.emplace(statePos, cueTrackPositionsChild);
108 break;
110 cueReferenceElementSize = 0;
111 for (EbmlElement *cueReferenceChild = cueTrackPositionsChild->firstChild(); cueReferenceChild;
112 cueReferenceChild = cueReferenceChild->nextSibling()) {
113 // parse children of "CueReference"-element
114 cueReferenceChild->parse(diag);
115 switch (cueReferenceChild->id()) {
116 case EbmlIds::Void:
117 case EbmlIds::Crc32:
118 break;
121 cueReferenceChild->makeBuffer();
122 cueReferenceElementSize += cueReferenceChild->totalSize();
123 break;
126 statePos = cueReferenceChild->readUInteger();
127 cueReferenceElementSize += 2u + EbmlElement::calculateUIntegerLength(statePos);
128 m_offsets.emplace(cueReferenceChild, statePos);
129 m_cueElementByOriginalOffset.emplace(statePos, cueReferenceChild);
130 break;
131 default:
132 diag.emplace_back(DiagLevel::Warning,
133 "\"CueReference\"-element contains a element which is not known to the parser. It will be ignored.", context);
134 }
135 }
136 cueTrackPositionsElementSize
137 += 1 + EbmlElement::calculateSizeDenotationLength(cueReferenceElementSize) + cueReferenceElementSize;
138 m_sizes.emplace(cueTrackPositionsChild, cueReferenceElementSize);
139 break;
140 default:
141 diag.emplace_back(DiagLevel::Warning,
142 "\"CueTrackPositions\"-element contains a element which is not known to the parser. It will be ignored.", context);
143 }
144 }
145 if (!cueClusterPositionElement) {
146 diag.emplace_back(
147 DiagLevel::Critical, "\"CueTrackPositions\"-element does not contain mandatory \"CueClusterPosition\"-element.", context);
148 } else if (cueRelativePositionElement) {
149 cueTrackPositionsElementSize += 2u + EbmlElement::calculateUIntegerLength(relPos);
150 m_relativeOffsets.emplace(piecewise_construct, forward_as_tuple(cueRelativePositionElement), forward_as_tuple(pos, relPos));
151 m_cueRelativePositionElementByOriginalOffsets.emplace(
152 piecewise_construct, forward_as_tuple(pos, relPos), forward_as_tuple(cueRelativePositionElement));
153 }
154 cuePointElementSize
155 += 1 + EbmlElement::calculateSizeDenotationLength(cueTrackPositionsElementSize) + cueTrackPositionsElementSize;
156 m_sizes.emplace(cuePointChild, cueTrackPositionsElementSize);
157 break;
158 default:
159 diag.emplace_back(DiagLevel::Warning,
160 "\"CuePoint\"-element contains a element which is not a \"CueTime\"- or a \"CueTrackPositions\"-element. It will be ignored.",
161 context);
162 }
163 }
164 cuesElementSize += 1 + EbmlElement::calculateSizeDenotationLength(cuePointElementSize) + cuePointElementSize;
165 m_sizes.emplace(cuePointElement, cuePointElementSize);
166 break;
167 default:
168 diag.emplace_back(
169 DiagLevel::Warning, "\"Cues\"-element contains a element which is not a \"CuePoint\"-element. It will be ignored.", context);
170 }
171 }
172 m_sizes.emplace(m_cuesElement = cuesElement, cuesElementSize);
173}
174
179bool MatroskaCuePositionUpdater::updateOffsets(std::uint64_t originalOffset, std::uint64_t newOffset)
180{
181 auto updated = false;
182 const auto newOffsetLength = static_cast<int>(EbmlElement::calculateUIntegerLength(newOffset));
183 for (auto cueElementRange = m_cueElementByOriginalOffset.equal_range(originalOffset); cueElementRange.first != cueElementRange.second;
184 ++cueElementRange.first) {
185 auto *const cueElement = cueElementRange.first->second;
186 const auto offsetIterator = m_offsets.find(cueElement);
187 if (offsetIterator == m_offsets.end()) {
188 continue;
189 }
190 auto &offset = offsetIterator->second;
191 if (offset.currentValue() != newOffset) {
192 updated
193 = updateSize(cueElement->parent(), newOffsetLength - static_cast<int>(EbmlElement::calculateUIntegerLength(offset.currentValue())))
194 || updated;
195 offset.update(newOffset);
196 }
197 }
198 return updated;
199}
200
206 std::uint64_t referenceOffset, std::uint64_t originalRelativeOffset, std::uint64_t newRelativeOffset)
207{
208 auto updated = false;
209 const auto newRelativeOffsetLength = static_cast<int>(EbmlElement::calculateUIntegerLength(newRelativeOffset));
210 for (auto cueElementRange = m_cueRelativePositionElementByOriginalOffsets.equal_range(std::make_pair(referenceOffset, originalRelativeOffset));
211 cueElementRange.first != cueElementRange.second; ++cueElementRange.first) {
212 auto *const cueRelativePositionElement = cueElementRange.first->second;
213 const auto offsetIterator = m_relativeOffsets.find(cueRelativePositionElement);
214 if (offsetIterator == m_relativeOffsets.end()) {
215 continue;
216 }
217 auto &offset = offsetIterator->second;
218 if (offset.currentValue() != newRelativeOffset) {
219 updated = updateSize(cueRelativePositionElement->parent(),
220 newRelativeOffsetLength - static_cast<int>(EbmlElement::calculateUIntegerLength(offset.currentValue())))
221 || updated;
222 offset.update(newRelativeOffset);
223 }
224 }
225 return updated;
226}
227
232bool MatroskaCuePositionUpdater::updateSize(EbmlElement *element, int shift)
233{
234 if (!shift) {
235 return false; // shift is gone
236 }
237 if (!element) {
238 // there was no parent (shouldn't happen in a normal file structure since the Segment element should
239 // be parent of the Cues element)
240 return shift;
241 }
242 // get size info
243 const auto sizeIterator = m_sizes.find(element);
244 if (sizeIterator == m_sizes.end()) {
245 return shift; // the element is out of the scope of the cue position updater (likely the Segment element)
246 }
247 std::uint64_t &size = sizeIterator->second;
248 // calculate new size
249 const std::uint64_t newSize = shift > 0 ? size + static_cast<std::uint64_t>(shift) : size - static_cast<std::uint64_t>(-shift);
250 // shift parent
251 const bool updated = updateSize(element->parent(),
252 shift + static_cast<int>(EbmlElement::calculateSizeDenotationLength(newSize))
253 - static_cast<int>(EbmlElement::calculateSizeDenotationLength(size)));
254 // apply new size
255 size = newSize;
256 return updated;
257}
258
263{
264 static const string context("making \"Cues\"-element");
265 if (!m_cuesElement) {
266 diag.emplace_back(DiagLevel::Warning, "No cues written; the cues of the source file could not be parsed correctly.", context);
267 return;
268 }
269 // temporary variables
270 char buff[8];
271 std::uint8_t len;
272 // write "Cues"-element
273 try {
274 BE::getBytes(static_cast<std::uint32_t>(MatroskaIds::Cues), buff);
275 stream.write(buff, 4);
276 len = EbmlElement::makeSizeDenotation(m_sizes[m_cuesElement], buff);
277 stream.write(buff, len);
278 // loop through original elements and write (a updated version) of them
279 for (EbmlElement *cuePointElement = m_cuesElement->firstChild(); cuePointElement; cuePointElement = cuePointElement->nextSibling()) {
280 cuePointElement->parse(diag);
281 switch (cuePointElement->id()) {
282 case EbmlIds::Void:
283 case EbmlIds::Crc32:
284 break;
286 // write "CuePoint"-element
287 stream.put(static_cast<char>(MatroskaIds::CuePoint));
288 len = EbmlElement::makeSizeDenotation(m_sizes[cuePointElement], buff);
289 stream.write(buff, len);
290 for (EbmlElement *cuePointChild = cuePointElement->firstChild(); cuePointChild; cuePointChild = cuePointChild->nextSibling()) {
291 cuePointChild->parse(diag);
292 switch (cuePointChild->id()) {
293 case EbmlIds::Void:
294 case EbmlIds::Crc32:
295 break;
297 // write "CueTime"-element
298 cuePointChild->copyBuffer(stream);
299 cuePointChild->discardBuffer();
300 break;
302 // write "CueTrackPositions"-element
303 stream.put(static_cast<char>(MatroskaIds::CueTrackPositions));
304 len = EbmlElement::makeSizeDenotation(m_sizes[cuePointChild], buff);
305 stream.write(buff, len);
306 for (EbmlElement *cueTrackPositionsChild = cuePointChild->firstChild(); cueTrackPositionsChild;
307 cueTrackPositionsChild = cueTrackPositionsChild->nextSibling()) {
308 cueTrackPositionsChild->parse(diag);
309 switch (cueTrackPositionsChild->id()) {
313 // write unchanged children of "CueTrackPositions"-element
314 cueTrackPositionsChild->copyBuffer(stream);
315 cueTrackPositionsChild->discardBuffer();
316 break;
318 if (const auto relativeOffset = m_relativeOffsets.find(cueTrackPositionsChild);
319 relativeOffset != m_relativeOffsets.end()) {
320 EbmlElement::makeSimpleElement(stream, cueTrackPositionsChild->id(), relativeOffset->second.currentValue());
321 }
322 // we were not able parse the relative offset because the absolute offset is missing
323 // continue anyways
324 break;
327 // write "CueClusterPosition"/"CueCodecState"-element
329 stream, cueTrackPositionsChild->id(), m_offsets.at(cueTrackPositionsChild).currentValue());
330 break;
332 // write "CueReference"-element
333 stream.put(static_cast<char>(MatroskaIds::CueRefTime));
334 len = EbmlElement::makeSizeDenotation(m_sizes[cueTrackPositionsChild], buff);
335 stream.write(buff, len);
336 for (EbmlElement *cueReferenceChild = cueTrackPositionsChild->firstChild(); cueReferenceChild;
337 cueReferenceChild = cueReferenceChild->nextSibling()) {
338 cueReferenceChild->parse(diag);
339 switch (cueReferenceChild->id()) {
340 case EbmlIds::Void:
341 case EbmlIds::Crc32:
342 break;
345 // write unchanged children of "CueReference"-element
346 cueReferenceChild->copyBuffer(stream);
347 cueReferenceChild->discardBuffer();
348 cueReferenceChild->copyEntirely(stream, diag, nullptr);
349 break;
352 // write "CueRefCluster"/"CueRefCodecState"-element
354 stream, cueReferenceChild->id(), m_offsets.at(cueReferenceChild).currentValue());
355 break;
356 default:
357 diag.emplace_back(DiagLevel::Warning,
358 "\"CueReference\"-element contains a element which is not known to the parser. It will be ignored.",
359 context);
360 }
361 }
362 break;
363 default:
364 diag.emplace_back(DiagLevel::Warning,
365 "\"CueTrackPositions\"-element contains a element which is not known to the parser. It will be ignored.",
366 context);
367 }
368 }
369 break;
370 default:
371 diag.emplace_back(DiagLevel::Warning,
372 "\"CuePoint\"-element contains a element which is not a \"CueTime\"- or a \"CueTrackPositions\"-element. It will be "
373 "ignored.",
374 context);
375 }
376 }
377 break;
378 default:
379 diag.emplace_back(
380 DiagLevel::Warning, "\"Cues\"-element contains a element which is not a \"CuePoint\"-element. It will be ignored.", context);
381 }
382 }
383 } catch (const out_of_range &) {
384 diag.emplace_back(
385 DiagLevel::Critical, "Unable to write the file index because the index of the original file could not be parsed correctly.", context);
386 throw InvalidDataException();
387 }
388}
389
390} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition ebmlelement.h:32
static void makeSimpleElement(std::ostream &stream, IdentifierType id, std::uint64_t content)
Makes a simple EBML element.
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 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 * parent()
Returns the parent of the element.
ImplementationType * firstChild()
Returns the first child of the element.
void parse(Diagnostics &diag)
Parses the header information of the element which is read from the related stream at the start offse...
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition exceptions.h:25
std::uint64_t totalSize() const
Returns how many bytes will be written when calling the make() method.
bool updateOffsets(std::uint64_t originalOffset, std::uint64_t newOffset)
Sets the offset of the entries with the specified originalOffset to newOffset.
bool updateRelativeOffsets(std::uint64_t referenceOffset, std::uint64_t originalRelativeOffset, std::uint64_t newRelativeOffset)
Sets the relative offset of the entries with the specified originalRelativeOffset and the specified r...
void clear()
Resets the object to its initial state.
void make(std::ostream &stream, Diagnostics &diag)
Writes the previously parsed "Cues"-element with updated positions to the specified stream.
void parse(EbmlElement *cuesElement, Diagnostics &diag)
Parses the specified cuesElement.
EbmlElement * cuesElement() const
Returns the "Cues"-element specified when calling the parse() method.
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10