Tag Parser 12.5.3
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
matroskatag.cpp
Go to the documentation of this file.
1#include "./matroskatag.h"
2#include "./ebmlelement.h"
3
4#include "../diagnostics.h"
5
6#include <initializer_list>
7#include <stdexcept>
8#include <unordered_map>
9
10using namespace std;
11using namespace CppUtilities;
12
13namespace TagParser {
14
19
21{
22 using namespace MatroskaTagIds;
23 switch (field) {
25 return std::string(artist());
27 return std::string(album());
29 return std::string(comment());
31 return std::string(dateRecorded());
33 return std::string(dateRelease());
35 return std::string(title());
37 return std::string(genre());
39 return std::string(partNumber());
41 return std::string(totalParts());
43 return std::string(encoder());
45 return std::string(encoderSettings());
46 case KnownField::Bpm:
47 return std::string(bpm());
48 case KnownField::Bps:
49 return std::string(bps());
51 return std::string(rating());
53 return std::string(description());
55 return std::string(lyrics());
57 return std::string(label());
59 return std::string(actor());
61 return std::string(lyricist());
63 return std::string(composer());
65 return std::string(duration());
67 return std::string(language());
69 return std::string(accompaniment());
71 return std::string(subtitle());
73 return std::string(leadPerformer());
75 return std::string(arranger());
77 return std::string(conductor());
79 return std::string(director());
81 return std::string(assistantDirector());
83 return std::string(directorOfPhotography());
85 return std::string(soundEngineer());
87 return std::string(artDirector());
89 return std::string(productionDesigner());
91 return std::string(choregrapher());
93 return std::string(costumeDesigner());
95 return std::string(actor());
97 return std::string(character());
99 return std::string(writtenBy());
101 return std::string(screenplayBy());
103 return std::string(editedBy());
105 return std::string(producer());
107 return std::string(coproducer());
109 return std::string(executiveProducer());
111 return std::string(distributedBy());
113 return std::string(masteredBy());
115 return std::string(encodedBy());
117 return std::string(mixedBy());
119 return std::string(remixedBy());
121 return std::string(productionStudio());
123 return std::string(thanksTo());
125 return std::string(publisher());
126 case KnownField::Mood:
127 return std::string(mood());
129 return std::string(originalMediaType());
131 return std::string(contentType());
133 return std::string(subject());
135 return std::string(keywords());
137 return std::string(summary());
139 return std::string(synopsis());
141 return std::string(initialKey());
143 return std::string(period());
145 return std::string(lawRating());
147 return std::string(dateEncoded());
149 return std::string(dateTagged());
151 return std::string(dateDigitized());
153 return std::string(dateWritten());
155 return std::string(datePurchased());
157 return std::string(recordingLocation());
159 return std::string(compositionLocation());
161 return std::string(composerNationality());
163 return std::string(playCounter());
165 return std::string(measure());
167 return std::string(tuning());
168 case KnownField::ISRC:
169 return std::string(isrc());
170 case KnownField::MCDI:
171 return std::string(mcdi());
172 case KnownField::ISBN:
173 return std::string(isbn());
175 return std::string(barcode());
177 return std::string(catalogNumber());
179 return std::string(labelCode());
180 case KnownField::LCCN:
181 return std::string(lccn());
182 case KnownField::IMDB:
183 return std::string(imdb());
184 case KnownField::TMDB:
185 return std::string(tmdb());
186 case KnownField::TVDB:
187 return std::string(tvdb());
189 return std::string(purchaseItem());
191 return std::string(purchaseInfo());
193 return std::string(purchaseOwner());
195 return std::string(purchasePrice());
197 return std::string(purchaseCurrency());
199 return std::string(copyright());
201 return std::string(productionCopyright());
203 return std::string(license());
205 return std::string(termsOfUse());
206 default:
207 return std::string();
208 }
209}
210
212{
213 using namespace MatroskaTagIds;
214 static const std::unordered_map<std::string_view, KnownField> fieldMap({
215 { artist(), KnownField::Artist },
216 { album(), KnownField::Album },
217 { comment(), KnownField::Comment },
218 { dateRecorded(), KnownField::RecordDate },
219 { dateRelease(), KnownField::ReleaseDate },
220 { title(), KnownField::Title },
221 { partNumber(), KnownField::PartNumber },
222 { totalParts(), KnownField::TotalParts },
223 { encoder(), KnownField::Encoder },
224 { encoderSettings(), KnownField::EncoderSettings },
225 { bpm(), KnownField::Bpm },
226 { bps(), KnownField::Bps },
227 { rating(), KnownField::Rating },
228 { description(), KnownField::Description },
229 { lyrics(), KnownField::Lyrics },
230 { label(), KnownField::RecordLabel },
231 { actor(), KnownField::Performers },
232 { lyricist(), KnownField::Lyricist },
233 { composer(), KnownField::Composer },
234 { duration(), KnownField::Length },
235 { language(), KnownField::Language },
236 { accompaniment(), KnownField::AlbumArtist },
237 { subtitle(), KnownField::Subtitle },
238 { leadPerformer(), KnownField::LeadPerformer },
239 { arranger(), KnownField::Arranger },
240 { conductor(), KnownField::Conductor },
241 { director(), KnownField::Director },
242 { assistantDirector(), KnownField::AssistantDirector },
243 { directorOfPhotography(), KnownField::DirectorOfPhotography },
244 { soundEngineer(), KnownField::SoundEngineer },
245 { artDirector(), KnownField::ArtDirector },
246 { productionDesigner(), KnownField::ProductionDesigner },
247 { choregrapher(), KnownField::Choregrapher },
248 { costumeDesigner(), KnownField::CostumeDesigner },
249 { actor(), KnownField::Actor },
250 { character(), KnownField::Character },
251 { writtenBy(), KnownField::WrittenBy },
252 { screenplayBy(), KnownField::ScreenplayBy },
253 { editedBy(), KnownField::EditedBy },
254 { producer(), KnownField::Producer },
255 { coproducer(), KnownField::Coproducer },
256 { executiveProducer(), KnownField::ExecutiveProducer },
257 { distributedBy(), KnownField::DistributedBy },
258 { masteredBy(), KnownField::MasteredBy },
259 { encodedBy(), KnownField::EncodedBy },
260 { mixedBy(), KnownField::MixedBy },
261 { remixedBy(), KnownField::RemixedBy },
262 { productionStudio(), KnownField::ProductionStudio },
263 { thanksTo(), KnownField::ThanksTo },
264 { publisher(), KnownField::Publisher },
265 { mood(), KnownField::Mood },
266 { originalMediaType(), KnownField::OriginalMediaType },
267 { contentType(), KnownField::ContentType },
268 { subject(), KnownField::Subject },
269 { keywords(), KnownField::Keywords },
270 { summary(), KnownField::Summary },
271 { synopsis(), KnownField::Synopsis },
272 { initialKey(), KnownField::InitialKey },
273 { period(), KnownField::Period },
274 { lawRating(), KnownField::LawRating },
275 { dateEncoded(), KnownField::EncodingDate },
276 { dateTagged(), KnownField::TaggingDate },
277 { dateDigitized(), KnownField::DigitalizationDate },
278 { dateWritten(), KnownField::WritingDate },
279 { datePurchased(), KnownField::PurchasingDate },
280 { recordingLocation(), KnownField::RecordingLocation },
281 { compositionLocation(), KnownField::CompositionLocation },
282 { composerNationality(), KnownField::ComposerNationality },
283 { playCounter(), KnownField::PlayCounter },
284 { measure(), KnownField::Measure },
285 { tuning(), KnownField::Tuning },
286 { isrc(), KnownField::ISRC },
287 { mcdi(), KnownField::MCDI },
288 { isbn(), KnownField::ISBN },
289 { barcode(), KnownField::Barcode },
290 { catalogNumber(), KnownField::CatalogNumber },
291 { labelCode(), KnownField::LabelCode },
292 { lccn(), KnownField::LCCN },
293 { imdb(), KnownField::IMDB },
294 { tmdb(), KnownField::TMDB },
295 { tvdb(), KnownField::TVDB },
296 { purchaseItem(), KnownField::PurchaseItem },
297 { purchaseInfo(), KnownField::PurchaseInfo },
298 { purchaseOwner(), KnownField::PurchaseOwner },
299 { purchasePrice(), KnownField::PurchasePrice },
300 { purchaseCurrency(), KnownField::PurchaseCurrency },
301 { copyright(), KnownField::Copyright },
302 { productionCopyright(), KnownField::ProductionCopyright },
303 { license(), KnownField::License },
304 { termsOfUse(), KnownField::TermsOfUse },
305 });
306 const auto knownField(fieldMap.find(id));
307 return knownField != fieldMap.cend() ? knownField->second : KnownField::Invalid;
308}
309
318{
319 parse2(tagElement, MatroskaTagFlags::None, diag);
320}
321
330{
331 static const string context("parsing Matroska tag");
332 m_size = tagElement.totalSize();
333 tagElement.parse(diag);
334 if (tagElement.totalSize() > numeric_limits<std::uint32_t>::max()) {
335 // FIXME: Support this? Likely not very useful in practise.
336 diag.emplace_back(DiagLevel::Critical, "Matroska tag is too big.", context);
338 }
339 const auto normalize = flags & MatroskaTagFlags::NormalizeKnownFieldIds;
340 for (EbmlElement *child = tagElement.firstChild(); child; child = child->nextSibling()) {
341 child->parse(diag);
342 switch (child->id()) {
344 try {
345 auto field = MatroskaTagField();
346 field.reparse(*child, diag, true);
347 if (normalize) {
348 auto normalizedId = field.id();
349 MatroskaTagField::normalizeId(normalizedId);
350 if (internallyGetKnownField(normalizedId) != KnownField::Invalid) {
351 field.id() = std::move(normalizedId);
352 }
353 }
354 fields().emplace(field.id(), std::move(field));
355 } catch (const Failure &) {
356 // message will be added to diag anyways
357 }
358 break;
360 parseTargets(*child, diag);
361 break;
362 }
363 }
364}
365
373void MatroskaTag::parseTargets(EbmlElement &targetsElement, Diagnostics &diag)
374{
375 static const string context("parsing targets of Matroska tag");
376 m_target.clear();
377 bool targetTypeValueFound = false;
378 bool targetTypeFound = false;
379 targetsElement.parse(diag);
380 for (EbmlElement *child = targetsElement.firstChild(); child; child = child->nextSibling()) {
381 try {
382 child->parse(diag);
383 } catch (const Failure &) {
384 diag.emplace_back(DiagLevel::Critical, "Unable to parse children of Targets element.", context);
385 break;
386 }
387 switch (child->id()) {
389 if (!targetTypeValueFound) {
390 m_target.setLevel(child->readUInteger());
391 targetTypeValueFound = true;
392 } else {
393 diag.emplace_back(
394 DiagLevel::Warning, "Targets element contains multiple TargetTypeValue elements. Surplus elements will be ignored.", context);
395 }
396 break;
398 if (!targetTypeFound) {
399 m_target.setLevelName(child->readString());
400 targetTypeFound = true;
401 } else {
402 diag.emplace_back(
403 DiagLevel::Warning, "Targets element contains multiple TargetType elements. Surplus elements will be ignored.", context);
404 }
405 break;
407 m_target.tracks().emplace_back(child->readUInteger());
408 break;
410 m_target.editions().emplace_back(child->readUInteger());
411 break;
413 m_target.chapters().emplace_back(child->readUInteger());
414 break;
416 m_target.attachments().emplace_back(child->readUInteger());
417 break;
418 default:
419 diag.emplace_back(DiagLevel::Warning, "Targets element contains unknown element. It will be ignored.", context);
420 }
421 }
422 if (!m_target.level()) {
423 m_target.setLevel(50); // default level
424 }
425}
426
433
438MatroskaTagMaker::MatroskaTagMaker(MatroskaTag &tag, Diagnostics &diag)
439 : m_tag(tag)
440{
441 // calculate size of "Targets" element
442 m_targetsSize = 0; // NOT including ID and size
443 if (m_tag.target().level() != 50) {
444 // size of "TargetTypeValue"
445 m_targetsSize += 2u + 1u + EbmlElement::calculateUIntegerLength(m_tag.target().level());
446 }
447 if (!m_tag.target().levelName().empty()) {
448 // size of "TargetType"
449 m_targetsSize += 2u + EbmlElement::calculateSizeDenotationLength(m_tag.target().levelName().size()) + m_tag.target().levelName().size();
450 }
451 for (const auto &v : initializer_list<vector<std::uint64_t>>{
452 m_tag.target().tracks(), m_tag.target().editions(), m_tag.target().chapters(), m_tag.target().attachments() }) {
453 for (auto uid : v) {
454 // size of UID denotation
455 m_targetsSize += 2u + 1u + EbmlElement::calculateUIntegerLength(uid);
456 }
457 }
458 m_tagSize = 2u + EbmlElement::calculateSizeDenotationLength(m_targetsSize) + m_targetsSize;
459 // calculate size of "SimpleTag" elements
460 m_maker.reserve(m_tag.fields().size());
461 m_simpleTagsSize = 0; // including ID and size
462 for (auto &pair : m_tag.fields()) {
463 if (pair.second.value().isNull()) {
464 continue;
465 }
466 try {
467 m_maker.emplace_back(pair.second.prepareMaking(diag));
468 m_simpleTagsSize += m_maker.back().requiredSize();
469 } catch (const Failure &) {
470 }
471 }
472 m_tagSize += m_simpleTagsSize;
473 m_totalSize = 2u + EbmlElement::calculateSizeDenotationLength(m_tagSize) + m_tagSize;
474}
475
483void MatroskaTagMaker::make(ostream &stream) const
484{
485 // write header
486 char buff[11];
487 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Tag), buff);
488 stream.write(buff, 2); // ID
489 std::uint8_t len = EbmlElement::makeSizeDenotation(m_tagSize, buff);
490 stream.write(buff, len); // size
491 // write "Targets" element
492 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::Targets), buff);
493 stream.write(buff, 2);
494 len = EbmlElement::makeSizeDenotation(m_targetsSize, buff);
495 stream.write(buff, len);
496 const TagTarget &t = m_tag.target();
497 if (t.level() != 50) {
498 // write "TargetTypeValue"
499 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetTypeValue), buff);
500 stream.write(buff, 2);
501 len = EbmlElement::makeUInteger(t.level(), buff);
502 stream.put(static_cast<char>(0x80 | len));
503 stream.write(buff, len);
504 }
505 if (!t.levelName().empty()) {
506 // write "TargetType"
507 BE::getBytes(static_cast<std::uint16_t>(MatroskaIds::TargetType), buff);
508 stream.write(buff, 2);
509 len = EbmlElement::makeSizeDenotation(t.levelName().size(), buff);
510 stream.write(buff, len);
511 stream.write(t.levelName().c_str(), static_cast<std::streamsize>(t.levelName().size()));
512 }
513 // write UIDs
514 using p = pair<std::uint16_t, vector<std::uint64_t>>;
515 for (const auto &pair : initializer_list<p>{ p(MatroskaIds::TagTrackUID, t.tracks()), p(MatroskaIds::TagEditionUID, t.editions()),
517 if (!pair.second.empty()) {
518 BE::getBytes(pair.first, buff);
519 for (auto uid : pair.second) {
520 len = EbmlElement::makeUInteger(uid, buff + 3);
521 *(buff + 2) = static_cast<char>(0x80 | len);
522 stream.write(buff, 3 + len);
523 }
524 }
525 }
526 // write "SimpleTag" elements using maker objects prepared previously
527 for (const auto &maker : m_maker) {
528 maker.make(stream);
529 }
530}
531
532} // 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 std::uint8_t makeUInteger(std::uint64_t value, char *buff)
Writes value to buff.
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.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition exceptions.h:11
typename FieldMapBasedTagTraits< MatroskaTag >::FieldType::IdentifierType IdentifierType
const std::multimap< IdentifierType, FieldType, Compare > & fields() const
KnownField knownField(const IdentifierType &id) const
ImplementationType * nextSibling()
Returns the next sibling of the element.
ImplementationType * firstChild()
Returns the first child of the element.
std::uint64_t totalSize() const
Returns the total size 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...
static void normalizeId(std::string &id)
Ensures the specified id is upper-case as recommended by the Matroska spec.
MatroskaTagField()
Constructs a new MatroskaTagField.
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
Implementation of TagParser::Tag for the Matroska container.
Definition matroskatag.h:72
void parse(EbmlElement &tagElement, Diagnostics &diag)
Parses tag information from the specified tagElement.
void parse2(EbmlElement &tagElement, MatroskaTagFlags flags, Diagnostics &diag)
Parses tag information from the specified tagElement.
IdentifierType internallyGetFieldId(KnownField field) const
KnownField internallyGetKnownField(const IdentifierType &id) const
NotImplementedException() noexcept
Constructs a new exception.
The TagTarget class specifies the target of a tag.
Definition tagtarget.h:20
const IdContainerType & tracks() const
Returns the tracks.
Definition tagtarget.h:105
const IdContainerType & chapters() const
Returns the chapters.
Definition tagtarget.h:121
std::uint64_t level() const
Returns the level.
Definition tagtarget.h:73
const std::string & levelName() const
Returns the level name.
Definition tagtarget.h:89
const IdContainerType & editions() const
Returns the editions.
Definition tagtarget.h:137
void clear()
Clears the TagTarget.
Definition tagtarget.h:177
const IdContainerType & attachments() const
Returns the attachments.
Definition tagtarget.h:153
void setLevel(std::uint64_t level)
Sets the level.
Definition tagtarget.h:81
void setLevelName(const std::string &levelName)
Sets the level name.
Definition tagtarget.h:97
std::uint64_t m_size
Definition tag.h:217
TagTarget m_target
Definition tag.h:219
Encapsulates Matroska tag IDs.
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
KnownField
Specifies the field.
Definition tag.h:37
MatroskaTagFlags
The MatroskaTagFlags enum specifies flags which controls parsing and making of Matroska tags.
Definition matroskatag.h:17