Tag Parser  10.0.0
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
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 
16 using namespace std;
17 using namespace CppUtilities;
18 
19 namespace TagParser {
20 
29 Mp4TagField::Mp4TagField()
30  : m_parsedRawDataType(RawDataType::Reserved)
31  , m_countryIndicator(0)
32  , m_langIndicator(0)
33 {
34 }
35 
39 Mp4TagField::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 
56 Mp4TagField::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 
73 void Mp4TagField::reparse(Mp4Atom &ilstChild, Diagnostics &diag)
74 {
75  // prepare reparsing
76  using namespace Mp4AtomIds;
77  using namespace Mp4TagAtomIds;
78  string context("parsing MP4 tag field");
79  ilstChild.parse(diag); // ensure child has been parsed
80  setId(ilstChild.id());
81  context = "parsing MP4 tag field " + ilstChild.idToString();
82  iostream &stream = ilstChild.stream();
83  BinaryReader &reader = ilstChild.container().reader();
84  int dataAtomFound = 0, meanAtomFound = 0, nameAtomFound = 0;
85  for (Mp4Atom *dataAtom = ilstChild.firstChild(); dataAtom; dataAtom = dataAtom->nextSibling()) {
86  try {
87  dataAtom->parse(diag);
88  if (dataAtom->id() == Mp4AtomIds::Data) {
89  if (dataAtom->dataSize() < 8) {
90  diag.emplace_back(DiagLevel::Warning,
91  "Truncated child atom \"data\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
92  context);
93  continue;
94  }
95  auto *val = &value();
96  auto *rawDataType = &m_parsedRawDataType;
97  auto *countryIndicator = &m_countryIndicator;
98  auto *languageIndicator = &m_langIndicator;
99  if (++dataAtomFound > 1) {
100  if (dataAtomFound == 2) {
101  diag.emplace_back(DiagLevel::Warning,
102  "Multiple \"data\" child atom in tag atom (ilst child) found. It will be ignored but preserved when applying changes.",
103  context);
104  }
105  auto &additionalData = m_additionalData.emplace_back();
106  val = &additionalData.value;
107  rawDataType = &additionalData.rawDataType;
108  countryIndicator = &additionalData.countryIndicator;
109  languageIndicator = &additionalData.languageIndicator;
110  }
111  stream.seekg(static_cast<streamoff>(dataAtom->dataOffset()));
112  if (reader.readByte() != 0) {
113  diag.emplace_back(DiagLevel::Warning,
114  "The version indicator byte is not zero, the tag atom might be unsupported and hence not be parsed correctly.", context);
115  }
116  setTypeInfo(*rawDataType = reader.readUInt24BE());
117  try { // try to show warning if parsed raw data type differs from expected raw data type for this atom id
118  const vector<std::uint32_t> expectedRawDataTypes = this->expectedRawDataTypes();
119  if (find(expectedRawDataTypes.cbegin(), expectedRawDataTypes.cend(), m_parsedRawDataType) == expectedRawDataTypes.cend()) {
120  diag.emplace_back(DiagLevel::Warning, "Unexpected data type indicator found.", context);
121  }
122  } catch (const Failure &) {
123  // tag id is unknown, it is not possible to validate parsed data type
124  }
125  *countryIndicator = reader.readUInt16BE(); // FIXME: use locale within the tag value
126  *languageIndicator = reader.readUInt16BE(); // FIXME: use locale within the tag value
127  switch (*rawDataType) {
128  case RawDataType::Utf8:
129  case RawDataType::Utf16:
130  stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 8));
131  val->assignText(reader.readString(dataAtom->dataSize() - 8),
133  break;
134  case RawDataType::Gif:
135  case RawDataType::Jpeg:
136  case RawDataType::Png:
137  case RawDataType::Bmp: {
138  switch (m_parsedRawDataType) {
139  case RawDataType::Gif:
140  val->setMimeType("image/gif");
141  break;
142  case RawDataType::Jpeg:
143  val->setMimeType("image/jpeg");
144  break;
145  case RawDataType::Png:
146  val->setMimeType("image/png");
147  break;
148  case RawDataType::Bmp:
149  val->setMimeType("image/bmp");
150  break;
151  default:;
152  }
153  const auto coverSize = static_cast<streamoff>(dataAtom->dataSize() - 8);
154  auto coverData = make_unique<char[]>(static_cast<size_t>(coverSize));
155  stream.read(coverData.get(), coverSize);
156  val->assignData(move(coverData), static_cast<size_t>(coverSize), TagDataType::Picture);
157  break;
158  }
160  int number = 0;
161  if (dataAtom->dataSize() > (8 + 4)) {
162  diag.emplace_back(DiagLevel::Warning, "Data atom stores integer of invalid size. Trying to read data anyways.", context);
163  }
164  if (dataAtom->dataSize() >= (8 + 4)) {
165  number = reader.readInt32BE();
166  } else if (dataAtom->dataSize() == (8 + 2)) {
167  number = reader.readInt16BE();
168  } else if (dataAtom->dataSize() == (8 + 1)) {
169  number = reader.readChar();
170  }
171  switch (ilstChild.id()) {
172  case PreDefinedGenre: // consider number as standard genre index
173  val->assignStandardGenreIndex(number);
174  break;
175  default:
176  val->assignInteger(number);
177  }
178  break;
179  }
181  int number = 0;
182  if (dataAtom->dataSize() > (8 + 4)) {
183  diag.emplace_back(DiagLevel::Warning, "Data atom stores integer of invalid size. Trying to read data anyways.", context);
184  }
185  if (dataAtom->dataSize() >= (8 + 4)) {
186  number = static_cast<int>(reader.readUInt32BE());
187  } else if (dataAtom->dataSize() == (8 + 2)) {
188  number = static_cast<int>(reader.readUInt16BE());
189  } else if (dataAtom->dataSize() == (8 + 1)) {
190  number = static_cast<int>(reader.readByte());
191  }
192  switch (ilstChild.id()) {
193  case PreDefinedGenre: // consider number as standard genre index
194  val->assignStandardGenreIndex(number - 1);
195  break;
196  default:
197  val->assignInteger(number);
198  }
199  break;
200  }
201  default:
202  switch (ilstChild.id()) {
203  // track number, disk number and genre have no specific data type id
204  case TrackPosition:
205  case DiskPosition: {
206  if (dataAtom->dataSize() < (8 + 6)) {
207  diag.emplace_back(DiagLevel::Warning, "Track/disk position is truncated. Trying to read data anyways.", context);
208  }
209  std::uint16_t pos = 0, total = 0;
210  if (dataAtom->dataSize() >= (8 + 4)) {
211  stream.seekg(2, ios_base::cur);
212  pos = reader.readUInt16BE();
213  }
214  if (dataAtom->dataSize() >= (8 + 6)) {
215  total = reader.readUInt16BE();
216  }
217  val->assignPosition(PositionInSet(pos, total));
218  break;
219  }
220  case PreDefinedGenre:
221  if (dataAtom->dataSize() < (8 + 2)) {
222  diag.emplace_back(DiagLevel::Warning, "Genre index is truncated.", context);
223  } else {
224  val->assignStandardGenreIndex(reader.readUInt16BE() - 1);
225  }
226  break;
227  default: // no supported data type, read raw data
228  const auto dataSize = static_cast<streamsize>(dataAtom->dataSize() - 8);
229  auto data = make_unique<char[]>(static_cast<size_t>(dataSize));
230  stream.read(data.get(), dataSize);
231  if (ilstChild.id() == Mp4TagAtomIds::Cover) {
232  val->assignData(move(data), static_cast<size_t>(dataSize), TagDataType::Picture);
233  } else {
234  val->assignData(move(data), static_cast<size_t>(dataSize), TagDataType::Undefined);
235  }
236  }
237  }
238  } else if (dataAtom->id() == Mp4AtomIds::Mean) {
239  if (dataAtom->dataSize() < 8) {
240  diag.emplace_back(DiagLevel::Warning,
241  "Truncated child atom \"mean\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
242  context);
243  continue;
244  }
245  if (++meanAtomFound > 1) {
246  if (meanAtomFound == 2) {
247  diag.emplace_back(DiagLevel::Warning,
248  "Tag atom contains more than one mean atom. The additional mean atoms will be ignored and discarded when applying "
249  "changes.",
250  context);
251  }
252  continue;
253  }
254  stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 4));
255  m_mean = reader.readString(dataAtom->dataSize() - 4);
256  } else if (dataAtom->id() == Mp4AtomIds::Name) {
257  if (dataAtom->dataSize() < 4) {
258  diag.emplace_back(DiagLevel::Warning,
259  "Truncated child atom \"name\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
260  context);
261  continue;
262  }
263  if (++nameAtomFound > 1) {
264  if (nameAtomFound == 2) {
265  diag.emplace_back(DiagLevel::Warning,
266  "Tag atom contains more than one name atom. The addiational name atoms will be ignored and discarded when applying "
267  "changes.",
268  context);
269  }
270  continue;
271  }
272  stream.seekg(static_cast<streamoff>(dataAtom->dataOffset() + 4));
273  m_name = reader.readString(dataAtom->dataSize() - 4);
274  } else {
275  diag.emplace_back(DiagLevel::Warning,
276  "Unkown child atom \"" % dataAtom->idToString()
277  + "\" in tag atom (ilst child) found. It will be ignored and discarded when applying changes.",
278  context);
279  }
280  } catch (const Failure &) {
281  diag.emplace_back(DiagLevel::Warning,
282  "Unable to parse all children atom in tag atom (ilst child) found. Invalid children will be ignored and discarded when applying "
283  "changes.",
284  context);
285  }
286  }
287  if (value().isEmpty()) {
288  diag.emplace_back(DiagLevel::Warning, "The field value is empty.", context);
289  }
290 }
291 
303 {
304  return Mp4TagFieldMaker(*this, diag);
305 }
306 
314 void Mp4TagField::make(ostream &stream, Diagnostics &diag)
315 {
316  prepareMaking(diag).make(stream);
317 }
318 
322 std::vector<std::uint32_t> Mp4TagField::expectedRawDataTypes() const
323 {
324  using namespace Mp4TagAtomIds;
325  std::vector<std::uint32_t> res;
326  switch (id()) {
327  case Album:
328  case Artist:
329  case Comment:
330  case Year:
331  case Title:
332  case Genre:
333  case Composer:
334  case Encoder:
335  case Grouping:
336  case Description:
337  case Lyrics:
338  case RecordLabel:
339  case Performers:
340  case Lyricist:
341  res.push_back(RawDataType::Utf8);
342  res.push_back(RawDataType::Utf16);
343  break;
344  case PreDefinedGenre:
345  case TrackPosition:
346  case DiskPosition:
347  res.push_back(RawDataType::Reserved);
348  break;
349  case Bpm:
350  case Rating:
351  res.push_back(RawDataType::BeSignedInt);
352  res.push_back(RawDataType::BeUnsignedInt);
353  break;
354  case Cover:
355  res.push_back(RawDataType::Gif);
356  res.push_back(RawDataType::Jpeg);
357  res.push_back(RawDataType::Png);
358  res.push_back(RawDataType::Bmp);
359  break;
360  case Extended:
362  throw Failure();
363  }
364  // assumption that extended "iTunes" tags always use Unicode correct?
365  res.push_back(RawDataType::Utf8);
366  res.push_back(RawDataType::Utf16);
367  break;
368  default:
369  throw Failure();
370  }
371  return res;
372 }
373 
382 {
383  if (isTypeInfoAssigned()) {
384  // obtain raw data type from tag field if present
385  return typeInfo();
386  }
387 
388  // there is no raw data type assigned (tag field was not present in original file and
389  // has been inserted by the library's user without type)
390  // -> try to derive appropriate raw data type from atom ID
392 }
393 
403 {
404  using namespace Mp4TagAtomIds;
405  switch (id()) {
406  case Album:
407  case Artist:
408  case Comment:
409  case Year:
410  case Title:
411  case Genre:
412  case Composer:
413  case Encoder:
414  case Grouping:
415  case Description:
416  case Lyrics:
417  case RecordLabel:
418  case Performers:
419  case Lyricist:
420  case AlbumArtist:
421  switch (value.dataEncoding()) {
423  return RawDataType::Utf8;
425  return RawDataType::Utf16;
426  default:;
427  }
428  break;
429  case TrackPosition:
430  case DiskPosition:
431  return RawDataType::Reserved;
432  case PreDefinedGenre:
433  case Bpm:
434  case Rating:
436  case Cover: {
437  const string &mimeType = value.mimeType();
438  if (mimeType == "image/jpg" || mimeType == "image/jpeg") { // "well-known" type
439  return RawDataType::Jpeg;
440  } else if (mimeType == "image/png") {
441  return RawDataType::Png;
442  } else if (mimeType == "image/bmp") {
443  return RawDataType::Bmp;
444  }
445  } break;
446  case Extended:
448  throw Failure();
449  }
450  switch (value.dataEncoding()) {
452  return RawDataType::Utf8;
454  return RawDataType::Utf16;
455  default:;
456  }
457  break;
458  default:;
459  }
460 
461  // do not forget to extend Mp4Tag::internallyGetFieldId() and Mp4Tag::internallyGetKnownField() as well
462 
463  throw Failure();
464 }
465 
469 void Mp4TagField::internallyClearValue()
470 {
472  m_additionalData.clear();
473  m_countryIndicator = 0;
474  m_langIndicator = 0;
475 }
476 
480 void Mp4TagField::internallyClearFurtherData()
481 {
482  m_name.clear();
483  m_mean.clear();
484  m_parsedRawDataType = RawDataType::Reserved;
485 }
486 
489  : convertedData(stringstream::in | stringstream::out | stringstream::binary)
490 {
491 }
493 
505 Mp4TagFieldMaker::Mp4TagFieldMaker(Mp4TagField &field, Diagnostics &diag)
506  : m_field(field)
507  , m_writer(nullptr)
508  , m_totalSize(0)
509 {
510  if (!m_field.id()) {
511  diag.emplace_back(DiagLevel::Warning, "Invalid tag atom ID.", "making MP4 tag field");
512  throw InvalidDataException();
513  }
514  const string context("making MP4 tag field " + Mp4TagField::fieldIdToString(m_field.id()));
515  if (m_field.value().isEmpty() && (!m_field.mean().empty() || !m_field.name().empty())) {
516  diag.emplace_back(DiagLevel::Critical, "No tag value assigned.", context);
517  throw InvalidDataException();
518  }
519 
520  // calculate size for name and mean
521  m_totalSize = 8 + (m_field.name().empty() ? 0 : (12 + m_field.name().size())) + (m_field.mean().empty() ? 0 : (12 + m_field.mean().size()));
522 
523  // prepare making data atom and calculate the expected size
524  m_totalSize += prepareDataAtom(field.value(), field.countryIndicator(), field.languageIndicator(), context, diag);
525  for (const auto &additionalData : m_field.additionalData()) {
526  m_totalSize += prepareDataAtom(additionalData.value, additionalData.countryIndicator, additionalData.languageIndicator, context, diag);
527  }
528 
529  if (m_totalSize > numeric_limits<std::uint32_t>::max()) {
530  diag.emplace_back(DiagLevel::Critical, "Making a such big MP4 tag field is not possible.", context);
531  throw NotImplementedException();
532  }
533 }
534 
538 std::uint64_t Mp4TagFieldMaker::prepareDataAtom(
539  const TagValue &value, std::uint16_t countryIndicator, std::uint16_t languageIndicator, const std::string &context, Diagnostics &diag)
540 {
541  // add new data entry
542  auto &data = m_data.emplace_back();
543  m_writer.setStream(&data.convertedData);
544 
545  // assign local info
546  // FIXME: use locale within the tag value instead of just passing through current values
547  data.countryIndicator = countryIndicator;
548  data.languageIndicator = languageIndicator;
549 
550  try {
551  // try to use appropriate raw data type
552  data.rawType = m_field.isTypeInfoAssigned() ? m_field.typeInfo() : m_field.appropriateRawDataTypeForValue(value);
553  } catch (const Failure &) {
554  // unable to obtain appropriate raw data type
555  if (m_field.id() == Mp4TagAtomIds::Cover) {
556  // assume JPEG image
557  data.rawType = RawDataType::Jpeg;
558  diag.emplace_back(
559  DiagLevel::Warning, "It was not possible to find an appropriate raw data type id. JPEG image will be assumed.", context);
560  } else {
561  // assume UTF-8 text
562  data.rawType = RawDataType::Utf8;
563  diag.emplace_back(DiagLevel::Warning, "It was not possible to find an appropriate raw data type id. UTF-8 will be assumed.", context);
564  }
565  }
566 
567  try {
568  if (!value.isEmpty()) { // there might be only mean and name info, but no data
569  data.convertedData.exceptions(std::stringstream::failbit | std::stringstream::badbit);
570  switch (data.rawType) {
571  case RawDataType::Utf8:
572  if (value.type() != TagDataType::Text || value.dataEncoding() != TagTextEncoding::Utf8) {
573  m_writer.writeString(value.toString(TagTextEncoding::Utf8));
574  }
575  break;
576  case RawDataType::Utf16:
577  if (value.type() != TagDataType::Text || value.dataEncoding() != TagTextEncoding::Utf16LittleEndian) {
578  m_writer.writeString(value.toString(TagTextEncoding::Utf16LittleEndian));
579  }
580  break;
582  int number = value.toInteger();
583  if (number <= numeric_limits<std::int16_t>::max() && number >= numeric_limits<std::int16_t>::min()) {
584  m_writer.writeInt16BE(static_cast<std::int16_t>(number));
585  } else {
586  m_writer.writeInt32BE(number);
587  }
588  break;
589  }
591  int number = value.toInteger();
592  if (number <= numeric_limits<std::uint16_t>::max() && number >= numeric_limits<std::uint16_t>::min()) {
593  m_writer.writeUInt16BE(static_cast<std::uint16_t>(number));
594  } else if (number > 0) {
595  m_writer.writeUInt32BE(static_cast<std::uint32_t>(number));
596  } else {
597  throw ConversionException(
598  "Negative integer can not be assigned to the field with the ID \"" % interpretIntegerAsString<std::uint32_t>(m_field.id())
599  + "\".");
600  }
601  break;
602  }
603  case RawDataType::Bmp:
604  case RawDataType::Jpeg:
605  case RawDataType::Png:
606  break; // leave converted data empty to write original data later
607  default:
608  switch (m_field.id()) {
609  // track number and disk number are exceptions
610  // raw data type 0 is used, information is stored as pair of unsigned integers
613  PositionInSet pos = value.toPositionInSet();
614  m_writer.writeInt32BE(pos.position());
615  if (pos.total() <= numeric_limits<std::int16_t>::max()) {
616  m_writer.writeInt16BE(static_cast<std::int16_t>(pos.total()));
617  } else {
618  throw ConversionException(
619  "Integer can not be assigned to the field with the id \"" % interpretIntegerAsString<std::uint32_t>(m_field.id())
620  + "\" because it is to big.");
621  }
622  m_writer.writeUInt16BE(0);
623  break;
624  }
626  m_writer.writeUInt16BE(static_cast<std::uint16_t>(value.toStandardGenreIndex()));
627  break;
628  default:; // leave converted data empty to write original data later
629  }
630  }
631  }
632  } catch (const ConversionException &e) {
633  // it was not possible to perform required conversions
634  if (char_traits<char>::length(e.what())) {
635  diag.emplace_back(DiagLevel::Critical, e.what(), context);
636  } else {
637  diag.emplace_back(DiagLevel::Critical, "The assigned tag value can not be converted to be written appropriately.", context);
638  }
639  throw InvalidDataException();
640  }
641 
642  // calculate data size; assign raw data
643  if (value.isEmpty()) {
644  return data.size = 0;
645  } else if (data.convertedData.tellp()) {
646  data.size = static_cast<std::size_t>(data.convertedData.tellp());
647  } else {
648  data.rawData = std::string_view(value.dataPointer(), data.size = value.dataSize());
649  }
650  return data.size += 16;
651 }
652 
660 void Mp4TagFieldMaker::make(ostream &stream)
661 {
662  m_writer.setStream(&stream);
663  // size of entire tag atom
664  m_writer.writeUInt32BE(static_cast<std::uint32_t>(m_totalSize));
665  // id of tag atom
666  m_writer.writeUInt32BE(m_field.id());
667  // write "mean" atom
668  if (!m_field.mean().empty()) {
669  m_writer.writeUInt32BE(static_cast<std::uint32_t>(12 + m_field.mean().size()));
670  m_writer.writeUInt32BE(Mp4AtomIds::Mean);
671  m_writer.writeUInt32BE(0);
672  m_writer.writeString(m_field.mean());
673  }
674  // write "name" atom
675  if (!m_field.name().empty()) {
676  m_writer.writeUInt32BE(static_cast<std::uint32_t>(12 + m_field.name().length()));
677  m_writer.writeUInt32BE(Mp4AtomIds::Name);
678  m_writer.writeUInt32BE(0);
679  m_writer.writeString(m_field.name());
680  }
681  // write "data" atoms
682  for (auto &data : m_data) {
683  if (!data.size) {
684  continue;
685  }
686  m_writer.writeUInt32BE(static_cast<std::uint32_t>(data.size)); // size of data atom
687  m_writer.writeUInt32BE(Mp4AtomIds::Data); // id of data atom
688  m_writer.writeByte(0); // version
689  m_writer.writeUInt24BE(data.rawType);
690  m_writer.writeUInt16BE(data.countryIndicator);
691  m_writer.writeUInt16BE(data.languageIndicator);
692  if (data.convertedData.tellp()) {
693  // write converted data
694  stream << data.convertedData.rdbuf();
695  } else {
696  // no conversion was needed, write data directly from tag value
697  stream.write(data.rawData.data(), static_cast<std::streamoff>(data.rawData.size()));
698  }
699  }
700 }
701 
702 } // namespace TagParser
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
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.
Definition: mp4atom.h:38
std::string idToString() const
Converts the specified atom ID to a printable string.
Definition: mp4atom.h:67
The Mp4TagFieldMaker class helps making tag fields.
Definition: mp4tagfield.h:63
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.
Definition: mp4tagfield.h:112
Mp4TagFieldMaker prepareMaking(Diagnostics &diag)
Prepares making.
const std::vector< AdditionalData > & additionalData() const
Returns additional data (and the corresponding raw data type, country and language).
Definition: mp4tagfield.h:164
void reparse(Mp4Atom &ilstChild, Diagnostics &diag)
Parses field information from the specified Mp4Atom.
Definition: mp4tagfield.cpp:73
const std::string & name() const
Returns the "name" for "extended" fields.
Definition: mp4tagfield.h:189
Mp4TagField()
Constructs a new Mp4TagField.
Definition: mp4tagfield.cpp:29
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.
Definition: mp4tagfield.h:205
std::uint32_t appropriateRawDataType() const
Returns an appropriate raw data type.
std::uint16_t languageIndicator() const
Returns the language indicator.
Definition: mp4tagfield.h:237
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.
Definition: mp4tagfield.h:229
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.
Definition: mp4tagfield.h:271
The PositionInSet class describes the position of an element in a set which consists of a certain num...
Definition: positioninset.h:21
The TagField class is used by FieldMapBasedTag to store the fields.
void setTypeInfo(const TypeInfoType &typeInfo)
Sets the type info of the current TagField.
const TypeInfoType & typeInfo() const
Returns the type info of the current TagField.
const IdentifierType & id() const
Returns the id of the current TagField.
bool isTypeInfoAssigned() const
Returns an indication whether a type info is assigned.
void setId(const IdentifierType &id)
Sets the id of the current Tag Field.
TagValue & value()
Returns the value of the current TagField.
The TagValue class wraps values of different types.
Definition: tagvalue.h:95
const std::string & mimeType() const
Returns the MIME type.
Definition: tagvalue.h:627
TagTextEncoding dataEncoding() const
Returns the data encoding.
Definition: tagvalue.h:755
void clearDataAndMetadata()
Wipes assigned data including meta data.
Definition: tagvalue.h:549
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10