Use segment title as fallback for returned tag title

Some applications like handbrake store the title only in the segment
info element.
This commit is contained in:
Urs Fleisch
2025-09-02 18:04:30 +02:00
parent 241b3b2921
commit 1d3a375765
8 changed files with 48 additions and 2 deletions

View File

@ -91,6 +91,7 @@ std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileData);
RETURN_ELEMENT_FOR_CASE(Id::MkSeekID);
RETURN_ELEMENT_FOR_CASE(Id::MkDuration);
RETURN_ELEMENT_FOR_CASE(Id::MkTitle);
RETURN_ELEMENT_FOR_CASE(Id::MkSamplingFrequency);
RETURN_ELEMENT_FOR_CASE(Id::MkSeekHead);
RETURN_ELEMENT_FOR_CASE(Id::VoidElement);

View File

@ -75,6 +75,7 @@ namespace TagLib::EBML {
MkInfo = 0x1549A966,
MkTimestampScale = 0x2AD7B1,
MkDuration = 0x4489,
MkTitle = 0x7BA9,
MkTracks = 0x1654AE6B,
MkTrackEntry = 0xAE,
MkCodecID = 0x86,
@ -186,6 +187,7 @@ namespace TagLib::EBML {
template <> struct GetElementTypeById<Element::Id::MkTagBinary> { using type = BinaryElement; };
template <> struct GetElementTypeById<Element::Id::MkCodecState> { using type = BinaryElement; };
template <> struct GetElementTypeById<Element::Id::MkDuration> { using type = FloatElement; };
template <> struct GetElementTypeById<Element::Id::MkTitle> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkSamplingFrequency> { using type = FloatElement; };
template <> struct GetElementTypeById<Element::Id::MkSeekHead> { using type = MkSeekHead; };
template <> struct GetElementTypeById<Element::Id::VoidElement> { using type = VoidElement; };

View File

@ -38,6 +38,7 @@ void EBML::MkInfo::parse(Matroska::Properties *properties)
unsigned long long timestampScale = 1000000;
double duration = 0.0;
String title;
for(const auto &element : elements) {
Id id = element->getId();
if (id == Id::MkTimestampScale) {
@ -46,8 +47,12 @@ void EBML::MkInfo::parse(Matroska::Properties *properties)
else if (id == Id::MkDuration) {
duration = element_cast<Id::MkDuration>(element)->getValueAsDouble();
}
else if (id == Id::MkTitle) {
title = element_cast<Id::MkTitle>(element)->getValue();
}
}
properties->setLengthInMilliseconds(
static_cast<int>(duration * static_cast<double>(timestampScale) / 1000000.0));
properties->setTitle(title);
}

View File

@ -108,8 +108,12 @@ Tag *Matroska::File::tag() const
Matroska::Tag *Matroska::File::tag(bool create) const
{
if(!d->tag && create)
if(!d->tag && create) {
d->tag = std::make_unique<Tag>();
if(d->properties) {
d->tag->setSegmentTitle(d->properties->title());
}
}
return d->tag.get();
}
@ -305,6 +309,9 @@ void Matroska::File::read(bool readProperties, Properties::ReadStyle readStyle)
d->properties = std::make_unique<Properties>(this);
segment->parseInfo(d->properties.get());
segment->parseTracks(d->properties.get());
if(d->tag) {
d->tag->setSegmentTitle(d->properties->title());
}
}
if(readStyle == AudioProperties::Accurate &&

View File

@ -40,6 +40,7 @@ public:
File *file;
String codecName;
String title;
int length { 0 };
int bitrate { -1 };
int sampleRate { 0 };
@ -92,6 +93,11 @@ String Matroska::Properties::codecName() const
return d->codecName;
}
String Matroska::Properties::title() const
{
return d->title;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
@ -125,3 +131,8 @@ void Matroska::Properties::setCodecName(const String &codecName)
{
d->codecName = codecName;
}
void Matroska::Properties::setTitle(const String& title)
{
d->title = title;
}

View File

@ -86,6 +86,13 @@ namespace TagLib::Matroska {
*/
String codecName() const;
/*!
* Returns the general name of the segment.
* Some applications store the title of the file here, but players should
* prioritize the tag title over the segment title.
*/
String title() const;
private:
class PropertiesPrivate;
friend class EBML::MkInfo;
@ -97,6 +104,7 @@ namespace TagLib::Matroska {
void setChannels(int channels);
void setBitsPerSample(int bitsPerSample);
void setCodecName(const String &codecName);
void setTitle(const String &title);
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;

View File

@ -74,6 +74,7 @@ public:
SimpleTagsList tags;
ByteVector data;
String segmentTitle;
};
Matroska::Tag::Tag() :
@ -115,6 +116,11 @@ const Matroska::SimpleTagsList &Matroska::Tag::simpleTagsList() const
return d->tags;
}
void Matroska::Tag::setSegmentTitle(const String& title)
{
d->segmentTitle = title;
}
void Matroska::Tag::setTitle(const String &s)
{
d->setTag("TITLE", s);
@ -152,7 +158,11 @@ void Matroska::Tag::setTrack(unsigned int i)
String Matroska::Tag::title() const
{
return d->getTag("TITLE");
String s = d->getTag("TITLE");
if(s.isEmpty()) {
return d->segmentTitle;
}
return s;
}
String Matroska::Tag::artist() const

View File

@ -98,6 +98,8 @@ namespace TagLib {
friend class EBML::MkTags;
class TagPrivate;
void setSegmentTitle(const String &title);
// private Element implementation
ByteVector renderInternal() override;