Tag Parser 12.3.1
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
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
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.
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...
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...
The MatroskaTagField class is used by MatroskaTag to store the fields.
static void normalizeId(std::string &id)
Ensures the specified id is upper-case as recommended by the Matroska spec.
void make(std::ostream &stream) const
Saves the tag (specified when constructing the object) to the specified stream (makes a "Tag"-element...
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
This exception is thrown when the an operation is invoked that has not been implemented yet.
The TagTarget class specifies the target of a tag.
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:206
TagTarget m_target
Definition tag.h:208
const TagTarget & target() const
Definition tag.h:246
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
KnownField
Specifies the field.
Definition tag.h:29
MatroskaTagFlags
The MatroskaTagFlags enum specifies flags which controls parsing and making of Matroska tags.
Definition matroskatag.h:17