Tag Parser 12.4.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
Loading...
Searching...
No Matches
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