Tag Parser  9.1.0
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 
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  }
1548  return;
1549  }
1550 
1551  // ID3v2 needs to be modified
1552  FlacStream *const flacStream = (m_containerFormat == ContainerFormat::Flac ? static_cast<FlacStream *>(m_singleTrack.get()) : nullptr);
1553  progress.updateStep(flacStream ? "Updating FLAC tags ..." : "Updating ID3v2 tags ...");
1554 
1555  // prepare ID3v2 tags
1556  vector<Id3v2TagMaker> makers;
1557  makers.reserve(m_id3v2Tags.size());
1558  std::uint32_t tagsSize = 0;
1559  for (auto &tag : m_id3v2Tags) {
1560  try {
1561  makers.emplace_back(tag->prepareMaking(diag));
1562  tagsSize += makers.back().requiredSize();
1563  } catch (const Failure &) {
1564  }
1565  }
1566 
1567  // determine stream offset and make track/format specific metadata
1568  std::uint32_t streamOffset; // where the actual stream starts
1569  stringstream flacMetaData(ios_base::in | ios_base::out | ios_base::binary);
1570  flacMetaData.exceptions(ios_base::badbit | ios_base::failbit);
1571  std::streamoff startOfLastMetaDataBlock;
1572  if (flacStream) {
1573  // if it is a raw FLAC stream, make FLAC metadata
1574  startOfLastMetaDataBlock = flacStream->makeHeader(flacMetaData, diag);
1575  tagsSize += flacMetaData.tellp();
1576  streamOffset = flacStream->streamOffset();
1577  } else {
1578  // make no further metadata, just use the container offset as stream offset
1579  streamOffset = static_cast<std::uint32_t>(m_containerOffset);
1580  }
1581 
1582  // check whether rewrite is required
1583  bool rewriteRequired = isForcingRewrite() || !m_saveFilePath.empty() || (tagsSize > streamOffset);
1584  size_t padding = 0;
1585  if (!rewriteRequired) {
1586  // rewriting is not forced and new tag is not too big for available space
1587  // -> calculate new padding
1588  padding = streamOffset - tagsSize;
1589  // -> check whether the new padding matches specifications
1590  if (padding < minPadding() || padding > maxPadding()) {
1591  rewriteRequired = true;
1592  }
1593  }
1594  if (makers.empty() && !flacStream) {
1595  // an ID3v2 tag is not written and it is not a FLAC stream
1596  // -> can't include padding
1597  if (padding) {
1598  // but padding would be present -> need to rewrite
1599  padding = 0; // can't write the preferred padding despite rewriting
1600  rewriteRequired = true;
1601  }
1602  } else if (rewriteRequired) {
1603  // rewriting is forced or new ID3v2 tag is too big for available space
1604  // -> use preferred padding when rewriting anyways
1605  padding = preferredPadding();
1606  } else if (makers.empty() && flacStream && padding && padding < 4) {
1607  // no ID3v2 tag -> must include padding in FLAC stream
1608  // but padding of 1, 2, and 3 byte isn't possible -> need to rewrite
1609  padding = preferredPadding();
1610  rewriteRequired = true;
1611  }
1612  if (rewriteRequired && flacStream && makers.empty() && padding) {
1613  // the first 4 byte of FLAC padding actually don't count because these
1614  // can not be used for additional meta data
1615  padding += 4;
1616  }
1617  progress.updateStep(rewriteRequired ? "Preparing streams for rewriting ..." : "Preparing streams for updating ...");
1618 
1619  // setup stream(s) for writing
1620  // -> define variables needed to handle output stream and backup stream (required when rewriting the file)
1621  string backupPath;
1622  NativeFileStream &outputStream = stream();
1623  NativeFileStream backupStream; // create a stream to open the backup/original file for the case rewriting the file is required
1624 
1625  if (rewriteRequired) {
1626  if (m_saveFilePath.empty()) {
1627  // move current file to temp dir and reopen it as backupStream, recreate original file
1628  try {
1629  BackupHelper::createBackupFile(backupDirectory(), path(), backupPath, outputStream, backupStream);
1630  // recreate original file, define buffer variables
1631  outputStream.open(BasicFileInfo::pathForOpen(path()), ios_base::out | ios_base::binary | ios_base::trunc);
1632  } catch (const std::ios_base::failure &failure) {
1633  diag.emplace_back(
1634  DiagLevel::Critical, argsToString("Creation of temporary file (to rewrite the original file) failed: ", failure.what()), context);
1635  throw;
1636  }
1637  } else {
1638  // open the current file as backupStream and create a new outputStream at the specified "save file path"
1639  try {
1640  close();
1641  backupStream.exceptions(ios_base::badbit | ios_base::failbit);
1642  backupStream.open(BasicFileInfo::pathForOpen(path()), ios_base::in | ios_base::binary);
1643  outputStream.open(BasicFileInfo::pathForOpen(m_saveFilePath), ios_base::out | ios_base::binary | ios_base::trunc);
1644  } catch (const std::ios_base::failure &failure) {
1645  diag.emplace_back(DiagLevel::Critical, argsToString("Opening streams to write output file failed: ", failure.what()), context);
1646  throw;
1647  }
1648  }
1649 
1650  } else { // !rewriteRequired
1651  // reopen original file to ensure it is opened for writing
1652  try {
1653  close();
1654  outputStream.open(BasicFileInfo::pathForOpen(path()), ios_base::in | ios_base::out | ios_base::binary);
1655  } catch (const std::ios_base::failure &failure) {
1656  diag.emplace_back(DiagLevel::Critical, argsToString("Opening the file with write permissions failed: ", failure.what()), context);
1657  throw;
1658  }
1659  }
1660  // TODO: fix code duplication
1661 
1662  // start actual writing
1663  try {
1664  // ensure we can cast padding safely to uint32
1665  if (padding > numeric_limits<std::uint32_t>::max()) {
1666  padding = numeric_limits<std::uint32_t>::max();
1667  diag.emplace_back(
1668  DiagLevel::Critical, argsToString("Preferred padding is not supported. Setting preferred padding to ", padding, '.'), context);
1669  }
1670 
1671  if (!makers.empty()) {
1672  // write ID3v2 tags
1673  progress.updateStep("Writing ID3v2 tag ...");
1674  for (auto i = makers.begin(), end = makers.end() - 1; i != end; ++i) {
1675  i->make(outputStream, 0, diag);
1676  }
1677  // include padding into the last ID3v2 tag
1678  makers.back().make(outputStream, (flacStream && padding && padding < 4) ? 0 : static_cast<std::uint32_t>(padding), diag);
1679  }
1680 
1681  if (flacStream) {
1682  if (padding && startOfLastMetaDataBlock) {
1683  // if appending padding, ensure the last flag of the last "METADATA_BLOCK_HEADER" is not set
1684  flacMetaData.seekg(startOfLastMetaDataBlock);
1685  flacMetaData.seekp(startOfLastMetaDataBlock);
1686  flacMetaData.put(static_cast<std::uint8_t>(flacMetaData.peek()) & (0x80u - 1));
1687  flacMetaData.seekg(0);
1688  }
1689 
1690  // write FLAC metadata
1691  outputStream << flacMetaData.rdbuf();
1692 
1693  // write padding
1694  if (padding) {
1695  flacStream->makePadding(outputStream, static_cast<std::uint32_t>(padding), true, diag);
1696  }
1697  }
1698 
1699  if (makers.empty() && !flacStream) {
1700  // just write padding (however, padding should be set to 0 in this case?)
1701  for (; padding; --padding) {
1702  outputStream.put(0);
1703  }
1704  }
1705 
1706  // copy / skip actual stream data
1707  // -> determine media data size
1708  std::uint64_t mediaDataSize = size() - streamOffset;
1709  if (m_actualExistingId3v1Tag) {
1710  mediaDataSize -= 128;
1711  }
1712 
1713  if (rewriteRequired) {
1714  // copy data from original file
1715  switch (m_containerFormat) {
1717  progress.updateStep("Writing MPEG audio frames ...");
1718  break;
1719  default:
1720  progress.updateStep("Writing frames ...");
1721  }
1722  backupStream.seekg(static_cast<streamoff>(streamOffset));
1723  CopyHelper<0x4000> copyHelper;
1724  copyHelper.callbackCopy(backupStream, stream(), mediaDataSize, bind(&AbortableProgressFeedback::isAborted, ref(progress)),
1725  bind(&AbortableProgressFeedback::updateStepPercentage, ref(progress), _1));
1726  } else {
1727  // just skip actual stream data
1728  outputStream.seekp(static_cast<std::streamoff>(mediaDataSize), ios_base::cur);
1729  }
1730 
1731  // write ID3v1 tag
1732  if (m_id3v1Tag) {
1733  progress.updateStep("Writing ID3v1 tag ...");
1734  try {
1735  m_id3v1Tag->make(stream(), diag);
1736  } catch (const Failure &) {
1737  diag.emplace_back(DiagLevel::Warning, "Unable to write ID3v1 tag.", context);
1738  }
1739  }
1740 
1741  // handle streams
1742  if (rewriteRequired) {
1743  // report new size
1744  reportSizeChanged(static_cast<std::uint64_t>(outputStream.tellp()));
1745  // "save as path" is now the regular path
1746  if (!saveFilePath().empty()) {
1748  m_saveFilePath.clear();
1749  }
1750  // stream is useless for further usage because it is write-only
1751  outputStream.close();
1752  } else {
1753  const auto newSize = static_cast<std::uint64_t>(outputStream.tellp());
1754  if (newSize < size()) {
1755  // file is smaller after the modification -> truncate
1756  // -> close stream before truncating
1757  outputStream.close();
1758  // -> truncate file
1759  if (truncate(BasicFileInfo::pathForOpen(path()), static_cast<streamoff>(newSize)) == 0) {
1760  reportSizeChanged(newSize);
1761  } else {
1762  diag.emplace_back(DiagLevel::Critical, "Unable to truncate the file.", context);
1763  }
1764  } else {
1765  // file is longer after the modification -> just report new size
1766  reportSizeChanged(newSize);
1767  }
1768  }
1769 
1770  } catch (...) {
1771  BackupHelper::handleFailureAfterFileModified(*this, backupPath, outputStream, backupStream, diag, context);
1772  }
1773 }
1774 
1775 } // namespace TagParser
TagParser::ContainerFormat::Webm
TagParser::ParsingStatus::NotSupported
mp4ids.h
TagParser::MediaType::Audio
TagParser::MediaFileInfo::mergeId3v2Tags
void mergeId3v2Tags()
Merges the assigned ID3v2 tags into a single ID3v2 tag.
Definition: mediafileinfo.cpp:1323
TagParser::TagDataType::TimeSpan
TagParser::TagCreationFlags::Id3TransferValuesOnRemoval
mp4atom.h
exceptions.h
TagParser::TagCreationFlags::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
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
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
Definition: mp4ids.h:390
TagParser::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
TagParser::ParsingStatus::Ok
TagParser::ContainerFormat::Matroska
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
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
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
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::FourccIds::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
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
TagParser::ContainerFormat::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::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
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
language.h
TagParser::MediaFileInfo::container
AbstractContainer * container() const
Returns the container for the current file.
Definition: mediafileinfo.h:432
TagParser::BasicFileInfo
The BasicFileInfo class provides basic file information such as file name, extension,...
Definition: basicfileinfo.h:14
TagParser::ElementPosition::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
Definition: signature.cpp:48
TagParser::ParsingStatus::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
TagParser::Adts
Definition: signature.cpp:86
TagParser::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::ParsingStatus::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
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::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
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::BasicFileInfo::stream
CppUtilities::NativeFileStream & stream()
Returns the std::fstream for the current instance.
Definition: basicfileinfo.h:81
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::BasicFileInfo::path
const std::string & path() const
Returns the path of the current file.
Definition: basicfileinfo.h:99
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
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
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
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
TagParser::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