Tag Parser  9.1.2
C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
mediafileinfo.cpp
Go to the documentation of this file.
1 #include "./mediafileinfo.h"
2 #include "./abstracttrack.h"
3 #include "./backuphelper.h"
4 #include "./diagnostics.h"
5 #include "./exceptions.h"
6 #include "./language.h"
7 #include "./progressfeedback.h"
8 #include "./signature.h"
9 #include "./tag.h"
10 
11 #include "./id3/id3v1tag.h"
12 #include "./id3/id3v2tag.h"
13 
14 #include "./wav/waveaudiostream.h"
15 
17 
18 #include "./adts/adtsstream.h"
19 
20 #include "./ivf/ivfstream.h"
21 
22 #include "./mp4/mp4atom.h"
23 #include "./mp4/mp4container.h"
24 #include "./mp4/mp4ids.h"
25 #include "./mp4/mp4tag.h"
26 #include "./mp4/mp4track.h"
27 
28 #include "./matroska/ebmlelement.h"
30 #include "./matroska/matroskatag.h"
32 
33 #include "./ogg/oggcontainer.h"
34 
35 #include "./flac/flacmetadata.h"
36 #include "./flac/flacstream.h"
37 
38 #include <c++utilities/chrono/timespan.h>
39 #include <c++utilities/conversion/stringconversion.h>
40 
41 #include <unistd.h>
42 
43 #include <algorithm>
44 #include <cstdio>
45 #include <functional>
46 #include <iomanip>
47 #include <ios>
48 #include <memory>
49 #include <system_error>
50 
51 using namespace std;
52 using namespace std::placeholders;
53 using namespace CppUtilities;
54 
60 namespace TagParser {
61 
62 #ifdef FORCE_FULL_PARSE_DEFAULT
63 #define MEDIAINFO_CPP_FORCE_FULL_PARSE true
64 #else
65 #define MEDIAINFO_CPP_FORCE_FULL_PARSE false
66 #endif
67 
81 MediaFileInfo::MediaFileInfo()
82  : m_containerParsingStatus(ParsingStatus::NotParsedYet)
83  , m_containerFormat(ContainerFormat::Unknown)
84  , m_containerOffset(0)
85  , m_actualExistingId3v1Tag(false)
86  , m_tracksParsingStatus(ParsingStatus::NotParsedYet)
87  , m_tagsParsingStatus(ParsingStatus::NotParsedYet)
88  , m_chaptersParsingStatus(ParsingStatus::NotParsedYet)
89  , m_attachmentsParsingStatus(ParsingStatus::NotParsedYet)
90  , m_minPadding(0)
91  , m_maxPadding(0)
92  , m_preferredPadding(0)
93  , m_tagPosition(ElementPosition::BeforeData)
94  , m_indexPosition(ElementPosition::BeforeData)
95  , m_forceFullParse(MEDIAINFO_CPP_FORCE_FULL_PARSE)
96  , m_forceRewrite(true)
97  , m_forceTagPosition(true)
98  , m_forceIndexPosition(true)
99 {
100 }
101 
107 MediaFileInfo::MediaFileInfo(const string &path)
108  : BasicFileInfo(path)
109  , m_containerParsingStatus(ParsingStatus::NotParsedYet)
110  , m_containerFormat(ContainerFormat::Unknown)
111  , m_containerOffset(0)
112  , m_actualExistingId3v1Tag(false)
113  , m_tracksParsingStatus(ParsingStatus::NotParsedYet)
114  , m_tagsParsingStatus(ParsingStatus::NotParsedYet)
115  , m_chaptersParsingStatus(ParsingStatus::NotParsedYet)
116  , m_attachmentsParsingStatus(ParsingStatus::NotParsedYet)
117  , m_minPadding(0)
118  , m_maxPadding(0)
119  , m_preferredPadding(0)
120  , m_tagPosition(ElementPosition::BeforeData)
121  , m_indexPosition(ElementPosition::BeforeData)
122  , m_forceFullParse(MEDIAINFO_CPP_FORCE_FULL_PARSE)
123  , m_forceRewrite(true)
124  , m_forceTagPosition(true)
125  , m_forceIndexPosition(true)
126 {
127 }
128 
133 {
134 }
135 
152 {
153  // skip if container format already parsed
155  return;
156  }
157 
158  static const string context("parsing file header");
159  open(); // ensure the file is open
160  m_containerFormat = ContainerFormat::Unknown;
161 
162  // file size
163  m_paddingSize = 0;
164  m_containerOffset = 0;
165 
166  // read signatrue
167  char buff[16];
168  const char *const buffEnd = buff + sizeof(buff), *buffOffset;
169 startParsingSignature:
170  if (size() - containerOffset() >= 16) {
171  stream().seekg(m_containerOffset, ios_base::beg);
172  stream().read(buff, sizeof(buff));
173 
174  // skip zero bytes/padding
175  size_t bytesSkipped = 0;
176  for (buffOffset = buff; buffOffset != buffEnd && !(*buffOffset); ++buffOffset, ++bytesSkipped)
177  ;
178  if (bytesSkipped >= 4) {
179  m_containerOffset += bytesSkipped;
180 
181  // give up after 0x100 bytes
182  if ((m_paddingSize += bytesSkipped) >= 0x100u) {
183  m_containerParsingStatus = ParsingStatus::NotSupported;
184  m_containerFormat = ContainerFormat::Unknown;
185  return;
186  }
187 
188  // try again
189  goto startParsingSignature;
190  }
191  if (m_paddingSize) {
192  diag.emplace_back(DiagLevel::Warning, argsToString(m_paddingSize, " zero-bytes skipped at the beginning of the file."), context);
193  }
194 
195  // parse signature
196  switch ((m_containerFormat = parseSignature(buff, sizeof(buff)))) {
198  // save position of ID3v2 tag
199  m_actualId3v2TagOffsets.push_back(m_containerOffset);
200  if (m_actualId3v2TagOffsets.size() == 2) {
201  diag.emplace_back(DiagLevel::Warning, "There is more than just one ID3v2 header at the beginning of the file.", context);
202  }
203 
204  // read ID3v2 header
205  stream().seekg(m_containerOffset + 5, ios_base::beg);
206  stream().read(buff, 5);
207 
208  // set the container offset to skip ID3v2 header
209  m_containerOffset += toNormalInt(BE::toUInt32(buff + 1)) + 10;
210  if ((*buff) & 0x10) {
211  // footer present
212  m_containerOffset += 10;
213  }
214 
215  // continue reading signature
216  goto startParsingSignature;
217 
220  // MP4/QuickTime is handled using Mp4Container instance
221  m_container = make_unique<Mp4Container>(*this, m_containerOffset);
222  try {
223  static_cast<Mp4Container *>(m_container.get())->validateElementStructure(diag, &m_paddingSize);
224  } catch (const Failure &) {
225  m_containerParsingStatus = ParsingStatus::CriticalFailure;
226  }
227  break;
228  }
229  case ContainerFormat::Ebml: {
230  // EBML/Matroska is handled using MatroskaContainer instance
231  auto container = make_unique<MatroskaContainer>(*this, m_containerOffset);
232  try {
233  container->parseHeader(diag);
234  if (container->documentType() == "matroska") {
235  m_containerFormat = ContainerFormat::Matroska;
236  } else if (container->documentType() == "webm") {
237  m_containerFormat = ContainerFormat::Webm;
238  }
239  if (m_forceFullParse) {
240  // validating the element structure of Matroska files takes too long when
241  // parsing big files so do this only when explicitely desired
242  container->validateElementStructure(diag, &m_paddingSize);
243  container->validateIndex(diag);
244  }
245  } catch (const Failure &) {
246  m_containerParsingStatus = ParsingStatus::CriticalFailure;
247  }
248  m_container = move(container);
249  break;
250  }
252  // Ogg is handled by OggContainer instance
253  m_container = make_unique<OggContainer>(*this, m_containerOffset);
254  static_cast<OggContainer *>(m_container.get())->setChecksumValidationEnabled(m_forceFullParse);
255  break;
257  // container format is still unknown -> check for magic numbers at odd offsets
258  // -> check for tar (magic number at offset 0x101)
259  if (size() > 0x107) {
260  stream().seekg(0x101);
261  stream().read(buff, 6);
262  if (buff[0] == 0x75 && buff[1] == 0x73 && buff[2] == 0x74 && buff[3] == 0x61 && buff[4] == 0x72 && buff[5] == 0x00) {
263  m_containerFormat = ContainerFormat::Tar;
264  break;
265  }
266  }
267  break;
268  default:;
269  }
270  }
271 
272  // set parsing status
273  if (m_containerParsingStatus == ParsingStatus::NotParsedYet) {
274  if (m_containerFormat == ContainerFormat::Unknown) {
275  m_containerParsingStatus = ParsingStatus::NotSupported;
276  } else {
277  m_containerParsingStatus = ParsingStatus::Ok;
278  }
279  }
280 }
281 
296 {
297  // skip if tracks already parsed
299  return;
300  }
301  static const string context("parsing tracks");
302 
303  try {
304  // parse tracks via container object
305  if (m_container) {
306  m_container->parseTracks(diag);
307  m_tracksParsingStatus = ParsingStatus::Ok;
308  return;
309  }
310 
311  // parse tracks via track object for "single-track"-formats
312  switch (m_containerFormat) {
314  m_singleTrack = make_unique<AdtsStream>(stream(), m_containerOffset);
315  break;
317  m_singleTrack = make_unique<FlacStream>(*this, m_containerOffset);
318  break;
320  m_singleTrack = make_unique<IvfStream>(stream(), m_containerOffset);
321  break;
323  m_singleTrack = make_unique<MpegAudioFrameStream>(stream(), m_containerOffset);
324  break;
326  m_singleTrack = make_unique<WaveAudioStream>(stream(), m_containerOffset);
327  break;
328  default:
329  throw NotImplementedException();
330  }
331  m_singleTrack->parseHeader(diag);
332 
333  // take padding for some "single-track" formats into account
334  switch (m_containerFormat) {
336  m_paddingSize += static_cast<FlacStream *>(m_singleTrack.get())->paddingSize();
337  break;
338  default:;
339  }
340 
341  m_tracksParsingStatus = ParsingStatus::Ok;
342 
343  } catch (const NotImplementedException &) {
344  diag.emplace_back(DiagLevel::Information, "Parsing tracks is not implemented for the container format of the file.", context);
345  m_tracksParsingStatus = ParsingStatus::NotSupported;
346  } catch (const Failure &) {
347  diag.emplace_back(DiagLevel::Critical, "Unable to parse tracks.", context);
348  m_tracksParsingStatus = ParsingStatus::CriticalFailure;
349  }
350 }
351 
367 {
368  // skip if tags already parsed
370  return;
371  }
372  static const string context("parsing tag");
373 
374  // check for ID3v1 tag
375  if (size() >= 128) {
376  m_id3v1Tag = make_unique<Id3v1Tag>();
377  try {
378  stream().seekg(-128, ios_base::end);
379  m_id3v1Tag->parse(stream(), diag);
380  m_actualExistingId3v1Tag = true;
381  } catch (const NoDataFoundException &) {
382  m_id3v1Tag.reset();
383  } catch (const Failure &) {
384  m_tagsParsingStatus = ParsingStatus::CriticalFailure;
385  diag.emplace_back(DiagLevel::Critical, "Unable to parse ID3v1 tag.", context);
386  }
387  }
388 
389  // check for ID3v2 tags: the offsets of the ID3v2 tags have already been parsed when parsing the container format
390  m_id3v2Tags.clear();
391  for (const auto offset : m_actualId3v2TagOffsets) {
392  auto id3v2Tag = make_unique<Id3v2Tag>();
393  stream().seekg(offset, ios_base::beg);
394  try {
395  id3v2Tag->parse(stream(), size() - static_cast<std::uint64_t>(offset), diag);
396  m_paddingSize += id3v2Tag->paddingSize();
397  } catch (const NoDataFoundException &) {
398  continue;
399  } catch (const Failure &) {
400  m_tagsParsingStatus = ParsingStatus::CriticalFailure;
401  diag.emplace_back(DiagLevel::Critical, "Unable to parse ID3v2 tag.", context);
402  }
403  m_id3v2Tags.emplace_back(id3v2Tag.release());
404  }
405 
406  // check for tags in tracks (FLAC only) or via container object
407  try {
408  if (m_containerFormat == ContainerFormat::Flac) {
409  parseTracks(diag);
410  if (m_tagsParsingStatus == ParsingStatus::NotParsedYet) {
411  m_tagsParsingStatus = m_tracksParsingStatus;
412  }
413  return;
414  } else if (m_container) {
415  m_container->parseTags(diag);
416  } else {
417  throw NotImplementedException();
418  }
419 
420  // set status, but do not override error/unsupported status form ID3 tags here
421  if (m_tagsParsingStatus == ParsingStatus::NotParsedYet) {
422  m_tagsParsingStatus = ParsingStatus::Ok;
423  }
424 
425  } catch (const NotImplementedException &) {
426  // set status to not supported, but do not override parsing status from ID3 tags here
427  if (m_tagsParsingStatus == ParsingStatus::NotParsedYet) {
428  m_tagsParsingStatus = ParsingStatus::NotSupported;
429  }
430  diag.emplace_back(DiagLevel::Information, "Parsing tags is not implemented for the container format of the file.", context);
431  } catch (const Failure &) {
432  m_tagsParsingStatus = ParsingStatus::CriticalFailure;
433  diag.emplace_back(DiagLevel::Critical, "Unable to parse tag.", context);
434  }
435 }
436 
449 {
450  // skip if chapters already parsed
452  return;
453  }
454  static const string context("parsing chapters");
455 
456  try {
457  // parse chapters via container object
458  if (!m_container) {
459  throw NotImplementedException();
460  }
461  m_container->parseChapters(diag);
462  m_chaptersParsingStatus = ParsingStatus::Ok;
463  } catch (const NotImplementedException &) {
464  m_chaptersParsingStatus = ParsingStatus::NotSupported;
465  diag.emplace_back(DiagLevel::Information, "Parsing chapters is not implemented for the container format of the file.", context);
466  } catch (const Failure &) {
467  m_chaptersParsingStatus = ParsingStatus::CriticalFailure;
468  diag.emplace_back(DiagLevel::Critical, "Unable to parse chapters.", context);
469  }
470 }
471 
484 {
485  // skip if attachments already parsed
487  return;
488  }
489  static const string context("parsing attachments");
490 
491  try {
492  // parse attachments via container object
493  if (!m_container) {
494  throw NotImplementedException();
495  }
496  m_container->parseAttachments(diag);
497  m_attachmentsParsingStatus = ParsingStatus::Ok;
498  } catch (const NotImplementedException &) {
499  m_attachmentsParsingStatus = ParsingStatus::NotSupported;
500  diag.emplace_back(DiagLevel::Information, "Parsing attachments is not implemented for the container format of the file.", context);
501  } catch (const Failure &) {
502  m_attachmentsParsingStatus = ParsingStatus::CriticalFailure;
503  diag.emplace_back(DiagLevel::Critical, "Unable to parse attachments.", context);
504  }
505 }
506 
514 {
515  parseContainerFormat(diag);
516  parseTracks(diag);
517  parseTags(diag);
518  parseChapters(diag);
519  parseAttachments(diag);
520 }
521 
534 {
535  // check if tags have been parsed yet (tags must have been parsed yet to create appropriate tags)
537  return false;
538  }
539 
540  // check if tags need to be created/adjusted/removed
541  const auto requiredTargets(settings.requiredTargets);
542  const auto flags(settings.flags);
543  const auto targetsRequired = !requiredTargets.empty() && (requiredTargets.size() != 1 || !requiredTargets.front().isEmpty());
544  auto targetsSupported = false;
545  if (areTagsSupported() && m_container) {
546  // container object takes care of tag management
547  if (targetsRequired) {
548  // check whether container supports targets
549  if (m_container->tagCount()) {
550  // all tags in the container should support targets if the first one supports targets
551  targetsSupported = m_container->tag(0)->supportsTarget();
552  } else {
553  // try to create a new tag and check whether targets are supported
554  auto *const tag = m_container->createTag();
555  if (tag && (targetsSupported = tag->supportsTarget())) {
556  tag->setTarget(requiredTargets.front());
557  }
558  }
559  if (targetsSupported) {
560  for (const auto &target : requiredTargets) {
561  m_container->createTag(target);
562  }
563  }
564  } else {
565  // no targets are required -> just ensure that at least one tag is present
566  m_container->createTag();
567  }
568  return true;
569  }
570 
571  // no container object present
572  switch (m_containerFormat) {
574  static_cast<FlacStream *>(m_singleTrack.get())->createVorbisComment();
575  break;
576  default:
577  // create ID3 tag(s)
579  switch (containerFormat()) {
583  break;
584  default:
585  return false;
586  }
587  }
588  // create ID3 tags according to id3v2usage and id3v2usage
589  // always create ID3v1 tag -> ensure there is one
590  if (settings.id3v1usage == TagUsage::Always && !id3v1Tag()) {
591  auto *const id3v1Tag = createId3v1Tag();
592  if (flags & TagCreationFlags::Id3InitOnCreate) {
593  for (const auto &id3v2Tag : id3v2Tags()) {
594  // overwrite existing values to ensure default ID3v1 genre "Blues" is updated as well
595  id3v1Tag->insertValues(*id3v2Tag, true);
596  // ID3v1 does not support all text encodings which might be used in ID3v2
598  }
599  }
600  }
601  if (settings.id3v2usage == TagUsage::Always && !hasId3v2Tag()) {
602  // always create ID3v2 tag -> ensure there is one and set version
603  auto *const id3v2Tag = createId3v2Tag();
604  id3v2Tag->setVersion(settings.id3v2MajorVersion, 0);
605  if ((flags & TagCreationFlags::Id3InitOnCreate) && id3v1Tag()) {
606  id3v2Tag->insertValues(*id3v1Tag(), true);
607  }
608  }
609  }
610 
612  mergeId3v2Tags();
613  }
614  // remove ID3 tags according to settings
615  if (settings.id3v1usage == TagUsage::Never && hasId3v1Tag()) {
616  // transfer tags to ID3v2 tag before removing
618  id3v2Tags().front()->insertValues(*id3v1Tag(), false);
619  }
620  removeId3v1Tag();
621  }
622  if (settings.id3v2usage == TagUsage::Never) {
624  // transfer tags to ID3v1 tag before removing
625  for (const auto &tag : id3v2Tags()) {
626  id3v1Tag()->insertValues(*tag, false);
627  }
628  }
630  } else if (!(flags & TagCreationFlags::KeepExistingId3v2Version)) {
631  // set version of ID3v2 tag according user preferences
632  for (const auto &tag : id3v2Tags()) {
633  tag->setVersion(settings.id3v2MajorVersion, 0);
634  }
635  }
636  return true;
637 }
638 
660 {
661  static const string context("making file");
662  diag.emplace_back(DiagLevel::Information, "Changes are about to be applied.", context);
663  bool previousParsingSuccessful = true;
664  switch (tagsParsingStatus()) {
665  case ParsingStatus::Ok:
667  break;
668  default:
669  previousParsingSuccessful = false;
670  diag.emplace_back(DiagLevel::Critical, "Tags have to be parsed without critical errors before changes can be applied.", context);
671  }
672  switch (tracksParsingStatus()) {
673  case ParsingStatus::Ok:
675  break;
676  default:
677  previousParsingSuccessful = false;
678  diag.emplace_back(DiagLevel::Critical, "Tracks have to be parsed without critical errors before changes can be applied.", context);
679  }
680  if (!previousParsingSuccessful) {
681  throw InvalidDataException();
682  }
683  if (m_container) { // container object takes care
684  // ID3 tags can not be applied in this case -> add warnings if ID3 tags have been assigned
685  if (hasId3v1Tag()) {
686  diag.emplace_back(DiagLevel::Warning, "Assigned ID3v1 tag can't be attached and will be ignored.", context);
687  }
688  if (hasId3v2Tag()) {
689  diag.emplace_back(DiagLevel::Warning, "Assigned ID3v2 tag can't be attached and will be ignored.", context);
690  }
691  m_tracksParsingStatus = ParsingStatus::NotParsedYet;
692  m_tagsParsingStatus = ParsingStatus::NotParsedYet;
693  try {
694  m_container->makeFile(diag, progress);
695  } catch (...) {
696  // since the file might be messed up, invalidate the parsing results
698  throw;
699  }
700  } else { // implementation if no container object is present
701  // assume the file is a MP3 file
702  try {
703  makeMp3File(diag, progress);
704  } catch (...) {
705  // since the file might be messed up, invalidate the parsing results
707  throw;
708  }
709  }
711 }
712 
726 {
727  MediaType mediaType = MediaType::Unknown;
728  unsigned int version = 0;
729  switch (m_containerFormat) {
730  case ContainerFormat::Ogg: {
731  // check for video track or whether only Opus or Speex tracks are present
732  const auto &tracks = static_cast<OggContainer *>(m_container.get())->tracks();
733  if (tracks.empty()) {
734  break;
735  }
736  bool onlyOpus = true, onlySpeex = true;
737  for (const auto &track : static_cast<OggContainer *>(m_container.get())->tracks()) {
738  if (track->mediaType() == MediaType::Video) {
739  mediaType = MediaType::Video;
740  }
741  if (track->format().general != GeneralMediaFormat::Opus) {
742  onlyOpus = false;
743  }
744  if (track->format().general != GeneralMediaFormat::Speex) {
745  onlySpeex = false;
746  }
747  }
748  if (onlyOpus) {
749  version = static_cast<unsigned int>(GeneralMediaFormat::Opus);
750  } else if (onlySpeex) {
751  version = static_cast<unsigned int>(GeneralMediaFormat::Speex);
752  }
753  break;
754  }
758  break;
760  if (m_singleTrack) {
761  version = m_singleTrack->format().sub;
762  }
763  break;
764  default:;
765  }
766  return TagParser::containerFormatAbbreviation(m_containerFormat, mediaType, version);
767 }
768 
779 const char *MediaFileInfo::mimeType() const
780 {
781  MediaType mediaType;
782  switch (m_containerFormat) {
787  break;
788  default:
789  mediaType = MediaType::Unknown;
790  }
791  return TagParser::containerMimeType(m_containerFormat, mediaType);
792 }
793 
806 vector<AbstractTrack *> MediaFileInfo::tracks() const
807 {
808  vector<AbstractTrack *> res;
809  size_t trackCount = 0;
810  size_t containerTrackCount = 0;
811  if (m_singleTrack) {
812  trackCount = 1;
813  }
814  if (m_container) {
815  trackCount += (containerTrackCount = m_container->trackCount());
816  }
817  res.reserve(trackCount);
818 
819  if (m_singleTrack) {
820  res.push_back(m_singleTrack.get());
821  }
822  for (size_t i = 0; i != containerTrackCount; ++i) {
823  res.push_back(m_container->track(i));
824  }
825  return res;
826 }
827 
837 {
839  return false;
840  }
841  if (m_singleTrack && m_singleTrack->mediaType() == type) {
842  return true;
843  } else if (m_container) {
844  for (size_t i = 0, count = m_container->trackCount(); i != count; ++i) {
845  if (m_container->track(i)->mediaType() == type) {
846  return true;
847  }
848  }
849  }
850  return false;
851 }
852 
862 CppUtilities::TimeSpan MediaFileInfo::duration() const
863 {
864  if (m_container) {
865  return m_container->duration();
866  } else if (m_singleTrack) {
867  return m_singleTrack->duration();
868  }
869  return TimeSpan();
870 }
871 
882 {
883  const auto duration = this->duration();
884  if (duration.isNull()) {
885  return 0.0;
886  }
887  return 0.0078125 * size() / duration.totalSeconds();
888 }
889 
900 unordered_set<string> MediaFileInfo::availableLanguages(MediaType type) const
901 {
902  unordered_set<string> res;
903  if (m_container) {
904  for (size_t i = 0, count = m_container->trackCount(); i != count; ++i) {
905  const AbstractTrack *track = m_container->track(i);
906  if ((type == MediaType::Unknown || track->mediaType() == type) && isLanguageDefined(track->language())) {
907  res.emplace(track->language());
908  }
909  }
910  } else if (m_singleTrack && (type == MediaType::Unknown || m_singleTrack->mediaType() == type) && isLanguageDefined(m_singleTrack->language())) {
911  res.emplace(m_singleTrack->language());
912  }
913  return res;
914 }
915 
928 {
929  if (m_container) {
930  const size_t trackCount = m_container->trackCount();
931  vector<string> parts;
932  parts.reserve(trackCount);
933  for (size_t i = 0; i != trackCount; ++i) {
934  const string description(m_container->track(i)->description());
935  if (!description.empty()) {
936  parts.emplace_back(move(description));
937  }
938  }
939  return joinStrings(parts, " / ");
940  } else if (m_singleTrack) {
941  return m_singleTrack->description();
942  }
943  return string();
944 }
945 
956 {
958  return false;
959  }
960  if (m_id3v1Tag) {
961  m_id3v1Tag.reset();
962  return true;
963  }
964  return false;
965 }
966 
983 {
985  return nullptr;
986  }
987  if (!m_id3v1Tag) {
988  m_id3v1Tag = make_unique<Id3v1Tag>();
989  }
990  return m_id3v1Tag.get();
991 }
992 
1004 {
1006  return false;
1007  }
1008  for (auto i = m_id3v2Tags.begin(), end = m_id3v2Tags.end(); i != end; ++i) {
1009  if (i->get() == tag) {
1010  m_id3v2Tags.erase(i);
1011  return true;
1012  }
1013  }
1014  return false;
1015 }
1016 
1026 {
1027  if (tagsParsingStatus() == ParsingStatus::NotParsedYet || m_id3v2Tags.empty()) {
1028  return false;
1029  }
1030  m_id3v2Tags.clear();
1031  return true;
1032 }
1033 
1050 {
1051  if (m_id3v2Tags.empty()) {
1052  m_id3v2Tags.emplace_back(make_unique<Id3v2Tag>());
1053  }
1054  return m_id3v2Tags.front().get();
1055 }
1056 
1071 {
1072  if (!tag) {
1073  return false;
1074  }
1075 
1076  // remove tag via container
1077  if (m_container) {
1078  return m_container->removeTag(tag);
1079  }
1080 
1081  // remove tag via track for "single-track" formats
1082  if (m_singleTrack && m_containerFormat == ContainerFormat::Flac) {
1083  auto *const flacStream(static_cast<FlacStream *>(m_singleTrack.get()));
1084  if (flacStream->vorbisComment() == tag) {
1085  return flacStream->removeVorbisComment();
1086  }
1087  }
1088 
1089  // remove ID3 tags
1090  if (m_id3v1Tag.get() == tag) {
1091  m_id3v1Tag.reset();
1092  return true;
1093  }
1094  for (auto i = m_id3v2Tags.begin(), end = m_id3v2Tags.end(); i != end; ++i) {
1095  if (i->get() == tag) {
1096  m_id3v2Tags.erase(i);
1097  return true;
1098  }
1099  }
1100  return false;
1101 }
1102 
1110 {
1111  if (m_container) {
1112  m_container->removeAllTags();
1113  }
1114  if (m_singleTrack && m_containerFormat == ContainerFormat::Flac) {
1115  static_cast<FlacStream *>(m_singleTrack.get())->removeVorbisComment();
1116  }
1117  m_id3v1Tag.reset();
1118  m_id3v2Tags.clear();
1119 }
1120 
1125 {
1126  if (m_container && m_container->chapterCount()) {
1127  return true;
1128  }
1129  switch (m_containerFormat) {
1131  case ContainerFormat::Webm:
1132  return true;
1133  default:
1134  return false;
1135  }
1136 }
1137 
1142 {
1143  if (m_container && m_container->attachmentCount()) {
1144  return true;
1145  }
1146  switch (m_containerFormat) {
1148  case ContainerFormat::Webm:
1149  return true;
1150  default:
1151  return false;
1152  }
1153 }
1154 
1159 {
1160  if (trackCount()) {
1161  return true;
1162  }
1163  switch (m_containerFormat) {
1164  case ContainerFormat::Mp4:
1167  case ContainerFormat::Ogg:
1169  case ContainerFormat::Webm:
1170  return true;
1171  default:
1172  return false;
1173  }
1174 }
1175 
1180 {
1181  switch (m_containerFormat) {
1182  case ContainerFormat::Adts:
1183  case ContainerFormat::Flac:
1186  case ContainerFormat::Mp4:
1187  case ContainerFormat::Ogg:
1189  case ContainerFormat::Webm:
1190  // these container formats are supported
1191  return true;
1192  default:
1193  // the container format is unsupported
1194  // -> an ID3 tag might be already present, in this case the tags are considered supported
1195  return !m_container && (hasId3v1Tag() || hasId3v2Tag());
1196  }
1197 }
1198 
1205 {
1206  // simply return the first tag here since MP4 files never contain multiple tags
1207  return (m_containerFormat == ContainerFormat::Mp4 || m_containerFormat == ContainerFormat::QuickTime) && m_container
1208  && m_container->tagCount() > 0
1209  ? static_cast<Mp4Container *>(m_container.get())->tags().front().get()
1210  : nullptr;
1211 }
1212 
1219 const vector<unique_ptr<MatroskaTag>> &MediaFileInfo::matroskaTags() const
1220 {
1221  // matroska files might contain multiple tags (targeting different scopes)
1222  if (m_containerFormat == ContainerFormat::Matroska && m_container) {
1223  return static_cast<MatroskaContainer *>(m_container.get())->tags();
1224  } else {
1225  static const std::vector<std::unique_ptr<MatroskaTag>> empty;
1226  return empty;
1227  }
1228 }
1229 
1236 {
1237  return m_containerFormat == ContainerFormat::Ogg && m_container && m_container->tagCount()
1238  ? static_cast<OggContainer *>(m_container.get())->tags().front().get()
1239  : (m_containerFormat == ContainerFormat::Flac && m_singleTrack ? static_cast<FlacStream *>(m_singleTrack.get())->vorbisComment() : nullptr);
1240 }
1241 
1247 vector<AbstractChapter *> MediaFileInfo::chapters() const
1248 {
1249  vector<AbstractChapter *> res;
1250  if (m_container) {
1251  const size_t count = m_container->chapterCount();
1252  res.reserve(count);
1253  for (size_t i = 0; i != count; ++i) {
1254  res.push_back(m_container->chapter(i));
1255  }
1256  }
1257  return res;
1258 }
1259 
1265 vector<AbstractAttachment *> MediaFileInfo::attachments() const
1266 {
1267  vector<AbstractAttachment *> res;
1268  if (m_container) {
1269  const size_t count = m_container->attachmentCount();
1270  res.reserve(count);
1271  for (size_t i = 0; i != count; ++i) {
1272  res.push_back(m_container->attachment(i));
1273  }
1274  }
1275  return res;
1276 }
1277 
1290 {
1291  m_containerParsingStatus = ParsingStatus::NotParsedYet;
1292  m_containerFormat = ContainerFormat::Unknown;
1293  m_containerOffset = 0;
1294  m_paddingSize = 0;
1295  m_tracksParsingStatus = ParsingStatus::NotParsedYet;
1296  m_tagsParsingStatus = ParsingStatus::NotParsedYet;
1297  m_chaptersParsingStatus = ParsingStatus::NotParsedYet;
1298  m_attachmentsParsingStatus = ParsingStatus::NotParsedYet;
1299  m_id3v1Tag.reset();
1300  m_id3v2Tags.clear();
1301  m_actualId3v2TagOffsets.clear();
1302  m_actualExistingId3v1Tag = false;
1303  m_container.reset();
1304  m_singleTrack.reset();
1305 }
1306 
1324 {
1325  auto begin = m_id3v2Tags.begin(), end = m_id3v2Tags.end();
1326  if (begin == end) {
1327  return;
1328  }
1329  Id3v2Tag &first = **begin;
1330  auto isecond = begin + 1;
1331  if (isecond == end) {
1332  return;
1333  }
1334  for (auto i = isecond; i != end; ++i) {
1335  first.insertFields(**i, false);
1336  }
1337  m_id3v2Tags.erase(isecond, end - 1);
1338 }
1339 
1351 {
1353  return false;
1354  }
1357 }
1358 
1370 {
1372  return false;
1373  }
1376 }
1377 
1393 {
1394  switch (m_containerFormat) {
1395  case ContainerFormat::Ogg:
1396  if (m_container) {
1397  return static_cast<OggContainer *>(m_container.get())->createTag(TagTarget());
1398  }
1399  break;
1400  case ContainerFormat::Flac:
1401  if (m_singleTrack) {
1402  return static_cast<FlacStream *>(m_singleTrack.get())->createVorbisComment();
1403  }
1404  break;
1405  default:;
1406  }
1407  return nullptr;
1408 }
1409 
1420 {
1421  switch (m_containerFormat) {
1422  case ContainerFormat::Ogg:
1423  if (m_container) {
1424  bool hadTags = static_cast<OggContainer *>(m_container.get())->tagCount();
1425  static_cast<OggContainer *>(m_container.get())->removeAllTags();
1426  return hadTags;
1427  }
1428  break;
1429  case ContainerFormat::Flac:
1430  if (m_singleTrack) {
1431  return static_cast<FlacStream *>(m_singleTrack.get())->removeVorbisComment();
1432  }
1433  break;
1434  default:;
1435  }
1436  return false;
1437 }
1438 
1447 void MediaFileInfo::tags(vector<Tag *> &tags) const
1448 {
1449  if (hasId3v1Tag()) {
1450  tags.push_back(m_id3v1Tag.get());
1451  }
1452  for (const unique_ptr<Id3v2Tag> &tag : m_id3v2Tags) {
1453  tags.push_back(tag.get());
1454  }
1455  if (m_containerFormat == ContainerFormat::Flac && m_singleTrack) {
1456  if (auto *const vorbisComment = static_cast<const FlacStream *>(m_singleTrack.get())->vorbisComment()) {
1457  tags.push_back(vorbisComment);
1458  }
1459  }
1460  if (m_container) {
1461  for (size_t i = 0, count = m_container->tagCount(); i < count; ++i) {
1462  tags.push_back(m_container->tag(i));
1463  }
1464  }
1465 }
1466 
1471 {
1472  return hasId3v1Tag() || hasId3v2Tag() || (m_container && m_container->tagCount())
1473  || (m_containerFormat == ContainerFormat::Flac && static_cast<FlacStream *>(m_singleTrack.get())->vorbisComment());
1474 }
1475 
1482 vector<Tag *> MediaFileInfo::tags() const
1483 {
1484  vector<Tag *> res;
1485  tags(res);
1486  return res;
1487 }
1488 
1493 {
1496 }
1497 
1501 void MediaFileInfo::makeMp3File(Diagnostics &diag, AbortableProgressFeedback &progress)
1502 {
1503  static const string context("making MP3/FLAC file");
1504 
1505  // don't rewrite the complete file if there are no ID3v2/FLAC tags present or to be written
1506  if (!isForcingRewrite() && m_id3v2Tags.empty() && m_actualId3v2TagOffsets.empty() && m_saveFilePath.empty()
1507  && m_containerFormat != ContainerFormat::Flac) {
1508  // alter ID3v1 tag
1509  if (!m_id3v1Tag) {
1510  // remove ID3v1 tag
1511  if (!m_actualExistingId3v1Tag) {
1512  diag.emplace_back(DiagLevel::Information, "Nothing to be changed.", context);
1513  return;
1514  }
1515  progress.updateStep("Removing ID3v1 tag ...");
1516  stream().close();
1517  if (truncate(BasicFileInfo::pathForOpen(path()), static_cast<std::streamoff>(size() - 128)) == 0) {
1518  reportSizeChanged(size() - 128);
1519  } else {
1520  diag.emplace_back(DiagLevel::Critical, "Unable to truncate file to remove ID3v1 tag.", context);
1521  throw std::ios_base::failure("Unable to truncate file to remove ID3v1 tag.");
1522  }
1523  return;
1524  } else {
1525  // add or update ID3v1 tag
1526  if (m_actualExistingId3v1Tag) {
1527  progress.updateStep("Updating existing ID3v1 tag ...");
1528  // ensure the file is still open / not readonly
1529  open();
1530  stream().seekp(-128, ios_base::end);
1531  try {
1532  m_id3v1Tag->make(stream(), diag);
1533  } catch (const Failure &) {
1534  diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
1535  }
1536  } else {
1537  progress.updateStep("Adding new ID3v1 tag ...");
1538  // ensure the file is still open / not readonly
1539  open();
1540  stream().seekp(0, ios_base::end);
1541  try {
1542  m_id3v1Tag->make(stream(), diag);
1543  } catch (const Failure &) {
1544  diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
1545  }
1546  }
1547  // prevent deferring final write operations (to catch and handle possible errors here)
1548  stream().flush();
1549  }
1550  return;
1551  }
1552 
1553  // ID3v2 needs to be modified
1554  FlacStream *const flacStream = (m_containerFormat == ContainerFormat::Flac ? static_cast<FlacStream *>(m_singleTrack.get()) : nullptr);
1555  progress.updateStep(flacStream ? "Updating FLAC tags ..." : "Updating ID3v2 tags ...");
1556 
1557  // prepare ID3v2 tags
1558  vector<Id3v2TagMaker> makers;
1559  makers.reserve(m_id3v2Tags.size());
1560  std::uint32_t tagsSize = 0;
1561  for (auto &tag : m_id3v2Tags) {
1562  try {
1563  makers.emplace_back(tag->prepareMaking(diag));
1564  tagsSize += makers.back().requiredSize();
1565  } catch (const Failure &) {
1566  }
1567  }
1568 
1569  // determine stream offset and make track/format specific metadata
1570  std::uint32_t streamOffset; // where the actual stream starts
1571  stringstream flacMetaData(ios_base::in | ios_base::out | ios_base::binary);
1572  flacMetaData.exceptions(ios_base::badbit | ios_base::failbit);
1573  std::streamoff startOfLastMetaDataBlock;
1574  if (flacStream) {
1575  // if it is a raw FLAC stream, make FLAC metadata
1576  startOfLastMetaDataBlock = flacStream->makeHeader(flacMetaData, diag);
1577  tagsSize += flacMetaData.tellp();
1578  streamOffset = flacStream->streamOffset();
1579  } else {
1580  // make no further metadata, just use the container offset as stream offset
1581  streamOffset = static_cast<std::uint32_t>(m_containerOffset);
1582  }
1583 
1584  // check whether rewrite is required
1585  bool rewriteRequired = isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
1586  size_t padding = 0;
1587  if (!rewriteRequired) {
1588  // rewriting is not forced and new tag is not too big for available space
1589  // -> calculate new padding
1590  padding = streamOffset - tagsSize;
1591  // -> check whether the new padding matches specifications
1592  if (padding < minPadding() || padding > maxPadding()) {
1593  rewriteRequired = true;
1594  }
1595  }
1596  if (makers.empty() && !flacStream) {
1597  // an ID3v2 tag is not written and it is not a FLAC stream
1598  // -> can't include padding
1599  if (padding) {
1600  // but padding would be present -> need to rewrite
1601  padding = 0; // can't write the preferred padding despite rewriting
1602  rewriteRequired = true;
1603  }
1604  } else if (rewriteRequired) {
1605  // rewriting is forced or new ID3v2 tag is too big for available space
1606  // -> use preferred padding when rewriting anyways
1607  padding = preferredPadding();
1608  } else if (makers.empty() && flacStream && padding && padding < 4) {
1609  // no ID3v2 tag -> must include padding in FLAC stream
1610  // but padding of 1, 2, and 3 byte isn't possible -> need to rewrite
1611  padding = preferredPadding();
1612  rewriteRequired = true;
1613  }
1614  if (rewriteRequired && flacStream && makers.empty() && padding) {
1615  // the first 4 byte of FLAC padding actually don't count because these
1616  // can not be used for additional meta data
1617  padding += 4;
1618  }
1619  progress.updateStep(rewriteRequired ? "Preparing streams for rewriting ..." : "Preparing streams for updating ...");
1620 
1621  // setup stream(s) for writing
1622  // -> define variables needed to handle output stream and backup stream (required when rewriting the file)
1623  string backupPath;
1624  NativeFileStream &outputStream = stream();
1625  NativeFileStream backupStream; // create a stream to open the backup/original file for the case rewriting the file is required
1626 
1627  if (rewriteRequired) {
1628  if (m_saveFilePath.empty()) {
1629  // move current file to temp dir and reopen it as backupStream, recreate original file
1630  try {
1631  BackupHelper::createBackupFile(backupDirectory(), path(), backupPath, outputStream, backupStream);
1632  // recreate original file, define buffer variables
1633  outputStream.open(BasicFileInfo::pathForOpen(path()), ios_base::out | ios_base::binary | ios_base::trunc);
1634  } catch (const std::ios_base::failure &failure) {
1635  diag.emplace_back(
1636  DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
1637  throw;
1638  }
1639  } else {
1640  // open the current file as backupStream and create a new outputStream at the specified "save file path"
1641  try {
1642  close();
1643  backupStream.exceptions(ios_base::badbit | ios_base::failbit);
1644  backupStream.open(BasicFileInfo::pathForOpen(path()), ios_base::in | ios_base::binary);
1645  outputStream.open(BasicFileInfo::pathForOpen(m_saveFilePath), ios_base::out | ios_base::binary | ios_base::trunc);
1646  } catch (const std::ios_base::failure &failure) {
1647  diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context);
1648  throw;
1649  }
1650  }
1651 
1652  } else { // !rewriteRequired
1653  // reopen original file to ensure it is opened for writing
1654  try {
1655  close();
1656  outputStream.open(BasicFileInfo::pathForOpen(path()), ios_base::in | ios_base::out | ios_base::binary);
1657  } catch (const std::ios_base::failure &failure) {
1658  diag.emplace_back(DiagLevel::Critical, argsToString("Opening the file with write permissions failed: ", failure.what()), context);
1659  throw;
1660  }
1661  }
1662  // TODO: fix code duplication
1663 
1664  // start actual writing
1665  try {
1666  // ensure we can cast padding safely to uint32
1667  if (padding > numeric_limits<std::uint32_t>::max()) {
1668  padding = numeric_limits<std::uint32_t>::max();
1669  diag.emplace_back(
1670  DiagLevel::Critical, argsToString("Preferred padding is not supported. Setting preferred padding to ", padding, '.'), context);
1671  }
1672 
1673  if (!makers.empty()) {
1674  // write ID3v2 tags
1675  progress.updateStep("Writing ID3v2 tag ...");
1676  for (auto i = makers.begin(), end = makers.end() - 1; i != end; ++i) {
1677  i->make(outputStream, 0, diag);
1678  }
1679  // include padding into the last ID3v2 tag
1680  makers.back().make(outputStream, (flacStream && padding && padding < 4) ? 0 : static_cast<std::uint32_t>(padding), diag);
1681  }
1682 
1683  if (flacStream) {
1684  if (padding && startOfLastMetaDataBlock) {
1685  // if appending padding, ensure the last flag of the last "METADATA_BLOCK_HEADER" is not set
1686  flacMetaData.seekg(startOfLastMetaDataBlock);
1687  flacMetaData.seekp(startOfLastMetaDataBlock);
1688  flacMetaData.put(static_cast<std::uint8_t>(flacMetaData.peek()) & (0x80u - 1));
1689  flacMetaData.seekg(0);
1690  }
1691 
1692  // write FLAC metadata
1693  outputStream << flacMetaData.rdbuf();
1694 
1695  // write padding
1696  if (padding) {
1697  flacStream->makePadding(outputStream, static_cast<std::uint32_t>(padding), true, diag);
1698  }
1699  }
1700 
1701  if (makers.empty() && !flacStream) {
1702  // just write padding (however, padding should be set to 0 in this case?)
1703  for (; padding; --padding) {
1704  outputStream.put(0);
1705  }
1706  }
1707 
1708  // copy / skip actual stream data
1709  // -> determine media data size
1710  std::uint64_t mediaDataSize = size() - streamOffset;
1711  if (m_actualExistingId3v1Tag) {
1712  mediaDataSize -= 128;
1713  }
1714 
1715  if (rewriteRequired) {
1716  // copy data from original file
1717  switch (m_containerFormat) {
1719  progress.updateStep("Writing MPEG audio frames ...");
1720  break;
1721  default:
1722  progress.updateStep("Writing frames ...");
1723  }
1724  backupStream.seekg(static_cast<streamoff>(streamOffset));
1725  CopyHelper<0x4000> copyHelper;
1726  copyHelper.callbackCopy(backupStream, stream(), mediaDataSize, bind(&AbortableProgressFeedback::isAborted, ref(progress)),
1727  bind(&AbortableProgressFeedback::updateStepPercentage, ref(progress), _1));
1728  } else {
1729  // just skip actual stream data
1730  outputStream.seekp(static_cast<std::streamoff>(mediaDataSize), ios_base::cur);
1731  }
1732 
1733  // write ID3v1 tag
1734  if (m_id3v1Tag) {
1735  progress.updateStep("Writing ID3v1 tag ...");
1736  try {
1737  m_id3v1Tag->make(stream(), diag);
1738  } catch (const Failure &) {
1739  diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
1740  }
1741  }
1742 
1743  // handle streams
1744  if (rewriteRequired) {
1745  // report new size
1746  reportSizeChanged(static_cast<std::uint64_t>(outputStream.tellp()));
1747  // "save as path" is now the regular path
1748  if (!saveFilePath().empty()) {
1750  m_saveFilePath.clear();
1751  }
1752  // prevent deferring final write operations (to catch and handle possible errors here); stream is useless for further
1753  // usage anyways because it is write-only
1754  outputStream.close();
1755  } else {
1756  const auto newSize = static_cast<std::uint64_t>(outputStream.tellp());
1757  if (newSize < size()) {
1758  // file is smaller after the modification -> truncate
1759  // -> prevent deferring final write operations
1760  outputStream.close();
1761  // -> truncate file
1762  if (truncate(BasicFileInfo::pathForOpen(path()), static_cast<streamoff>(newSize)) == 0) {
1763  reportSizeChanged(newSize);
1764  } else {
1765  diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context);
1766  }
1767  } else {
1768  // file is longer after the modification
1769  // -> prevent deferring final write operations (to catch and handle possible errors here)
1770  outputStream.flush();
1771  // -> report new size
1772  reportSizeChanged(newSize);
1773  }
1774  }
1775 
1776  } catch (...) {
1777  BackupHelper::handleFailureAfterFileModified(*this, backupPath, outputStream, backupStream, diag, context);
1778  }
1779 }
1780 
1781 } // namespace TagParser
TagParser::ContainerFormat::Webm
@ Webm
TagParser::Mp4Container
Implementation of GenericContainer<MediaFileInfo, Mp4Tag, Mp4Track, Mp4Atom>.
Definition: mp4container.h:18
TagParser::ParsingStatus::NotSupported
@ NotSupported
mp4ids.h
TagParser::MediaType::Audio
@ Audio
TagParser::MediaFileInfo::mergeId3v2Tags
void mergeId3v2Tags()
Merges the assigned ID3v2 tags into a single ID3v2 tag.
Definition: mediafileinfo.cpp:1323
TagParser::TagDataType::TimeSpan
@ TimeSpan
TagParser::TagCreationFlags::Id3TransferValuesOnRemoval
@ Id3TransferValuesOnRemoval
mp4atom.h
exceptions.h
TagParser::TagCreationFlags::KeepExistingId3v2Version
@ KeepExistingId3v2Version
TagParser::MediaFileInfo::MediaFileInfo
MediaFileInfo()
Constructs a new MediaFileInfo.
Definition: mediafileinfo.cpp:81
TagParser::TagCreationSettings
The TagSettings struct contains settings which can be passed to MediaFileInfo::createAppropriateTags(...
Definition: settings.h:50
TagParser::MediaFileInfo::removeTag
bool removeTag(Tag *tag)
Removes a possibly assigned tag from the current file.
Definition: mediafileinfo.cpp:1070
TagParser::TagCreationFlags::Id3InitOnCreate
@ Id3InitOnCreate
TagParser::BasicFileInfo::invalidated
virtual void invalidated()
This function is called when the BasicFileInfo gets invalidated.
Definition: basicfileinfo.cpp:218
TagParser::AbortableProgressFeedback
The AbortableProgressFeedback class provides feedback about an ongoing operation via callbacks....
Definition: progressfeedback.h:186
TagParser::MediaFileInfo::mp4Tag
Mp4Tag * mp4Tag() const
Returns a pointer to the assigned MP4 tag or nullptr if none is assigned.
Definition: mediafileinfo.cpp:1204
TagParser::parseSignature
TAG_PARSER_EXPORT ContainerFormat parseSignature(const char *buffer, int bufferSize)
Parses the signature read from the specified buffer.
Definition: signature.cpp:104
TagParser::Id3v1Tag::ensureTextValuesAreProperlyEncoded
void ensureTextValuesAreProperlyEncoded() override
Ensures the encoding of all assigned text values is supported by the tag by converting the character ...
Definition: id3v1tag.cpp:262
TagParser::MediaFileInfo::parseTags
void parseTags(Diagnostics &diag)
Parses the tag(s) of the current file.
Definition: mediafileinfo.cpp:366
adtsstream.h
TagParser::MediaFileInfo::tracks
std::vector< AbstractTrack * > tracks() const
Returns the tracks for the current file.
Definition: mediafileinfo.cpp:806
TagParser::MediaFileInfo::parseContainerFormat
void parseContainerFormat(Diagnostics &diag)
Parses the container format of the current file.
Definition: mediafileinfo.cpp:151
waveaudiostream.h
TagParser::MediaFileInfo::matroskaTags
const std::vector< std::unique_ptr< MatroskaTag > > & matroskaTags() const
Returns pointers to the assigned Matroska tags.
Definition: mediafileinfo.cpp:1219
TagParser::MediaFileInfo::saveFilePath
const std::string & saveFilePath() const
Returns the "save file path" which has been set using setSaveFilePath().
Definition: mediafileinfo.h:370
TagParser::DiagLevel::Information
@ Information
TagParser::MediaFileInfo::removeId3v2Tag
bool removeId3v2Tag(Id3v2Tag *tag)
Removes an assigned ID3v2 tag from the current file.
Definition: mediafileinfo.cpp:1003
TagParser::MediaFileInfo::invalidated
void invalidated() override
Reimplemented from BasicFileInfo::invalidated().
Definition: mediafileinfo.cpp:1492
backuphelper.h
mp4track.h
TagParser::MediaFileInfo::removeId3v1Tag
bool removeId3v1Tag()
Removes a possibly assigned ID3v1 tag from the current file.
Definition: mediafileinfo.cpp:955
TagParser::AbortableProgressFeedback::isAborted
bool isAborted() const
Returns whether the operation has been aborted via tryToAbort().
Definition: progressfeedback.h:226
TagParser::FourccIds::WavPack
@ WavPack
Definition: mp4ids.h:390
TagParser::Ogg
@ Ogg
Definition: signature.cpp:54
TagParser::MediaFileInfo::attachments
std::vector< AbstractAttachment * > attachments() const
Returns all attachments assigned to the current file.
Definition: mediafileinfo.cpp:1265
TagParser::MediaFileInfo::id3v1ToId3v2
bool id3v1ToId3v2()
Converts an existing ID3v1 tag into an ID3v2 tag.
Definition: mediafileinfo.cpp:1350
TagParser::DiagLevel::Warning
@ Warning
TagParser::ParsingStatus::Ok
@ Ok
TagParser::ContainerFormat::Matroska
@ Matroska
TagParser::OggContainer
Implementation of TagParser::AbstractContainer for OGG files.
Definition: oggcontainer.h:129
TagParser::BasicFileInfo::pathForOpen
static const char * pathForOpen(const std::string &url)
Returns removes the "file:/" prefix from url to be able to pass it to functions like open(),...
Definition: basicfileinfo.h:140
TagParser::MediaFileInfo::parseTracks
void parseTracks(Diagnostics &diag)
Parses the tracks of the current file.
Definition: mediafileinfo.cpp:295
progressfeedback.h
TagParser::MediaFileInfo::parseChapters
void parseChapters(Diagnostics &diag)
Parses the chapters of the current file.
Definition: mediafileinfo.cpp:448
TagParser::Ebml
@ Ebml
Definition: signature.cpp:50
TagParser::MediaFileInfo::duration
CppUtilities::TimeSpan duration() const
Returns the overall duration of the file if known; otherwise returns a TimeSpan with zero ticks.
Definition: mediafileinfo.cpp:862
TagParser::MediaFileInfo::availableLanguages
std::unordered_set< std::string > availableLanguages(TagParser::MediaType type=TagParser::MediaType::Audio) const
Determines the available languages for specified media type (by default MediaType::Audio).
Definition: mediafileinfo.cpp:900
TagParser::Tag
The Tag class is used to store, read and write tag information.
Definition: tag.h:98
TagParser::VorbisCommentIds::version
constexpr const TAG_PARSER_EXPORT char * version()
Definition: vorbiscommentids.h:33
TagParser::MediaFileInfo::preferredPadding
size_t preferredPadding() const
Returns the padding to be written before the data block when applying changes and the file needs to b...
Definition: mediafileinfo.h:537
TagParser::GeneralMediaFormat::Speex
@ Speex
TagParser::MediaFileInfo::id3v1Tag
Id3v1Tag * id3v1Tag() const
Returns a pointer to the assigned ID3v1 tag or nullptr if none is assigned.
Definition: mediafileinfo.h:330
TagParser::Diagnostics
The Diagnostics class is a container for DiagMessage.
Definition: diagnostics.h:156
TagParser::TagUsage::Never
@ Never
TagParser::MediaFileInfo::createId3v2Tag
Id3v2Tag * createId3v2Tag()
Creates an ID3v2 tag for the current file.
Definition: mediafileinfo.cpp:1049
TagParser::MediaFileInfo::chaptersParsingStatus
ParsingStatus chaptersParsingStatus() const
Returns whether the chapters have been parsed yet.
Definition: mediafileinfo.h:294
TagParser::MediaFileInfo::chapters
std::vector< AbstractChapter * > chapters() const
Returns all chapters assigned to the current file.
Definition: mediafileinfo.cpp:1247
TagParser
Contains all classes and functions of the TagInfo library.
Definition: aaccodebook.h:10
TagParser::MediaFileInfo::areTagsSupported
bool areTagsSupported() const
Returns an indication whether this library supports the tag format of the current file.
Definition: mediafileinfo.cpp:1179
TagParser::TagCreationSettings::requiredTargets
std::vector< TagTarget > requiredTargets
Specifies the required targets. If targets are not supported by the container an informal notificatio...
Definition: settings.h:52
matroskacontainer.h
TagParser::FlacStream::vorbisComment
VorbisComment * vorbisComment() const
Returns the Vorbis comment if one is present in the stream.
Definition: flacstream.h:51
TagParser::FourccIds::Flac
@ Flac
Definition: mp4ids.h:272
TagParser::BasicFileInfo::open
void open(bool readOnly=false)
Opens a std::fstream for the current file.
Definition: basicfileinfo.cpp:46
TagParser::BackupHelper::handleFailureAfterFileModified
TAG_PARSER_EXPORT void handleFailureAfterFileModified(MediaFileInfo &mediaFileInfo, const std::string &backupPath, CppUtilities::NativeFileStream &outputStream, CppUtilities::NativeFileStream &backupStream, Diagnostics &diag, const std::string &context="making file")
TagParser::isLanguageDefined
bool isLanguageDefined(const std::string &languageSpecification)
Returns whether languageSpecification is not empty or undefined.
Definition: language.h:16
TagParser::MediaFileInfo::removeVorbisComment
bool removeVorbisComment()
Removes all assigned Vorbis comment from the current file.
Definition: mediafileinfo.cpp:1419
TagParser::MediaFileInfo::parseAttachments
void parseAttachments(Diagnostics &diag)
Parses the attachments of the current file.
Definition: mediafileinfo.cpp:483
matroskatag.h
TagParser::TagCreationFlags::MergeMultipleSuccessiveId3v2Tags
@ MergeMultipleSuccessiveId3v2Tags
TagParser::containerFormatAbbreviation
const TAG_PARSER_EXPORT char * containerFormatAbbreviation(ContainerFormat containerFormat, MediaType mediaType=MediaType::Unknown, unsigned int version=0)
Returns the abbreviation of the container format as C-style string considering the specified media ty...
Definition: signature.cpp:251
TagParser::MediaFileInfo::minPadding
size_t minPadding() const
Returns the minimum padding to be written before the data blocks when applying changes.
Definition: mediafileinfo.h:488
TagParser::GeneralMediaFormat::Opus
@ Opus
TagParser::ContainerFormat::Tar
@ Tar
TagParser::AbstractContainer::documentType
const std::string & documentType() const
Returns a string that describes the document type if available; otherwise returns an empty string.
Definition: abstractcontainer.h:227
id3v1tag.h
ivfstream.h
TagParser::MediaFileInfo::createAppropriateTags
bool createAppropriateTags(const TagCreationSettings &settings=TagCreationSettings())
Ensures appropriate tags are created according the given settings.
Definition: mediafileinfo.cpp:533
TagParser::Failure
The class inherits from std::exception and serves as base class for exceptions thrown by the elements...
Definition: exceptions.h:11
matroskatrack.h
signature.h
TagParser::BackupHelper::createBackupFile
TAG_PARSER_EXPORT void createBackupFile(const std::string &backupDir, const std::string &originalPath, std::string &backupPath, CppUtilities::NativeFileStream &originalStream, CppUtilities::NativeFileStream &backupStream)
TagParser::GenericContainer::tracks
const std::vector< std::unique_ptr< TrackType > > & tracks() const
Returns the tracks of the file.
Definition: genericcontainer.h:237
TagParser::MediaFileInfo::removeAllId3v2Tags
bool removeAllId3v2Tags()
Removes all assigned ID3v2 tags from the current file.
Definition: mediafileinfo.cpp:1025
TagParser::MediaType
MediaType
The MediaType enum specifies the type of media data (audio, video, text, ...).
Definition: mediaformat.h:13
TagParser::QuickTime
@ QuickTime
Definition: signature.cpp:56
TagParser::AbstractTrack::mediaType
MediaType mediaType() const
Returns the media type if known; otherwise returns MediaType::Other.
Definition: abstracttrack.h:296
TagParser::BasicFileInfo::reportPathChanged
void reportPathChanged(const std::string &newPath)
Call this function to report that the path changed.
Definition: basicfileinfo.h:129
TagParser::DiagLevel::Critical
@ Critical
language.h
TagParser::MediaFileInfo::container
AbstractContainer * container() const
Returns the container for the current file.
Definition: mediafileinfo.h:432
TagParser::FlacStream
Implementation of TagParser::AbstractTrack for raw FLAC streams.
Definition: flacstream.h:14
TagParser::BasicFileInfo
The BasicFileInfo class provides basic file information such as file name, extension,...
Definition: basicfileinfo.h:14
TagParser::ElementPosition::BeforeData
@ BeforeData
TagParser::MediaFileInfo::createVorbisComment
VorbisComment * createVorbisComment()
Creates a Vorbis comment for the current file.
Definition: mediafileinfo.cpp:1392
CppUtilities
Definition: abstractcontainer.h:15
diagnostics.h
TagParser::MediaFileInfo::containerParsingStatus
ParsingStatus containerParsingStatus() const
Returns an indication whether the container format has been parsed yet.
Definition: mediafileinfo.h:200
TagParser::BasicProgressFeedback::updateStep
void updateStep(const std::string &step, std::uint8_t stepPercentage=0)
Updates the current step and invokes the first callback specified on construction.
Definition: progressfeedback.h:96
TagParser::Tag::insertValues
virtual unsigned int insertValues(const Tag &from, bool overwrite)
Inserts all compatible values from another Tag.
Definition: tag.cpp:86
TagParser::MediaFileInfo::hasId3v2Tag
bool hasId3v2Tag() const
Returns an indication whether an ID3v2 tag is assigned.
Definition: mediafileinfo.h:318
TagParser::Ivf
@ Ivf
Definition: signature.cpp:48
TagParser::ParsingStatus::CriticalFailure
@ CriticalFailure
TagParser::MediaFileInfo::tagsParsingStatus
ParsingStatus tagsParsingStatus() const
Returns an indication whether tag information has been parsed yet.
Definition: mediafileinfo.h:265
TagParser::MediaFileInfo::hasId3v1Tag
bool hasId3v1Tag() const
Returns an indication whether an ID3v1 tag is assigned.
Definition: mediafileinfo.h:310
TagParser::MediaFileInfo::overallAverageBitrate
double overallAverageBitrate() const
Returns the overall average bitrate in kbit/s of the file if known; otherwise returns 0....
Definition: mediafileinfo.cpp:881
TagParser::AbstractTrack
The AbstractTrack class parses and stores technical information about video, audio and other kinds of...
Definition: abstracttrack.h:39
flacstream.h
TagParser::MediaFileInfo::clearParsingResults
void clearParsingResults()
Clears all parsing results and assigned/created/changed information such as detected container format...
Definition: mediafileinfo.cpp:1289
TagParser::MediaFileInfo::maxPadding
size_t maxPadding() const
Returns the maximum padding to be written before the data blocks when applying changes.
Definition: mediafileinfo.h:516
TagParser::ParsingStatus
ParsingStatus
The ParsingStatus enum specifies whether a certain part of the file (tracks, tags,...
Definition: mediafileinfo.h:38
TagParser::Id3v2Tag
Implementation of TagParser::Tag for ID3v2 tags.
Definition: id3v2tag.h:61
TagParser::MediaFileInfo::tags
std::vector< Tag * > tags() const
Returns all tags assigned to the current file.
Definition: mediafileinfo.cpp:1482
TagParser::ContainerFormat
ContainerFormat
Specifies the container format.
Definition: signature.h:17
TagParser::containerMimeType
const TAG_PARSER_EXPORT char * containerMimeType(ContainerFormat containerFormat, MediaType mediaType=MediaType::Unknown)
Returns the MIME-type of the container format as C-style string.
Definition: signature.cpp:499
TagParser::TagCreationSettings::id3v1usage
TagUsage id3v1usage
Specifies the usage of ID3v1 when creating tags for MP3 files (has no effect when the file is no MP3 ...
Definition: settings.h:57
id3v2tag.h
TagParser::Id3v1Tag
Implementation of TagParser::Tag for ID3v1 tags.
Definition: id3v1tag.h:10
CppUtilities::CopyHelper
Definition: oggcontainer.h:16
TagParser::MediaFileInfo::parseEverything
void parseEverything(Diagnostics &diag)
Parses the container format, the tracks and the tag information of the current file.
Definition: mediafileinfo.cpp:513
TagParser::MediaFileInfo::id3v2Tags
const std::vector< std::unique_ptr< Id3v2Tag > > & id3v2Tags() const
Returns pointers to the assigned ID3v2 tags.
Definition: mediafileinfo.h:342
TagParser::ContainerFormat::Id2v2Tag
@ Id2v2Tag
TagParser::Adts
@ Adts
Definition: signature.cpp:86
TagParser::MpegAudioFrames
@ MpegAudioFrames
Definition: signature.cpp:91
TagParser::MediaFileInfo::removeAllTags
void removeAllTags()
Removes all assigned tags from the file.
Definition: mediafileinfo.cpp:1109
TagParser::MediaFileInfo::technicalSummary
std::string technicalSummary() const
Generates a short technical summary about the file's tracks.
Definition: mediafileinfo.cpp:927
TagParser::BasicFileInfo::stream
CppUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
Definition: basicfileinfo.h:81
TagParser::ParsingStatus::NotParsedYet
@ NotParsedYet
TagParser::BasicProgressFeedback< AbortableProgressFeedback >::updateStepPercentage
void updateStepPercentage(std::uint8_t stepPercentage)
Updates the current step percentage and invokes the second callback specified on construction (or the...
Definition: progressfeedback.h:124
TagParser::MediaFileInfo::id3v2ToId3v1
bool id3v2ToId3v1()
Converts the existing ID3v2 tags into an ID3v1 tag.
Definition: mediafileinfo.cpp:1369
TagParser::NoDataFoundException
The exception that is thrown when the data to be parsed holds no parsable information (e....
Definition: exceptions.h:18
TagParser::TrackType::FlacStream
@ FlacStream
TagParser::InvalidDataException
The exception that is thrown when the data to be parsed or to be made seems invalid and therefore can...
Definition: exceptions.h:25
TagParser::BasicFileInfo::reportSizeChanged
void reportSizeChanged(std::uint64_t newSize)
Call this function to report that the size changed.
Definition: basicfileinfo.h:120
TagParser::TagTarget
The TagTarget class specifies the target of a tag.
Definition: tagtarget.h:20
TagParser::ElementPosition
ElementPosition
Definition: settings.h:13
TagParser::Mp4Tag
Implementation of TagParser::Tag for the MP4 container.
Definition: mp4tag.h:97
abstracttrack.h
TagParser::MediaFileInfo::mimeType
const char * mimeType() const
Returns the MIME-type of the container format as C-style string.
Definition: mediafileinfo.cpp:779
TagParser::FieldMapBasedTag::insertFields
int insertFields(const FieldMapBasedTag< ImplementationType > &from, bool overwrite)
Inserts all fields from another tag of the same field type and compare function.
Definition: fieldbasedtag.h:366
TagParser::MediaFileInfo::~MediaFileInfo
~MediaFileInfo() override
Destroys the MediaFileInfo.
Definition: mediafileinfo.cpp:132
TagParser::AbstractTrack::language
const std::string & language() const
Returns the language of the track if known; otherwise returns an empty string.
Definition: abstracttrack.h:413
TagParser::MediaType::Unknown
@ Unknown
TagParser::MediaFileInfo::containerOffset
std::uint64_t containerOffset() const
Returns the actual container start offset.
Definition: mediafileinfo.h:249
TagParser::BasicFileInfo::size
std::uint64_t size() const
Returns size of the current file in bytes.
Definition: basicfileinfo.h:111
TagParser::MediaFileInfo::areChaptersSupported
bool areChaptersSupported() const
Returns an indication whether this library supports parsing the chapters of the current file.
Definition: mediafileinfo.cpp:1124
TagParser::MediaFileInfo::attachmentsParsingStatus
ParsingStatus attachmentsParsingStatus() const
Returns whether the attachments have been parsed yet.
Definition: mediafileinfo.h:302
TagParser::RiffWave
@ RiffWave
Definition: signature.cpp:58
flacmetadata.h
tag.h
ebmlelement.h
TagParser::MediaFileInfo::createId3v1Tag
Id3v1Tag * createId3v1Tag()
Creates an ID3v1 tag for the current file.
Definition: mediafileinfo.cpp:982
TagParser::TagCreationSettings::id3v2usage
TagUsage id3v2usage
Specifies the usage of ID3v2 when creating tags for MP3 files (has no effect when the file is no MP3 ...
Definition: settings.h:59
TagParser::MediaFileInfo::paddingSize
std::uint64_t paddingSize() const
Returns the padding size.
Definition: mediafileinfo.h:257
TagParser::MediaFileInfo::tracksParsingStatus
ParsingStatus tracksParsingStatus() const
Returns an indication whether tracks have been parsed yet.
Definition: mediafileinfo.h:273
TagParser::MediaFileInfo::areAttachmentsSupported
bool areAttachmentsSupported() const
Returns an indication whether this library supports attachment format of the current file.
Definition: mediafileinfo.cpp:1141
TagParser::MatroskaContainer
Implementation of GenericContainer<MediaFileInfo, MatroskaTag, MatroskaTrack, EbmlElement>.
Definition: matroskacontainer.h:24
TagParser::BasicFileInfo::path
const std::string & path() const
Returns the path of the current file.
Definition: basicfileinfo.h:99
TagParser::ContainerFormat::Unknown
@ Unknown
TagParser::MediaFileInfo::containerFormatAbbreviation
const char * containerFormatAbbreviation() const
Returns the abbreviation of the container format as C-style string.
Definition: mediafileinfo.cpp:725
TagParser::TagCreationSettings::flags
TagCreationFlags flags
Specifies options to control the tag creation. See TagSettings::Flags.
Definition: settings.h:54
TagParser::BasicFileInfo::close
void close()
A possibly opened std::fstream will be closed.
Definition: basicfileinfo.cpp:71
mp4container.h
TagParser::MediaType::Video
@ Video
MEDIAINFO_CPP_FORCE_FULL_PARSE
#define MEDIAINFO_CPP_FORCE_FULL_PARSE
Definition: mediafileinfo.cpp:65
TagParser::MediaFileInfo::applyChanges
void applyChanges(Diagnostics &diag, AbortableProgressFeedback &progress)
Applies assigned/changed tag information to the current file.
Definition: mediafileinfo.cpp:659
TagParser::VorbisComment
Implementation of TagParser::Tag for Vorbis comments.
Definition: vorbiscomment.h:25
TagParser::MediaFileInfo::areTracksSupported
bool areTracksSupported() const
Returns an indication whether this library supports parsing the tracks information of the current fil...
Definition: mediafileinfo.cpp:1158
TagParser::MediaFileInfo::hasTracksOfType
bool hasTracksOfType(TagParser::MediaType type) const
Returns an indication whether the current file has tracks of the specified type.
Definition: mediafileinfo.cpp:836
TagParser::TagCreationSettings::id3v2MajorVersion
std::uint8_t id3v2MajorVersion
Specifies the ID3v2 version to be used in case an ID3v2 tag present or will be created....
Definition: settings.h:61
TagParser::MediaFileInfo::backupDirectory
const std::string & backupDirectory() const
Returns the directory used to store backup files.
Definition: mediafileinfo.h:352
mediafileinfo.h
TagParser::MediaFileInfo::containerFormat
ContainerFormat containerFormat() const
Returns the container format of the current file.
Definition: mediafileinfo.h:211
oggcontainer.h
TagParser::TagUsage::Always
@ Always
TagParser::MediaFileInfo::isForcingRewrite
bool isForcingRewrite() const
Returns whether forcing rewriting (when applying changes) is enabled.
Definition: mediafileinfo.h:463
TagParser::MediaFileInfo::vorbisComment
VorbisComment * vorbisComment() const
Returns a pointer to the first assigned Vorbis comment or nullptr if none is assigned.
Definition: mediafileinfo.cpp:1235
TagParser::MediaFileInfo::trackCount
std::size_t trackCount() const
Returns the number of tracks that could be parsed.
Definition: mediafileinfo.h:286
mpegaudioframestream.h
TagParser::MatroskaElementLevel::Unknown
@ Unknown
TagParser::NotImplementedException
This exception is thrown when the an operation is invoked that has not been implemented yet.
Definition: exceptions.h:60
TagParser::MatroskaTagIds::description
constexpr const TAG_PARSER_EXPORT char * description()
Definition: matroskatagid.h:234
TagParser::TagCreationFlags::TreatUnknownFilesAsMp3Files
@ TreatUnknownFilesAsMp3Files
TagParser::Mp4
@ Mp4
Definition: signature.cpp:53
TagParser::AbstractContainer::parseHeader
void parseHeader(Diagnostics &diag)
Parses the header if not parsed yet.
Definition: abstractcontainer.cpp:55
TagParser::MediaFileInfo::hasAnyTag
bool hasAnyTag() const
Returns an indication whether a tag of any format is assigned.
Definition: mediafileinfo.cpp:1470
mp4tag.h