Tag Parser 12.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mp4tagfield.cpp
Go to the documentation of this file.
1#include "./mp4tagfield.h"
2#include "./mp4atom.h"
3#include "./mp4container.h"
4#include "./mp4ids.h"
5
6#include "../exceptions.h"
7
8#include <c++utilities/conversion/stringbuilder.h>
9#include <c++utilities/io/binaryreader.h>
10#include <c++utilities/io/binarywriter.h>
11
12#include <algorithm>
13#include <limits>
14#include <memory>
15
16using namespace std;
17using namespace CppUtilities;
18
19namespace TagParser {
20
30 : m_parsedRawDataType(RawDataType::Reserved)
31 , m_countryIndicator(0)
32 , m_langIndicator(0)
33{
34}
35
39Mp4TagField::Mp4TagField(IdentifierType id, const TagValue &value)
40 : TagField<Mp4TagField>(id, value)
41 , m_parsedRawDataType(RawDataType::Reserved)
42 , m_countryIndicator(0)
43 , m_langIndicator(0)
44{
45}
46
56Mp4TagField::Mp4TagField(std::string_view mean, std::string_view name, const TagValue &value)
57 : Mp4TagField(Mp4TagAtomIds::Extended, value)
58{
59 m_name = name;
60 m_mean = mean;
61}
62
64static bool assignSpecialInteger(const Mp4Atom &ilstChild, int source, TagValue &dest)
65{
66 switch (ilstChild.id()) {
67 case Mp4TagAtomIds::PreDefinedGenre: // consider number as standard genre index
68 dest.assignStandardGenreIndex(source - 1);
69 return true;
71 using Limits = std::numeric_limits<Mp4TagMediaTypeId>;
72 if (source >= static_cast<int>(Limits::min()) && source <= static_cast<int>(Limits::max())) {
73 if (const auto mediaTypeName = mp4TagMediaTypeName(static_cast<Mp4TagMediaTypeId>(source)); !mediaTypeName.empty()) {
75 return true;
76 }
77 }
78 break;
80 using Limits = std::numeric_limits<Mp4TagContentRatingId>;
81 if (source >= static_cast<int>(Limits::min()) && source <= static_cast<int>(Limits::max())) {
82 if (const auto mediaTypeName = mp4TagContentRatingName(static_cast<Mp4TagContentRatingId>(source)); !mediaTypeName.empty()) {
84 return true;
85 }
86 }
87 break;
88 default:;
89 }
90 return false;
91}
93
105{
106 // prepare reparsing
107 using namespace Mp4AtomIds;
108 using namespace Mp4TagAtomIds;
109 string context("parsing MP4 tag field");
110 ilstChild.parse(diag); // ensure child has been parsed
111 setId(ilstChild.id());
112 context = "parsing MP4 tag field " + ilstChild.idToString();
113 iostream &stream = ilstChild.stream();
114 BinaryReader &reader = ilstChild.container().reader();
115 int dataAtomFound = 0, meanAtomFound = 0, nameAtomFound = 0;
116 for (Mp4Atom *dataAtom = ilstChild.firstChild(); dataAtom; dataAtom = dataAtom->nextSibling()) {
117 try {
118 dataAtom->parse(diag);
119 if (dataAtom->id() == Mp4AtomIds::Data) {
120 if (dataAtom->dataSize() < 8) {
121 diag.emplace_back(DiagLevel::Warning,
122 "Truncated child atom \"data\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
123 context);
124 continue;
125 }
126 auto *val = &value();
127 auto *rawDataType = &m_parsedRawDataType;
128 auto *countryIndicator = &m_countryIndicator;
129 auto *languageIndicator = &m_langIndicator;
130 if (++dataAtomFound > 1) {
131 if (dataAtomFound == 2) {
132 diag.emplace_back(DiagLevel::Warning,
133 "Multiple \"data\" child atom in tag atom (ilst child) found. It will be ignored but preserved when applying changes.",
134 context);
135 }
136 auto &additionalData = m_additionalData.emplace_back();
137 val = &additionalData.value;
138 rawDataType = &additionalData.rawDataType;
139 countryIndicator = &additionalData.countryIndicator;
140 languageIndicator = &additionalData.languageIndicator;
141 }
142 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset()));
143 if (reader.readByte() != 0) {
144 diag.emplace_back(DiagLevel::Warning,
145 "The version indicator byte is not zero, the tag atom might be unsupported and hence not be parsed correctly.", context);
146 }
147 setTypeInfo(*rawDataType = reader.readUInt24BE());
148 try { // try to show warning if parsed raw data type differs from expected raw data type for this atom id
149 const vector<std::uint32_t> expectedRawDataTypes = this->expectedRawDataTypes();
150 if (find(expectedRawDataTypes.cbegin(), expectedRawDataTypes.cend(), m_parsedRawDataType) == expectedRawDataTypes.cend()) {
151 diag.emplace_back(DiagLevel::Warning, "Unexpected data type indicator found.", context);
152 }
153 } catch (const Failure &) {
154 // tag id is unknown, it is not possible to validate parsed data type
155 }
156 *countryIndicator = reader.readUInt16BE(); // FIXME: use locale within the tag value
157 *languageIndicator = reader.readUInt16BE(); // FIXME: use locale within the tag value
158 switch (*rawDataType) {
161 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 8));
162 val->assignText(reader.readString(dataAtom->dataSize() - 8),
164 break;
165 case RawDataType::Gif:
167 case RawDataType::Png:
168 case RawDataType::Bmp: {
169 switch (m_parsedRawDataType) {
170 case RawDataType::Gif:
171 val->setMimeType("image/gif");
172 break;
174 val->setMimeType("image/jpeg");
175 break;
176 case RawDataType::Png:
177 val->setMimeType("image/png");
178 break;
179 case RawDataType::Bmp:
180 val->setMimeType("image/bmp");
181 break;
182 default:;
183 }
184 const auto coverSize = static_cast<streamoff>(dataAtom->dataSize() - 8);
185 auto coverData = make_unique<char[]>(static_cast<size_t>(coverSize));
186 stream.read(coverData.get(), coverSize);
187 val->assignData(std::move(coverData), static_cast<size_t>(coverSize), TagDataType::Picture);
188 break;
189 }
191 auto number = int();
192 if (dataAtom->dataSize() > (8 + 4)) {
193 diag.emplace_back(DiagLevel::Warning, "Data atom stores integer of invalid size. Trying to read data anyway.", context);
194 }
195 if (dataAtom->dataSize() >= (8 + 4)) {
196 number = reader.readInt32BE();
197 } else if (dataAtom->dataSize() == (8 + 2)) {
198 number = reader.readInt16BE();
199 } else if (dataAtom->dataSize() == (8 + 1)) {
200 number = reader.readChar();
201 }
202 if (!assignSpecialInteger(ilstChild, number, *val)) {
203 val->assignInteger(number);
204 }
205 break;
206 }
208 auto number = std::uint64_t();
209 if (dataAtom->dataSize() > (8 + 8)) {
210 diag.emplace_back(DiagLevel::Warning, "Data atom stores integer of invalid size. Trying to read data anyway.", context);
211 }
212 if (dataAtom->dataSize() >= (8 + 8)) {
213 number = reader.readUInt64BE();
214 } else if (dataAtom->dataSize() >= (8 + 4)) {
215 number = reader.readUInt32BE();
216 } else if (dataAtom->dataSize() == (8 + 2)) {
217 number = reader.readUInt16BE();
218 } else if (dataAtom->dataSize() == (8 + 1)) {
219 number = reader.readByte();
220 }
221 if (number <= static_cast<std::uint64_t>(std::numeric_limits<int>::max())
222 && !assignSpecialInteger(ilstChild, static_cast<int>(number), *val)) {
223 val->assignUnsignedInteger(number);
224 }
225 break;
226 }
227 default:
228 switch (ilstChild.id()) {
229 // track number, disk number and genre have no specific data type id
230 case TrackPosition:
231 case DiskPosition: {
232 if (dataAtom->dataSize() < (8 + 6)) {
233 diag.emplace_back(DiagLevel::Warning, "Track/disk position is truncated. Trying to read data anyways.", context);
234 }
235 std::uint16_t pos = 0, total = 0;
236 if (dataAtom->dataSize() >= (8 + 4)) {
237 stream.seekg(2, ios_base::cur);
238 pos = reader.readUInt16BE();
239 }
240 if (dataAtom->dataSize() >= (8 + 6)) {
241 total = reader.readUInt16BE();
242 }
243 val->assignPosition(PositionInSet(pos, total));
244 break;
245 }
246 case PreDefinedGenre:
247 if (dataAtom->dataSize() < (8 + 2)) {
248 diag.emplace_back(DiagLevel::Warning, "Genre index is truncated.", context);
249 } else {
250 val->assignStandardGenreIndex(reader.readUInt16BE() - 1);
251 }
252 break;
253 default: // no supported data type, read raw data
254 const auto dataSize = static_cast<streamsize>(dataAtom->dataSize() - 8);
255 auto data = make_unique<char[]>(static_cast<size_t>(dataSize));
256 stream.read(data.get(), dataSize);
257 if (ilstChild.id() == Mp4TagAtomIds::Cover) {
258 val->assignData(std::move(data), static_cast<size_t>(dataSize), TagDataType::Picture);
259 } else {
260 val->assignData(std::move(data), static_cast<size_t>(dataSize), TagDataType::Undefined);
261 }
262 }
263 }
264 } else if (dataAtom->id() == Mp4AtomIds::Mean) {
265 if (dataAtom->dataSize() < 8) {
266 diag.emplace_back(DiagLevel::Warning,
267 "Truncated child atom \"mean\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
268 context);
269 continue;
270 }
271 if (++meanAtomFound > 1) {
272 if (meanAtomFound == 2) {
273 diag.emplace_back(DiagLevel::Warning,
274 "Tag atom contains more than one mean atom. The additional mean atoms will be ignored and discarded when applying "
275 "changes.",
276 context);
277 }
278 continue;
279 }
280 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 4));
281 m_mean = reader.readString(dataAtom->dataSize() - 4);
282 } else if (dataAtom->id() == Mp4AtomIds::Name) {
283 if (dataAtom->dataSize() < 4) {
284 diag.emplace_back(DiagLevel::Warning,
285 "Truncated child atom \"name\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
286 context);
287 continue;
288 }
289 if (++nameAtomFound > 1) {
290 if (nameAtomFound == 2) {
291 diag.emplace_back(DiagLevel::Warning,
292 "Tag atom contains more than one name atom. The addiational name atoms will be ignored and discarded when applying "
293 "changes.",
294 context);
295 }
296 continue;
297 }
298 stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 4));
299 m_name = reader.readString(dataAtom->dataSize() - 4);
300 } else {
301 diag.emplace_back(DiagLevel::Warning,
302 "Unknown child atom \"" % dataAtom->idToString()
303 + "\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
304 context);
305 }
306 } catch (const Failure &) {
307 diag.emplace_back(DiagLevel::Warning,
308 "Unable to parse all children atom in tag atom (ilst child) found. Invalid children will be ignored and discarded when applying "
309 "changes.",
310 context);
311 }
312 }
313 if (value().isEmpty()) {
314 diag.emplace_back(DiagLevel::Warning, "The field value is empty.", context);
315 }
316}
317
332
340void Mp4TagField::make(ostream &stream, Diagnostics &diag)
341{
342 prepareMaking(diag).make(stream);
343}
344
348std::vector<std::uint32_t> Mp4TagField::expectedRawDataTypes() const
349{
350 using namespace Mp4TagAtomIds;
351 std::vector<std::uint32_t> res;
352 switch (id()) {
353 case Album:
354 case Artist:
355 case Comment:
356 case Year:
357 case Title:
358 case Genre:
359 case Composer:
360 case Encoder:
361 case Grouping:
362 case Description:
363 case Lyrics:
364 case RecordLabel:
365 case Performers:
366 case Lyricist:
367 res.push_back(RawDataType::Utf8);
368 res.push_back(RawDataType::Utf16);
369 break;
370 case PreDefinedGenre:
371 case TrackPosition:
372 case DiskPosition:
373 res.push_back(RawDataType::Reserved);
374 break;
375 case Bpm:
377 case Rating: // 0 = None, 1 = Explicit, 2 = Clean
378 res.push_back(RawDataType::BeSignedInt);
379 res.push_back(RawDataType::BeUnsignedInt);
380 break;
381 case Cover:
382 res.push_back(RawDataType::Gif);
383 res.push_back(RawDataType::Jpeg);
384 res.push_back(RawDataType::Png);
385 res.push_back(RawDataType::Bmp);
386 break;
387 case Extended:
389 throw Failure();
390 }
391 // assumption that extended "iTunes" tags always use Unicode correct?
392 res.push_back(RawDataType::Utf8);
393 res.push_back(RawDataType::Utf16);
394 break;
395 default:
396 throw Failure();
397 }
398 return res;
399}
400
409{
410 if (isTypeInfoAssigned()) {
411 // obtain raw data type from tag field if present
412 return typeInfo();
413 }
414
415 // there is no raw data type assigned (tag field was not present in original file and
416 // has been inserted by the library's user without type)
417 // -> try to derive appropriate raw data type from atom ID
419}
420
430{
431 using namespace Mp4TagAtomIds;
432 switch (id()) {
433 case Album:
434 case Artist:
435 case Comment:
436 case Year:
437 case Title:
438 case Genre:
439 case Composer:
440 case Encoder:
441 case Grouping:
442 case Description:
443 case Lyrics:
444 case RecordLabel:
445 case Performers:
446 case Lyricist:
447 case AlbumArtist:
448 switch (value.dataEncoding()) {
450 return RawDataType::Utf8;
452 return RawDataType::Utf16;
453 default:;
454 }
455 break;
456 case TrackPosition:
457 case DiskPosition:
459 case PreDefinedGenre:
460 case Bpm:
461 case Rating:
464 case Cover: {
465 const string &mimeType = value.mimeType();
466 if (mimeType == "image/jpg" || mimeType == "image/jpeg") { // "well-known" type
467 return RawDataType::Jpeg;
468 } else if (mimeType == "image/png") {
469 return RawDataType::Png;
470 } else if (mimeType == "image/bmp") {
471 return RawDataType::Bmp;
472 }
473 } break;
474 case Extended:
476 throw Failure();
477 }
478 switch (value.dataEncoding()) {
480 return RawDataType::Utf8;
482 return RawDataType::Utf16;
483 default:;
484 }
485 break;
486 default:;
487 }
488
489 // do not forget to extend Mp4Tag::internallyGetFieldId() and Mp4Tag::internallyGetKnownField() as well
490
491 throw Failure();
492}
493
497void Mp4TagField::internallyClearValue()
498{
500 m_additionalData.clear();
501 m_countryIndicator = 0;
502 m_langIndicator = 0;
503}
504
508void Mp4TagField::internallyClearFurtherData()
509{
510 m_name.clear();
511 m_mean.clear();
512 m_parsedRawDataType = RawDataType::Reserved;
513}
514
516Mp4TagFieldMaker::Data::Data()
517 : convertedData(stringstream::in | stringstream::out | stringstream::binary)
518{
519}
521
533Mp4TagFieldMaker::Mp4TagFieldMaker(Mp4TagField &field, Diagnostics &diag)
534 : m_field(field)
535 , m_writer(nullptr)
536 , m_totalSize(0)
537{
538 if (!m_field.id()) {
539 diag.emplace_back(DiagLevel::Warning, "Invalid tag atom ID.", "making MP4 tag field");
540 throw InvalidDataException();
541 }
542 const string context("making MP4 tag field " + Mp4TagField::fieldIdToString(m_field.id()));
543 if (m_field.value().isEmpty() && (!m_field.mean().empty() || !m_field.name().empty())) {
544 diag.emplace_back(DiagLevel::Critical, "No tag value assigned.", context);
545 throw InvalidDataException();
546 }
547
548 // calculate size for name and mean
549 m_totalSize = 8 + (m_field.name().empty() ? 0 : (12 + m_field.name().size())) + (m_field.mean().empty() ? 0 : (12 + m_field.mean().size()));
550
551 // prepare making data atom and calculate the expected size
552 m_totalSize += prepareDataAtom(field.value(), field.countryIndicator(), field.languageIndicator(), context, diag);
553 for (const auto &additionalData : m_field.additionalData()) {
554 m_totalSize += prepareDataAtom(additionalData.value, additionalData.countryIndicator, additionalData.languageIndicator, context, diag);
555 }
556
557 if (m_totalSize > numeric_limits<std::uint32_t>::max()) {
558 diag.emplace_back(DiagLevel::Critical, "Making a such big MP4 tag field is not possible.", context);
559 throw NotImplementedException();
560 }
561}
562
564static bool writeSpecialInteger(std::underlying_type_t<Mp4TagAtomIds::KnownValue> fieldId, const TagValue &value, CppUtilities::BinaryWriter &writer)
565{
566 switch (fieldId) {
568 if (value.type() == TagDataType::Text) {
569 if (const auto id = mp4TagMediaTypeId(value.toString(TagTextEncoding::Utf8)); id.has_value()) {
570 writer.writeByte(static_cast<Mp4TagMediaTypeId>(id.value()));
571 return true;
572 }
573 }
574 break;
576 if (value.type() == TagDataType::Text) {
577 if (const auto id = mp4TagContentRatingId(value.toString(TagTextEncoding::Utf8)); id.has_value()) {
578 writer.writeByte(static_cast<Mp4TagContentRatingId>(id.value()));
579 return true;
580 }
581 }
582 break;
583 default:;
584 }
585 return false;
586}
588
592std::uint64_t Mp4TagFieldMaker::prepareDataAtom(
593 const TagValue &value, std::uint16_t countryIndicator, std::uint16_t languageIndicator, const std::string &context, Diagnostics &diag)
594{
595 // add new data entry
596 auto &data = m_data.emplace_back();
597 m_writer.setStream(&data.convertedData);
598
599 // assign local info
600 // FIXME: use locale within the tag value instead of just passing through current values
601 data.countryIndicator = countryIndicator;
602 data.languageIndicator = languageIndicator;
603
604 try {
605 // try to use appropriate raw data type
606 data.rawType = m_field.isTypeInfoAssigned() ? m_field.typeInfo() : m_field.appropriateRawDataTypeForValue(value);
607 } catch (const Failure &) {
608 // unable to obtain appropriate raw data type
609 if (m_field.id() == Mp4TagAtomIds::Cover) {
610 // assume JPEG image
611 data.rawType = RawDataType::Jpeg;
612 diag.emplace_back(
613 DiagLevel::Warning, "It was not possible to find an appropriate raw data type id. JPEG image will be assumed.", context);
614 } else {
615 // assume UTF-8 text
616 data.rawType = RawDataType::Utf8;
617 diag.emplace_back(DiagLevel::Warning, "It was not possible to find an appropriate raw data type id. UTF-8 will be assumed.", context);
618 }
619 }
620
621 try {
622 if (!value.isEmpty()) { // there might be only mean and name info, but no data
623 data.convertedData.exceptions(std::stringstream::failbit | std::stringstream::badbit);
624 switch (data.rawType) {
626 if (value.type() != TagDataType::Text || value.dataEncoding() != TagTextEncoding::Utf8) {
627 m_writer.writeString(value.toString(TagTextEncoding::Utf8));
628 }
629 break;
631 if (value.type() != TagDataType::Text || value.dataEncoding() != TagTextEncoding::Utf16LittleEndian) {
632 m_writer.writeString(value.toString(TagTextEncoding::Utf16LittleEndian));
633 }
634 break;
636 if (writeSpecialInteger(m_field.id(), value, m_writer)) {
637 break;
638 }
639 const auto number = value.toInteger();
640 if (number <= numeric_limits<std::int16_t>::max() && number >= numeric_limits<std::int16_t>::min()) {
641 m_writer.writeInt16BE(static_cast<std::int16_t>(number));
642 } else {
643 m_writer.writeInt32BE(number);
644 }
645 break;
646 }
648 if (writeSpecialInteger(m_field.id(), value, m_writer)) {
649 break;
650 }
651 const auto number = value.toUnsignedInteger();
652 if (number <= numeric_limits<std::uint16_t>::max()) {
653 m_writer.writeUInt16BE(static_cast<std::uint16_t>(number));
654 } else if (number <= numeric_limits<std::uint32_t>::max()) {
655 m_writer.writeUInt32BE(static_cast<std::uint32_t>(number));
656 } else {
657 m_writer.writeUInt64BE(number);
658 }
659 break;
660 }
661 case RawDataType::Bmp:
663 case RawDataType::Png:
664 break; // leave converted data empty to write original data later
665 default:
666 switch (m_field.id()) {
667 // track number and disk number are exceptions
668 // raw data type 0 is used, information is stored as pair of unsigned integers
671 PositionInSet pos = value.toPositionInSet();
672 m_writer.writeInt32BE(pos.position());
673 if (pos.total() <= numeric_limits<std::int16_t>::max()) {
674 m_writer.writeInt16BE(static_cast<std::int16_t>(pos.total()));
675 } else {
676 throw ConversionException(
677 "Integer can not be assigned to the field with the id \"" % interpretIntegerAsString<std::uint32_t>(m_field.id())
678 + "\" because it is to big.");
679 }
680 m_writer.writeUInt16BE(0);
681 break;
682 }
684 m_writer.writeUInt16BE(static_cast<std::uint16_t>(value.toStandardGenreIndex()));
685 break;
686 default:
687 if (writeSpecialInteger(m_field.id(), value, m_writer)) {
688 break;
689 }
690 // leave converted data empty to write original data later
691 }
692 }
693 }
694 } catch (const ConversionException &e) {
695 // it was not possible to perform required conversions
696 if (const auto what = std::string_view(e.what()); !what.empty()) {
697 diag.emplace_back(DiagLevel::Critical, e.what(), context);
698 } else {
699 diag.emplace_back(DiagLevel::Critical, "The assigned tag value can not be converted to be written appropriately.", context);
700 }
701 throw InvalidDataException();
702 }
703
704 // calculate data size; assign raw data
705 if (value.isEmpty()) {
706 return data.size = 0;
707 } else if (data.convertedData.tellp()) {
708 data.size = static_cast<std::size_t>(data.convertedData.tellp());
709 } else {
710 data.rawData = std::string_view(value.dataPointer(), data.size = value.dataSize());
711 }
712 return data.size += 16;
713}
714
722void Mp4TagFieldMaker::make(ostream &stream)
723{
724 m_writer.setStream(&stream);
725 // size of entire tag atom
726 m_writer.writeUInt32BE(static_cast<std::uint32_t>(m_totalSize));
727 // id of tag atom
728 m_writer.writeUInt32BE(m_field.id());
729 // write "mean" atom
730 if (!m_field.mean().empty()) {
731 m_writer.writeUInt32BE(static_cast<std::uint32_t>(12 + m_field.mean().size()));
732 m_writer.writeUInt32BE(Mp4AtomIds::Mean);
733 m_writer.writeUInt32BE(0);
734 m_writer.writeString(m_field.mean());
735 }
736 // write "name" atom
737 if (!m_field.name().empty()) {
738 m_writer.writeUInt32BE(static_cast<std::uint32_t>(12 + m_field.name().length()));
739 m_writer.writeUInt32BE(Mp4AtomIds::Name);
740 m_writer.writeUInt32BE(0);
741 m_writer.writeString(m_field.name());
742 }
743 // write "data" atoms
744 for (auto &data : m_data) {
745 if (!data.size) {
746 continue;
747 }
748 m_writer.writeUInt32BE(static_cast<std::uint32_t>(data.size)); // size of data atom
749 m_writer.writeUInt32BE(Mp4AtomIds::Data); // id of data atom
750 m_writer.writeByte(0); // version
751 m_writer.writeUInt24BE(data.rawType);
752 m_writer.writeUInt16BE(data.countryIndicator);
753 m_writer.writeUInt16BE(data.languageIndicator);
754 if (data.convertedData.tellp()) {
755 // write converted data
756 stream << data.convertedData.rdbuf();
757 } else {
758 // no conversion was needed, write data directly from tag value
759 stream.write(data.rawData.data(), static_cast<std::streamoff>(data.rawData.size()));
760 }
761 }
762}
763
764} // namespace TagParser
The Diagnostics class is a container for DiagMessage.
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
const IdentifierType & id() const
Returns the element ID.
std::iostream & stream()
Returns the related stream.
ImplementationType * nextSibling()
Returns the next sibling 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...
ContainerType & container()
Returns the related container.
The Mp4Atom class helps to parse MP4 files.
std::string idToString() const
Converts the specified atom ID to a printable string.
Definition mp4atom.h:67
The Mp4TagFieldMaker class helps making tag fields.
void make(std::ostream &stream)
Saves the field (specified when constructing the object) to the specified stream.
Mp4TagFieldMaker(Mp4TagFieldMaker &&)=default
The Mp4TagField class is used by Mp4Tag to store the fields.
Mp4TagFieldMaker prepareMaking(Diagnostics &diag)
Prepares making.
const std::vector< AdditionalData > & additionalData() const
Returns additional data (and the corresponding raw data type, country and language).
void reparse(Mp4Atom &ilstChild, Diagnostics &diag)
Parses field information from the specified Mp4Atom.
const std::string & name() const
Returns the "name" for "extended" fields.
Mp4TagField()
Constructs a new Mp4TagField.
std::uint32_t appropriateRawDataTypeForValue(const TagValue &value) const
Returns an appropriate raw data type.
const std::string & mean() const
Returns the "mean" for "extended" fields.
std::uint32_t appropriateRawDataType() const
Returns an appropriate raw data type.
std::uint16_t languageIndicator() const
Returns the language indicator.
std::vector< std::uint32_t > expectedRawDataTypes() const
Returns the expected raw data types for the ID of the field.
std::uint16_t countryIndicator() const
Returns the country indicator.
void make(std::ostream &stream, Diagnostics &diag)
Saves the field to the specified stream.
static std::string fieldIdToString(IdentifierType id)
Returns the string representation for the specified id.
The TagField class is used by FieldMapBasedTag to store the fields.
void setTypeInfo(const TypeInfoType &typeInfo)
const TypeInfoType & typeInfo() const
IdentifierType & id()
Returns the id of the current TagField.
void setId(const IdentifierType &id)
The TagValue class wraps values of different types.
void assignText(const char *text, std::size_t textSize, TagTextEncoding textEncoding=TagTextEncoding::Latin1, TagTextEncoding convertTo=TagTextEncoding::Unspecified)
Assigns a copy of the given text.
const std::string & mimeType() const
Returns the MIME type.
Definition tagvalue.h:590
TagTextEncoding dataEncoding() const
Returns the data encoding.
Definition tagvalue.h:718
void clearDataAndMetadata()
Wipes assigned data including meta data.
Definition tagvalue.h:512
void assignStandardGenreIndex(int index)
Assigns the given standard genre index to be assigned.
Definition tagvalue.h:424
Contains all classes and functions of the TagInfo library.
Definition aaccodebook.h:10
TAG_PARSER_EXPORT std::string_view mp4TagMediaTypeName(Mp4TagMediaTypeId tagMediaTypeId)
Definition mp4ids.cpp:502
TAG_PARSER_EXPORT std::string_view mp4TagContentRatingName(Mp4TagContentRatingId tagContentRatingId)
Definition mp4ids.cpp:552
std::uint8_t Mp4TagContentRatingId
Definition mp4ids.h:698
std::uint8_t Mp4TagMediaTypeId
Definition mp4ids.h:676
TAG_PARSER_EXPORT std::string_view mediaTypeName(MediaType mediaType)
Returns the string representation for the specified mediaType.
TAG_PARSER_EXPORT std::optional< Mp4TagContentRating > mp4TagContentRatingId(std::string_view tagContentRatingName)
Definition mp4ids.cpp:564
TAG_PARSER_EXPORT std::optional< Mp4TagMediaType > mp4TagMediaTypeId(std::string_view tagMediaTypeName)
Definition mp4ids.cpp:530