Tag Parser: matroska/matroskacues.cpp Source File

Tag Parser 12.5.2 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"
2#include "./matroskacontainer.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
20
28
35
40std::uint64_t MatroskaCuePositionUpdater::totalSize() const
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
54 void MatroskaCuePositionUpdater::parse(EbmlElement *cuesElement, Diagnostics &diag)
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;
67 case MatroskaIds::CuePoint:
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;
76 case MatroskaIds::CueTime:
77 cuePointChild->makeBuffer();
78 cuePointElementSize += cuePointChild->totalSize();
79 break;
80 case MatroskaIds::CueTrackPositions:
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()) {
88 case MatroskaIds::CueTrack:
89 case MatroskaIds::CueDuration:
90 case MatroskaIds::CueBlockNumber:
91 cueTrackPositionsChild->makeBuffer();
92 cueTrackPositionsElementSize += cueTrackPositionsChild->totalSize();
93 break;
94 case MatroskaIds::CueRelativePosition:
95 relPos = (cueRelativePositionElement = cueTrackPositionsChild)->readUInteger();
96 break;
97 case MatroskaIds::CueClusterPosition:
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;
103 case MatroskaIds::CueCodecState:
104 statePos = cueTrackPositionsChild->readUInteger();
105 cueTrackPositionsElementSize += 2u + EbmlElement::calculateUIntegerLength(statePos);
106 m_offsets.emplace(cueTrackPositionsChild, statePos);
107 m_cueElementByOriginalOffset.emplace(statePos, cueTrackPositionsChild);
108 break;
109 case MatroskaIds::CueReference:
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;
119 case MatroskaIds::CueRefTime:
120 case MatroskaIds::CueRefNumber:
121 cueReferenceChild->makeBuffer();
122 cueReferenceElementSize += cueReferenceChild->totalSize();
123 break;
124 case MatroskaIds::CueRefCluster:
125 case MatroskaIds::CueRefCodecState:
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
179 bool 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
205 bool MatroskaCuePositionUpdater::updateRelativeOffsets(
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
262 void MatroskaCuePositionUpdater::make(ostream &stream, Diagnostics &diag)
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;
285 case MatroskaIds::CuePoint:
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;
296 case MatroskaIds::CueTime:
297 // write "CueTime"-element
298 cuePointChild->copyBuffer(stream);
299 cuePointChild->discardBuffer();
300 break;
301 case MatroskaIds::CueTrackPositions:
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()) {
310 case MatroskaIds::CueTrack:
311 case MatroskaIds::CueDuration:
312 case MatroskaIds::CueBlockNumber:
313 // write unchanged children of "CueTrackPositions"-element
314 cueTrackPositionsChild->copyBuffer(stream);
315 cueTrackPositionsChild->discardBuffer();
316 break;
317 case MatroskaIds::CueRelativePosition:
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;
325 case MatroskaIds::CueClusterPosition:
326 case MatroskaIds::CueCodecState:
327 // write "CueClusterPosition"/"CueCodecState"-element
328 EbmlElement::makeSimpleElement(
329 stream, cueTrackPositionsChild->id(), m_offsets.at(cueTrackPositionsChild).currentValue());
330 break;
331 case MatroskaIds::CueReference:
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;
343 case MatroskaIds::CueRefTime:
344 case MatroskaIds::CueRefNumber:
345 // write unchanged children of "CueReference"-element
346 cueReferenceChild->copyBuffer(stream);
347 cueReferenceChild->discardBuffer();
348 cueReferenceChild->copyEntirely(stream, diag, nullptr);
349 break;
350 case MatroskaIds::CueRefCluster:
351 case MatroskaIds::CueRefCodecState:
352 // write "CueRefCluster"/"CueRefCodecState"-element
353 EbmlElement::makeSimpleElement(
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
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition diagnostics.h:156
TagParser::EbmlElement
The EbmlElement class helps to parse EBML files such as Matroska files.
Definition ebmlelement.h:32
TagParser::EbmlElement::makeSimpleElement
static void makeSimpleElement(std::ostream &stream, IdentifierType id, std::uint64_t content)
Makes a simple EBML element.
Definition ebmlelement.cpp:514
TagParser::EbmlElement::calculateSizeDenotationLength
static std::uint8_t calculateSizeDenotationLength(std::uint64_t size)
Returns the length of the size denotation for the specified size in byte.
Definition ebmlelement.cpp:288
TagParser::EbmlElement::makeSizeDenotation
static std::uint8_t makeSizeDenotation(std::uint64_t size, char *buff)
Makes the size denotation for the specified size and stores it to buff.
Definition ebmlelement.cpp:343
TagParser::EbmlElement::calculateUIntegerLength
static std::uint8_t calculateUIntegerLength(std::uint64_t integer)
Returns the length of the specified unsigned integer in byte.
Definition ebmlelement.cpp:416
TagParser::GenericFileElement::nextSibling
ImplementationType * nextSibling()
Returns the next sibling of the element.
Definition genericfileelement.h:439
TagParser::GenericFileElement::parent
ImplementationType * parent()
Returns the parent of the element.
Definition genericfileelement.h:390
TagParser::GenericFileElement::firstChild
ImplementationType * firstChild()
Returns the first child of the element.
Definition genericfileelement.h:465
TagParser::InvalidDataException
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition exceptions.h:25
TagParser::MatroskaCuePositionUpdater::totalSize
std::uint64_t totalSize() const
Returns how many bytes will be written when calling the make() method.
Definition matroskacues.cpp:40
TagParser::MatroskaCuePositionUpdater::updateOffsets
bool updateOffsets(std::uint64_t originalOffset, std::uint64_t newOffset)
Sets the offset of the entries with the specified originalOffset to newOffset.
Definition matroskacues.cpp:179
TagParser::MatroskaCuePositionUpdater::updateRelativeOffsets
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...
Definition matroskacues.cpp:205
TagParser::MatroskaCuePositionUpdater::clear
void clear()
Resets the object to its initial state.
Definition matroskacues.h:121
TagParser::MatroskaCuePositionUpdater::make
void make(std::ostream &stream, Diagnostics &diag)
Writes the previously parsed "Cues"-element with updated positions to the specified stream.
Definition matroskacues.cpp:262
TagParser::MatroskaCuePositionUpdater::parse
void parse(EbmlElement *cuesElement, Diagnostics &diag)
Parses the specified cuesElement.
Definition matroskacues.cpp:54
TagParser::MatroskaCuePositionUpdater::cuesElement
EbmlElement * cuesElement() const
Returns the "Cues"-element specified when calling the parse() method.
Definition matroskacues.h:113
matroskacontainer.h
matroskacues.h
mediafileinfo.h
CppUtilities
Definition abstractcontainer.h:16
TagParser::EbmlIds::Void
@ Void
Definition ebmlid.h:28
TagParser::EbmlIds::Crc32
@ Crc32
Definition ebmlid.h:28
TagParser::MatroskaIds::CuePoint
@ CuePoint
Definition matroskaid.h:249
TagParser::MatroskaIds::CueTime
@ CueTime
Definition matroskaid.h:254
TagParser::MatroskaIds::CueTrackPositions
@ CueTrackPositions
Definition matroskaid.h:254
TagParser::MatroskaIds::Cues
@ Cues
Definition matroskaid.h:20
TagParser::MatroskaIds::CueRefNumber
@ CueRefNumber
Definition matroskaid.h:272
TagParser::MatroskaIds::CueRefCluster
@ CueRefCluster
Definition matroskaid.h:272
TagParser::MatroskaIds::CueRefTime
@ CueRefTime
Definition matroskaid.h:272
TagParser::MatroskaIds::CueRefCodecState
@ CueRefCodecState
Definition matroskaid.h:272
TagParser::MatroskaIds::CueTrack
@ CueTrack
Definition matroskaid.h:260
TagParser::MatroskaIds::CueBlockNumber
@ CueBlockNumber
Definition matroskaid.h:264
TagParser::MatroskaIds::CueDuration
@ CueDuration
Definition matroskaid.h:263
TagParser::MatroskaIds::CueCodecState
@ CueCodecState
Definition matroskaid.h:265
TagParser::MatroskaIds::CueRelativePosition
@ CueRelativePosition
Definition matroskaid.h:262
TagParser::MatroskaIds::CueReference
@ CueReference
Definition matroskaid.h:266
TagParser::MatroskaIds::CueClusterPosition
@ CueClusterPosition
Definition matroskaid.h:261
TagParser
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
TagParser::DiagLevel::Warning
@ Warning
Definition diagnostics.h:20
TagParser::DiagLevel::Critical
@ Critical
Definition diagnostics.h:21