C++ library for reading and writing MP4/M4A/AAC (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
Martchus 2448bcd456 Fix TagValue::convertDescriptionEncoding() 3 hours ago
aac Fix warnings 2 months ago
adts Fix warnings 2 months ago
av1 Adapt to changes in c++utilities 2 years ago
avc Fix handling AVC config errors leading to crashes 5 months ago
avi Adapt to changes in c++utilities 2 years ago
cmake/scripts Fix usage of bibliographic language codes 4 months ago
doc Improve refs in "Adding new fields" doc 2 years ago
flac Fix warnings 2 months ago
id3 Fix handling empty ID3v2 record date fields to avoid year "0001" in this case 10 hours ago
ivf Add API to allow aborting overall/expensive parsing functions 3 months ago
matroska Preserve implicit language setting "English" via ISO language code of MKV tracks 1 month ago
mp4 Preserve media type of MP4 tracks if media type is unknown 4 weeks ago
mpegaudio Fix warnings 2 months ago
ogg Fix warnings 2 months ago
opus Adapt to changes in c++utilities 2 years ago
scripts Add script to update enum comments 1 year ago
testfiles Add MP4 testfile with chapters 2 years ago
tests Improve passing paths 2 weeks ago
vorbis Fix warnings 2 months ago
wav Fix warnings 2 months ago
.gitignore Apply clang-format later 4 years ago
CMakeLists.txt Allow to specify location of JSON document for ISO-639-2 4 months ago
LICENSE Initial commit 6 years ago
README.md Add API to allow aborting overall/expensive parsing functions 3 months ago
abstractattachment.cpp Fix warnings 2 months ago
abstractattachment.h Fix warnings 2 months ago
abstractchapter.cpp Add API to allow aborting overall/expensive parsing functions 3 months ago
abstractchapter.h Add API to allow aborting overall/expensive parsing functions 3 months ago
abstractcontainer.cpp Add API to allow aborting overall/expensive parsing functions 3 months ago
abstractcontainer.h Add API to allow aborting overall/expensive parsing functions 3 months ago
abstracttrack.cpp Avoid implicit conversion 1 month ago
abstracttrack.h Add API to allow aborting overall/expensive parsing functions 3 months ago
aspectratio.cpp Adapt to c++utilities v5 2 years ago
aspectratio.h Make many constructors explicit to avoid unintended implicit conversions 5 months ago
backuphelper.cpp Use std::string_view where it makes sense 3 months ago
backuphelper.h Adapt to changes in c++utilities 2 years ago
basicfileinfo.cpp Improve passing paths 2 weeks ago
basicfileinfo.h Improve passing paths 2 weeks ago
caseinsensitivecomparer.h Use std::string_view where it makes sense 3 months ago
diagnostics.cpp Use std::string_view where it makes sense 3 months ago
diagnostics.h Use std::string_view where it makes sense 3 months ago
exceptions.cpp Improve documentation 6 months ago
exceptions.h Use noexcept directly 2 years ago
fieldbasedtag.h Fix warnings 2 months ago
genericcontainer.h Fix warnings 2 months ago
genericfileelement.h Fix warnings 2 months ago
generictagfield.h Improve customization point for clearing tag field 3 months ago
global.h Adapt to changes in c++utilities 2 years ago
localeawarestring.cpp Apply clang-format 3 years ago
localeawarestring.h Add Locale class to deal with differently specified languages/countries 5 months ago
localehelper.cpp Don't map language code "und" 4 months ago
localehelper.h Remove unused optional header from localehelper.h 4 months ago
margin.h Make many constructors explicit to avoid unintended implicit conversions 5 months ago
mediafileinfo.cpp Improve passing paths 2 weeks ago
mediafileinfo.h Improve passing paths 2 weeks ago
mediaformat.cpp Use std::string_view where it makes sense 3 months ago
mediaformat.h Use std::string_view where it makes sense 3 months ago
positioninset.h Make many constructors explicit to avoid unintended implicit conversions 5 months ago
progressfeedback.cpp Document progress feedback 3 years ago
progressfeedback.h Add API to allow aborting overall/expensive parsing functions 3 months ago
settings.h Use helper for flag enum from c++utilities 2 years ago
signature.cpp Use std::string_view where it makes sense 3 months ago
signature.h Use std::string_view where it makes sense 3 months ago
size.cpp Consider 4:3 resolutions as well in Size::abbreviation() 3 months ago
size.h Use std::string_view where it makes sense 3 months ago
tag.cpp Fix warnings 2 months ago
tag.h Fix warnings 2 months ago
tagtarget.cpp Use std::string_view where it makes sense 3 months ago
tagtarget.h Use std::string_view where it makes sense 3 months ago
tagvalue.cpp Fix TagValue::convertDescriptionEncoding() 3 hours ago
tagvalue.h Use std::string_view where it makes sense 3 months ago

README.md

Tag Parser

C++ library for reading and writing MP4 (iTunes), ID3, Vorbis, Opus, FLAC and Matroska tags.

Supported formats

The tag library can read and write the following tag formats:

  • iTunes-style MP4/M4A tags (MP4-DASH is supported)
  • ID3v1 and ID3v2 tags
    • conversion between ID3v1 and different versions of ID3v2 is possible
  • Vorbis, Opus and FLAC comments in Ogg streams
    • cover art via "METADATA_BLOCK_PICTURE" is supported
  • Vorbis comments and "METADATA_BLOCK_PICTURE" in raw FLAC streams
  • Matroska/WebM tags and attachments

File layout options

Tag position

The library allows you to choose whether tags should be placed at the beginning or at the end of an MP4/Matroska file.

Padding

Padding allows adding additional tag information without rewriting the entire file or appending the tag. Usage of padding can be configured:

  • minimum/maximum padding: The file is rewritten if the padding would fall below/exceed the specifed limits.
  • preferred padding: If the file needs to be rewritten the preferred padding is used.

Default value for minimum and maximum padding is zero. Hence the library will almost always have to rewrite the entire file to apply changes. To prevent this, set at least the maximum padding to a higher value.

It is also possible to force rewriting the entire file always.

Taking advantage of padding is currently not supported when dealing with Ogg streams (it is supported when dealing with raw FLAC streams).

Additional features

The library can also display technical information such as the ID, format, language, bitrate, duration, size, timestamps, sampling frequency, FPS and other information of the tracks.

It also allows to inspect and validate the element structure of MP4 and Matroska files.

Text encoding, Unicode support

The library is aware of different text encodings and can convert between different encodings using iconv.

Windows specific

A workaround to support filenames containing non-ASCII characters (despite the lack of an UTF-8 supporting std::fstream under Windows) can be enabled by adding -DUSE_NATIVE_FILE_BUFFER=ON to the CMake arguments when building c++utilities. It is not sufficient to specify this option only when building tagparser.

Usage

This example shows how to read and write tag fields in a format-independent way:

#include <tagparser/mediafileinfo.h>
#include <tagparser/diagnostics.h>
#include <tagparser/progressfeedback.h>

// create a MediaFileInfo for high-level access to overall functionality of the library
auto fileInfo = MediaFileInfo();

// create container for errors, warnings, etc.
auto diag = Diagnostics();

// create handle to abort gracefully and get feedback during during long operations
auto progress = AbortableProgressFeedback([callback for status update], [callback for percentage-only updates]);

// open file (might throw ios_base::failure)
fileInfo.setPath("/path/to/some/file");
fileInfo.open();

// parse container format, tags, attachments and/or chapters as needed
// notes:
// - These functions might throw exceptions derived from ios_base::failure for IO errors and
//   populate diag with possibly critical parsing messages you definitely want to check in production
//   code.
// - Parsing a file can be expensive if the file is big or the disk IO is slow. You might want to
//   run it in a separate thread.
// - At this point the parser does not make much use of the progress object.
fileInfo.parseContainerFormat(diag, progress);
fileInfo.parseTags(diag, progress);
fileInfo.parseAttachments(diag, progress);
fileInfo.parseChapters(diag, progress);
fileInfo.parseEverything(diag, progress); // just use that one if you want all over the above

// get tag as an object derived from the Tag class
// notes:
// - In real code you might want to check how many tags are assigned or use
//   fileInfo.createAppropriateTags(…) to create tags as needed.
auto tag = fileInfo.tags().at(0);

// extract a field value and convert it to UTF-8 std::string (toString() might throw ConversionException)
#include <tagparser/tag.h>
#include <tagparser/tagvalue.h>
auto title = tag->value(TagParser::KnownField::Title).toString(TagParser::TagTextEncoding::Utf8);

// change a field value using an encoding suitable for the tag format
tag->setValue(KnownField::Album, TagValue("some UTF-8 string", TagTextEncoding::Utf8, tag->proposedTextEncoding()));

// get/remove/create attachments
#include <tagparser/abstractattachment.h>
if (auto *const container = fileInfo.container()) {
    for (auto i = 0, count = container->attachmentCount(); i != count; ++i) {
        auto attachment = container->attachment(i);
        if (attachment->mimeType() == "image/jpeg") {
            attachment->setIgnored(true); // remove existing attachment
        }
    }
    // create new attachment
    auto attachment = container->createAttachment();
    attachment->setName("cover.jpg");
    attachment->setFile(cover, diag);
}

// apply changes to the file on disk
// notes:
// - Might throw exception derived from TagParser::Failure for fatal processing error or ios_base::failure
//   for IO errors.
// - Applying changes can be expensive if the file is big or the disk IO is slow. You might want to
//   run it in a separate thread.
// - Use progress.tryToAbort() from another thread or an interrupt handler to abort gracefully without leaving
//   the file in an inconsistent state.
// - Be sure everyting has been parsed before as the library needs to be aware of the whole file structure.
fileInfo.parseEverything(diag, progress);
fileInfo.applyChanges(diag, progress);

Summary

  • The most important class is TagParser::MediaFileInfo providing access to everything else.
  • IO errors are propagated via standard std::ios_base::failure.
  • Fatal processing errors are propagated by throwing a class derived from TagParser::Failure.
  • All operations which might generate warnings, non-fatal errors, ... take a TagParser::Diagnostics object to store those messages.
  • All operations which might be aborted or might provide progress feedback take a TagParser::AbortableProgressFeedback object for callbacks and aborting.
  • Field values are stored using TagParser::TagValue objects. Those objects erase the actual type similar to QVariant from the Qt framework. The documentation of TagParser::TagValue covers how different types and encodings are handled.

Further documentation

For more examples check out the command line interface of Tag Editor. API documentation can be generated using Doxygen with make tagparser_apidoc.

Bugs, stability

Bugs can be reported on GitHub.

It is recommend to create backups before editing because I can not test whether the library works with all kinds of files. (When forcing rewrite a backup is always created.)

Build instructions

The tagparser library depends on c++utilities and is built in the same way. It also depends on zlib, iso-codes and requires at least CMake 3.19. For checking integrity of testfiles, the OpenSSL crypto library is required.

TODOs

  • Support more formats (EXIF, PDF metadata, Theora, ...)
  • Support adding cue-sheet to FLAC files

More TODOs are tracked in the issue section at GitHub.