diff --git a/examples/matroskareader.cpp b/examples/matroskareader.cpp index 453d583a..4f7f86cc 100644 --- a/examples/matroskareader.cpp +++ b/examples/matroskareader.cpp @@ -44,6 +44,11 @@ int main(int argc, char *argv[]) PRINT_PRETTY("Target Type Value", targetTypeValue == 0 ? "None" : TagLib::Utils::formatString("%i", targetTypeValue).toCString(false) ); + if(auto trackUid = t.trackUid()) { + PRINT_PRETTY("Track UID", + TagLib::Utils::formatString("%llu",trackUid).toCString(false) + ); + } const TagLib::String &language = t.language(); PRINT_PRETTY("Language", !language.isEmpty() ? language.toCString(false) : "Not set"); diff --git a/taglib/matroska/ebml/ebmlelement.cpp b/taglib/matroska/ebml/ebmlelement.cpp index 348303ac..8d2e3a41 100644 --- a/taglib/matroska/ebml/ebmlelement.cpp +++ b/taglib/matroska/ebml/ebmlelement.cpp @@ -82,6 +82,7 @@ std::unique_ptr EBML::Element::factory(File &file) RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileMediaType); RETURN_ELEMENT_FOR_CASE(Id::MkCodecID); RETURN_ELEMENT_FOR_CASE(Id::MkTagTargetTypeValue); + RETURN_ELEMENT_FOR_CASE(Id::MkTagTrackUID); RETURN_ELEMENT_FOR_CASE(Id::MkTagsLanguageDefault); RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileUID); RETURN_ELEMENT_FOR_CASE(Id::MkSeekPosition); diff --git a/taglib/matroska/ebml/ebmlelement.h b/taglib/matroska/ebml/ebmlelement.h index 3b8c9267..ac043150 100644 --- a/taglib/matroska/ebml/ebmlelement.h +++ b/taglib/matroska/ebml/ebmlelement.h @@ -40,6 +40,7 @@ namespace TagLib::EBML { MkTag = 0x7373, MkTagTargets = 0x63C0, MkTagTargetTypeValue = 0x68CA, + MkTagTrackUID = 0x63C5, MkSimpleTag = 0x67C8, MkTagName = 0x45A3, MkTagLanguage = 0x447A, @@ -168,6 +169,7 @@ namespace TagLib::EBML { template <> struct GetElementTypeById { using type = Latin1StringElement; }; template <> struct GetElementTypeById { using type = Latin1StringElement; }; template <> struct GetElementTypeById { using type = UIntElement; }; + template <> struct GetElementTypeById { using type = UIntElement; }; template <> struct GetElementTypeById { using type = UIntElement; }; template <> struct GetElementTypeById { using type = UIntElement; }; template <> struct GetElementTypeById { using type = UIntElement; }; diff --git a/taglib/matroska/ebml/ebmlmktags.cpp b/taglib/matroska/ebml/ebmlmktags.cpp index bbfec2c6..ffee7e5c 100644 --- a/taglib/matroska/ebml/ebmlmktags.cpp +++ b/taglib/matroska/ebml/ebmlmktags.cpp @@ -54,6 +54,7 @@ std::unique_ptr EBML::MkTags::parse() // Parse the element Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None; + unsigned long long trackUid = 0; if(targets) { for(const auto &targetsChild : *targets) { Id id = targetsChild->getId(); @@ -63,6 +64,9 @@ std::unique_ptr EBML::MkTags::parse() element_cast(targetsChild)->getValue() ); } + else if(id == Id::MkTagTrackUID) { + trackUid = element_cast(targetsChild)->getValue(); + } } } @@ -92,9 +96,11 @@ std::unique_ptr EBML::MkTags::parse() mTag->addSimpleTag(tagValueString ? Matroska::SimpleTag(tagName, *tagValueString, - targetTypeValue, language, defaultLanguageFlag) + targetTypeValue, language, defaultLanguageFlag, + trackUid) : Matroska::SimpleTag(tagName, *tagValueBinary, - targetTypeValue, language, defaultLanguageFlag)); + targetTypeValue, language, defaultLanguageFlag, + trackUid)); } } return mTag; diff --git a/taglib/matroska/matroskasimpletag.cpp b/taglib/matroska/matroskasimpletag.cpp index 43fb0fc5..2c5209fa 100644 --- a/taglib/matroska/matroskasimpletag.cpp +++ b/taglib/matroska/matroskasimpletag.cpp @@ -30,16 +30,19 @@ class Matroska::SimpleTag::SimpleTagPrivate { public: explicit SimpleTagPrivate(const String &name, const String& value, - TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) : - value(value), name(name), language(language), + TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage, + unsigned long long trackUid) : + value(value), name(name), language(language), trackUid(trackUid), targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {} explicit SimpleTagPrivate(const String &name, const ByteVector& value, - TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) : - value(value), name(name), language(language), + TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage, + unsigned long long trackUid) : + value(value), name(name), language(language), trackUid(trackUid), targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {} const std::variant value; const String name; const String language; + const unsigned long long trackUid; const TargetTypeValue targetTypeValue; const bool defaultLanguageFlag; }; @@ -49,16 +52,20 @@ public: //////////////////////////////////////////////////////////////////////////////// Matroska::SimpleTag::SimpleTag(const String &name, const String &value, - TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) : + TargetTypeValue targetTypeValue, + const String &language, bool defaultLanguage, + unsigned long long trackUid) : d(std::make_unique(name, value, targetTypeValue, - language, defaultLanguage)) + language, defaultLanguage, trackUid)) { } Matroska::SimpleTag::SimpleTag(const String &name, const ByteVector &value, - TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage) : + TargetTypeValue targetTypeValue, + const String &language, bool defaultLanguage, + unsigned long long trackUid) : d(std::make_unique(name, value, targetTypeValue, - language, defaultLanguage)) + language, defaultLanguage, trackUid)) { } @@ -106,6 +113,11 @@ bool Matroska::SimpleTag::defaultLanguageFlag() const return d->defaultLanguageFlag; } +unsigned long long Matroska::SimpleTag::trackUid() const +{ + return d->trackUid; +} + Matroska::SimpleTag::ValueType Matroska::SimpleTag::type() const { return std::holds_alternative(d->value) ? BinaryType : StringType; diff --git a/taglib/matroska/matroskasimpletag.h b/taglib/matroska/matroskasimpletag.h index def0fea8..faff96e0 100644 --- a/taglib/matroska/matroskasimpletag.h +++ b/taglib/matroska/matroskasimpletag.h @@ -55,14 +55,16 @@ namespace TagLib { */ SimpleTag(const String &name, const String &value, TargetTypeValue targetTypeValue = None, - const String &language = String(), bool defaultLanguage = true); + const String &language = String(), bool defaultLanguage = true, + unsigned long long trackUid = 0); /*! * Construct a binary simple tag. */ SimpleTag(const String &name, const ByteVector &value, TargetTypeValue targetTypeValue = None, - const String &language = String(), bool defaultLanguage = true); + const String &language = String(), bool defaultLanguage = true, + unsigned long long trackUid = 0); /*! * Construct a simple tag as a copy of \a other. @@ -114,6 +116,12 @@ namespace TagLib { */ bool defaultLanguageFlag() const; + /*! + * Returns the UID that identifies the track that the tags belong to, + * zero if not defined, the tag applies to all tracks + */ + unsigned long long trackUid() const; + /*! * Returns the type of the value. */ diff --git a/taglib/matroska/matroskatag.cpp b/taglib/matroska/matroskatag.cpp index 7c896934..94a19e88 100644 --- a/taglib/matroska/matroskatag.cpp +++ b/taglib/matroska/matroskatag.cpp @@ -92,11 +92,13 @@ void Matroska::Tag::addSimpleTag(const SimpleTag &tag) } void Matroska::Tag::removeSimpleTag(const String &name, - SimpleTag::TargetTypeValue targetTypeValue) + SimpleTag::TargetTypeValue targetTypeValue, + unsigned long long trackUid) { auto it = std::find_if(d->tags.begin(), d->tags.end(), - [&name, targetTypeValue](const SimpleTag &t) { - return t.name() == name && t.targetTypeValue() == targetTypeValue; + [&name, targetTypeValue, trackUid](const SimpleTag &t) { + return t.name() == name && t.targetTypeValue() == targetTypeValue && + t.trackUid() == trackUid; } ); if(it != d->tags.end()) { @@ -221,11 +223,13 @@ ByteVector Matroska::Tag::renderInternal() // Build target-based list for(const auto &tag : std::as_const(d->tags)) { auto targetTypeValue = tag.targetTypeValue(); + auto trackUid = tag.trackUid(); auto it = std::find_if(targetList.begin(), targetList.end(), [&](const auto &list) { const auto &simpleTag = list.front(); - return simpleTag.targetTypeValue() == targetTypeValue; + return simpleTag.targetTypeValue() == targetTypeValue && + simpleTag.trackUid() == trackUid; } ); if(it == targetList.end()) { @@ -239,6 +243,7 @@ ByteVector Matroska::Tag::renderInternal() for(const auto &list : targetList) { const auto &frontTag = list.front(); auto targetTypeValue = frontTag.targetTypeValue(); + auto trackUid = frontTag.trackUid(); auto tag = EBML::make_unique_element(); // Build element @@ -248,6 +253,11 @@ ByteVector Matroska::Tag::renderInternal() element->setValue(static_cast(targetTypeValue)); targets->appendElement(std::move(element)); } + if(trackUid != 0) { + auto element = EBML::make_unique_element(); + element->setValue(trackUid); + targets->appendElement(std::move(element)); + } tag->appendElement(std::move(targets)); // Build element @@ -401,7 +411,8 @@ String Matroska::Tag::TagPrivate::getTag(const String &key) const return t.name() == name && t.type() == SimpleTag::StringType && (t.targetTypeValue() == targetTypeValue || - (t.targetTypeValue() == SimpleTag::TargetTypeValue::None && !strict)); + (t.targetTypeValue() == SimpleTag::TargetTypeValue::None && !strict)) + && t.trackUid() == 0; } ); return it != tags.end() ? it->toString() : String(); @@ -411,7 +422,7 @@ PropertyMap Matroska::Tag::properties() const { PropertyMap properties; for(const auto &simpleTag : std::as_const(d->tags)) { - if(simpleTag.type() == SimpleTag::StringType) { + if(simpleTag.type() == SimpleTag::StringType && simpleTag.trackUid() == 0) { String key = translateTag(simpleTag.name(), simpleTag.targetTypeValue()); if(!key.isEmpty()) properties[key].append(simpleTag.toString()); @@ -428,6 +439,7 @@ PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap) for(auto it = d->tags.begin(); it != d->tags.end();) { String key; if(it->type() == SimpleTag::StringType && + it->trackUid() == 0 && !(key = translateTag(it->name(), it->targetTypeValue())).isEmpty()) { it = d->tags.erase(it); setNeedsRender(true); @@ -468,6 +480,7 @@ StringList Matroska::Tag::complexPropertyKeys() const StringList keys; for(const SimpleTag &t : std::as_const(d->tags)) { if(t.type() != SimpleTag::StringType || + t.trackUid() != 0 || translateTag(t.name(), t.targetTypeValue()).isEmpty()) { keys.append(t.name()); } @@ -482,6 +495,7 @@ List Matroska::Tag::complexProperties(const String& key) const for(const SimpleTag &t : std::as_const(d->tags)) { if(t.name() == key && (t.type() != SimpleTag::StringType || + t.trackUid() != 0 || translateTag(t.name(), t.targetTypeValue()).isEmpty())) { VariantMap property; if(t.type() != SimpleTag::StringType) { @@ -491,7 +505,12 @@ List Matroska::Tag::complexProperties(const String& key) const property.insert("value", t.toString()); } property.insert("name", t.name()); - property.insert("targetTypeValue", t.targetTypeValue()); + if(t.targetTypeValue() != SimpleTag::TargetTypeValue::None) { + property.insert("targetTypeValue", t.targetTypeValue()); + } + if(t.trackUid()) { + property.insert("trackUid", t.trackUid()); + } property.insert("language", t.language()); property.insert("defaultLanguage", t.defaultLanguageFlag()); props.append(property); @@ -511,6 +530,7 @@ bool Matroska::Tag::setComplexProperties(const String& key, const List(); bool defaultLanguage = property.value("defaultLanguage", true).value(); + auto trackUid = property.value("trackUid", 0ULL).value(); d->tags.append(property.contains("data") ? SimpleTag(key, property.value("data").value(), - targetTypeValue, language, defaultLanguage) + targetTypeValue, language, defaultLanguage, trackUid) : SimpleTag(key, property.value("value").value(), - targetTypeValue, language, defaultLanguage)); + targetTypeValue, language, defaultLanguage, trackUid)); setNeedsRender(true); result = true; } diff --git a/taglib/matroska/matroskatag.h b/taglib/matroska/matroskatag.h index 2ceaa453..81f83876 100644 --- a/taglib/matroska/matroskatag.h +++ b/taglib/matroska/matroskatag.h @@ -89,7 +89,8 @@ namespace TagLib { bool setComplexProperties(const String& key, const List& value) override; void addSimpleTag(const SimpleTag &tag); - void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue); + void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue, + unsigned long long trackUid = 0); void clearSimpleTags(); const SimpleTagsList &simpleTagsList() const;