Tag Parser 12.3.1
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 = relPos = 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.
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.
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
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