From cfade6d0825847bd2d1966ea364f758314382edd Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Sat, 16 Aug 2025 11:19:25 +0200 Subject: [PATCH] Implement Matroska audio properties --- taglib/CMakeLists.txt | 6 ++ taglib/matroska/ebml/ebmlelement.cpp | 18 +++++ taglib/matroska/ebml/ebmlelement.h | 10 +++ taglib/matroska/ebml/ebmlfloatelement.cpp | 82 +++++++++++++++++++++++ taglib/matroska/ebml/ebmlfloatelement.h | 64 ++++++++++++++++++ taglib/matroska/ebml/ebmlmkinfo.cpp | 53 +++++++++++++++ taglib/matroska/ebml/ebmlmkinfo.h | 56 ++++++++++++++++ taglib/matroska/ebml/ebmlmkseekhead.cpp | 6 +- taglib/matroska/ebml/ebmlmksegment.cpp | 28 ++++++++ taglib/matroska/ebml/ebmlmksegment.h | 6 ++ taglib/matroska/ebml/ebmlmktracks.cpp | 73 ++++++++++++++++++++ taglib/matroska/ebml/ebmlmktracks.h | 56 ++++++++++++++++ taglib/matroska/matroskafile.cpp | 15 ++++- taglib/matroska/matroskafile.h | 3 +- taglib/matroska/matroskaproperties.cpp | 63 +++++++++++++++-- taglib/matroska/matroskaproperties.h | 40 ++++++++++- 16 files changed, 565 insertions(+), 14 deletions(-) create mode 100644 taglib/matroska/ebml/ebmlfloatelement.cpp create mode 100644 taglib/matroska/ebml/ebmlfloatelement.h create mode 100644 taglib/matroska/ebml/ebmlmkinfo.cpp create mode 100644 taglib/matroska/ebml/ebmlmkinfo.h create mode 100644 taglib/matroska/ebml/ebmlmktracks.cpp create mode 100644 taglib/matroska/ebml/ebmlmktracks.h diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 778b1d9c..68983824 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -245,9 +245,12 @@ if(WITH_MATROSKA) matroska/ebml/ebmlmkcues.h matroska/ebml/ebmlmkseekhead.h matroska/ebml/ebmlmksegment.h + matroska/ebml/ebmlmkinfo.h + matroska/ebml/ebmlmktracks.h matroska/ebml/ebmlmktags.h matroska/ebml/ebmlstringelement.h matroska/ebml/ebmluintelement.h + matroska/ebml/ebmlfloatelement.h matroska/ebml/ebmlutils.h matroska/ebml/ebmlvoidelement.h ) @@ -464,9 +467,12 @@ if(WITH_MATROSKA) matroska/ebml/ebmlmkcues.cpp matroska/ebml/ebmlmkseekhead.cpp matroska/ebml/ebmlmksegment.cpp + matroska/ebml/ebmlmkinfo.cpp + matroska/ebml/ebmlmktracks.cpp matroska/ebml/ebmlmktags.cpp matroska/ebml/ebmlstringelement.cpp matroska/ebml/ebmluintelement.cpp + matroska/ebml/ebmlfloatelement.cpp matroska/ebml/ebmlutils.cpp matroska/ebml/ebmlvoidelement.cpp ) diff --git a/taglib/matroska/ebml/ebmlelement.cpp b/taglib/matroska/ebml/ebmlelement.cpp index e8bdbea3..25fdc52a 100644 --- a/taglib/matroska/ebml/ebmlelement.cpp +++ b/taglib/matroska/ebml/ebmlelement.cpp @@ -22,10 +22,12 @@ #include "ebmlvoidelement.h" #include "ebmlmasterelement.h" #include "ebmlbinaryelement.h" +#include "ebmlfloatelement.h" #include "ebmlmkseekhead.h" #include "ebmlmksegment.h" #include "ebmlmktags.h" #include "ebmlmkattachments.h" +#include "ebmlmktracks.h" #include "ebmlstringelement.h" #include "ebmluintelement.h" #include "ebmlutils.h" @@ -60,6 +62,12 @@ EBML::Element *EBML::Element::factory(File &file) case ElementIDs::MkSegment: return new MkSegment(sizeLength, dataSize, offset); + case ElementIDs::MkInfo: + return new MkInfo(sizeLength, dataSize, offset); + + case ElementIDs::MkTracks: + return new MkTracks(sizeLength, dataSize, offset); + case ElementIDs::MkTags: return new MkTags(sizeLength, dataSize, offset); @@ -71,6 +79,8 @@ EBML::Element *EBML::Element::factory(File &file) case ElementIDs::MkSimpleTag: case ElementIDs::MkAttachedFile: case ElementIDs::MkSeek: + case ElementIDs::MkTrackEntry: + case ElementIDs::MkAudio: return new MasterElement(id, sizeLength, dataSize, offset); case ElementIDs::MkTagName: @@ -81,17 +91,25 @@ EBML::Element *EBML::Element::factory(File &file) case ElementIDs::MkTagLanguage: case ElementIDs::MkAttachedFileMediaType: + case ElementIDs::MkCodecID: return new Latin1StringElement(id, sizeLength, dataSize); case ElementIDs::MkTagTargetTypeValue: case ElementIDs::MkAttachedFileUID: case ElementIDs::MkSeekPosition: + case ElementIDs::MkTimestampScale: + case ElementIDs::MkBitDepth: + case ElementIDs::MkChannels: return new UIntElement(id, sizeLength, dataSize); case ElementIDs::MkAttachedFileData: case ElementIDs::MkSeekID: return new BinaryElement(id, sizeLength, dataSize); + case ElementIDs::MkDuration: + case ElementIDs::MkSamplingFrequency: + return new FloatElement(id, sizeLength, dataSize); + case ElementIDs::MkSeekHead: return new MkSeekHead(sizeLength, dataSize, offset); diff --git a/taglib/matroska/ebml/ebmlelement.h b/taglib/matroska/ebml/ebmlelement.h index cb020303..5cc0e06e 100644 --- a/taglib/matroska/ebml/ebmlelement.h +++ b/taglib/matroska/ebml/ebmlelement.h @@ -98,6 +98,16 @@ namespace TagLib::EBML { inline constexpr Element::Id MkCueCodecState = 0xEA; inline constexpr Element::Id MkCueReference = 0xDB; inline constexpr Element::Id MkCueRefTime = 0x96; + inline constexpr Element::Id MkInfo = 0x1549A966; + inline constexpr Element::Id MkTimestampScale = 0x2AD7B1; + inline constexpr Element::Id MkDuration = 0x4489; + inline constexpr Element::Id MkTracks = 0x1654AE6B; + inline constexpr Element::Id MkTrackEntry = 0xAE; + inline constexpr Element::Id MkCodecID = 0x86; + inline constexpr Element::Id MkAudio = 0xE1; + inline constexpr Element::Id MkSamplingFrequency = 0xB5; + inline constexpr Element::Id MkBitDepth = 0x6264; + inline constexpr Element::Id MkChannels = 0x9F; } } diff --git a/taglib/matroska/ebml/ebmlfloatelement.cpp b/taglib/matroska/ebml/ebmlfloatelement.cpp new file mode 100644 index 00000000..4752d862 --- /dev/null +++ b/taglib/matroska/ebml/ebmlfloatelement.cpp @@ -0,0 +1,82 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include "ebmlfloatelement.h" +#include "ebmlutils.h" +#include "tbytevector.h" +#include "tfile.h" +#include "tdebug.h" + +using namespace TagLib; + +double EBML::FloatElement::getValueAsDouble(double defaultValue) const +{ + if(std::holds_alternative(value)) { + return std::get(value); + } + if(std::holds_alternative(value)) { + return std::get(value); + } + return defaultValue; +} + +bool EBML::FloatElement::read(File &file) +{ + ByteVector buffer = file.readBlock(dataSize); + if(buffer.size() != dataSize) { + debug("Failed to read EBML Float element"); + return false; + } + + if(dataSize == 0) { + value = std::monostate(); + } + else if(dataSize == 4) { + value = buffer.toFloat32BE(0); + } + else if(dataSize == 8) { + value = buffer.toFloat64BE(0); + } + else { + debug("Invalid size for EBML Float element"); + return false; + } + return true; +} + +ByteVector EBML::FloatElement::render() +{ + ByteVector data; + if(std::holds_alternative(value)) { + data = ByteVector::fromFloat64BE(std::get(value)); + } + else if(std::holds_alternative(value)) { + data = ByteVector::fromFloat32BE(std::get(value)); + } + ByteVector buffer = renderId(); + buffer.append(renderVINT(data.size(), 0)); + buffer.append(data); + return buffer; +} diff --git a/taglib/matroska/ebml/ebmlfloatelement.h b/taglib/matroska/ebml/ebmlfloatelement.h new file mode 100644 index 00000000..a51e444c --- /dev/null +++ b/taglib/matroska/ebml/ebmlfloatelement.h @@ -0,0 +1,64 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_EBMLFLOATELEMENT_H +#define TAGLIB_EBMLFLOATELEMENT_H +#ifndef DO_NOT_DOCUMENT + +#include +#include "ebmlelement.h" + +namespace TagLib { + class File; + + namespace EBML { + class FloatElement : public Element + { + public: + using FloatVariantType = std::variant; + + FloatElement(Id id, int sizeLength, offset_t dataSize) : + Element(id, sizeLength, dataSize) + { + } + + explicit FloatElement(Id id) : + FloatElement(id, 0, 0) + { + } + FloatVariantType getValue() const { return value; } + double getValueAsDouble(double defaultValue = 0.0) const; + void setValue(FloatVariantType val) { value = val; } + bool read(File &file) override; + ByteVector render() override; + + private: + FloatVariantType value; + }; + } +} + +#endif +#endif diff --git a/taglib/matroska/ebml/ebmlmkinfo.cpp b/taglib/matroska/ebml/ebmlmkinfo.cpp new file mode 100644 index 00000000..feba7c01 --- /dev/null +++ b/taglib/matroska/ebml/ebmlmkinfo.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include "ebmlmkinfo.h" +#include "ebmlstringelement.h" +#include "ebmluintelement.h" +#include "ebmlfloatelement.h" +#include "matroskaproperties.h" + +using namespace TagLib; + +void EBML::MkInfo::parse(Matroska::Properties *properties) +{ + if(!properties) + return; + + unsigned long long timestampScale = 1000000; + double duration = 0.0; + for(auto element : elements) { + Id id = element->getId(); + if (id == ElementIDs::MkTimestampScale) { + timestampScale = static_cast(element)->getValue(); + } + else if (id == ElementIDs::MkDuration) { + duration = static_cast(element)->getValueAsDouble(); + } + } + + properties->setLengthInMilliseconds( + static_cast(duration * static_cast(timestampScale) / 1000000.0)); +} diff --git a/taglib/matroska/ebml/ebmlmkinfo.h b/taglib/matroska/ebml/ebmlmkinfo.h new file mode 100644 index 00000000..479c7056 --- /dev/null +++ b/taglib/matroska/ebml/ebmlmkinfo.h @@ -0,0 +1,56 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_EBMLMKINFO_H +#define TAGLIB_EBMLMKINFO_H +#ifndef DO_NOT_DOCUMENT + +#include "ebmlmasterelement.h" +#include "ebmlutils.h" +#include "taglib.h" + +namespace TagLib { + namespace Matroska { + class Properties; + } + namespace EBML { + class MkInfo : public MasterElement + { + public: + MkInfo(int sizeLength, offset_t dataSize, offset_t offset) : + MasterElement(ElementIDs::MkInfo, sizeLength, dataSize, offset) + { + } + MkInfo() : + MasterElement(ElementIDs::MkInfo, 0, 0, 0) + { + } + void parse(Matroska::Properties * properties); + }; + } +} + +#endif +#endif diff --git a/taglib/matroska/ebml/ebmlmkseekhead.cpp b/taglib/matroska/ebml/ebmlmkseekhead.cpp index 144d94e8..477d30f2 100644 --- a/taglib/matroska/ebml/ebmlmkseekhead.cpp +++ b/taglib/matroska/ebml/ebmlmkseekhead.cpp @@ -25,9 +25,9 @@ using namespace TagLib; -Matroska::SeekHead *EBML::MkSeekHead::parse() +std::unique_ptr EBML::MkSeekHead::parse() { - auto seekHead = new Matroska::SeekHead(); + auto seekHead = std::make_unique(); seekHead->setOffset(offset); seekHead->setSize(getSize() + padding); @@ -50,7 +50,7 @@ Matroska::SeekHead *EBML::MkSeekHead::parse() if(entryId && offset) seekHead->addEntry(entryId, offset); else { - delete seekHead; + seekHead.reset(); return nullptr; } } diff --git a/taglib/matroska/ebml/ebmlmksegment.cpp b/taglib/matroska/ebml/ebmlmksegment.cpp index de77a64c..0de3d5e8 100644 --- a/taglib/matroska/ebml/ebmlmksegment.cpp +++ b/taglib/matroska/ebml/ebmlmksegment.cpp @@ -22,6 +22,8 @@ #include "ebmlmktags.h" #include "ebmlmkattachments.h" #include "ebmlmkseekhead.h" +#include "ebmlmkinfo.h" +#include "ebmlmktracks.h" #include "ebmlutils.h" #include "matroskafile.h" #include "matroskatag.h" @@ -36,6 +38,8 @@ EBML::MkSegment::~MkSegment() delete tags; delete attachments; delete seekHead; + delete info; + delete tracks; } bool EBML::MkSegment::read(File &file) @@ -52,6 +56,16 @@ bool EBML::MkSegment::read(File &file) if(!seekHead->read(file)) return false; } + else if(id == ElementIDs::MkInfo) { + info = static_cast(element); + if(!info->read(file)) + return false; + } + else if(id == ElementIDs::MkTracks) { + tracks = static_cast(element); + if(!tracks->read(file)) + return false; + } else if(id == ElementIDs::MkTags) { tags = static_cast(element); if(!tags->read(file)) @@ -95,3 +109,17 @@ Matroska::Segment *EBML::MkSegment::parseSegment() { return new Matroska::Segment(sizeLength, dataSize, offset + idSize(id)); } + +void EBML::MkSegment::parseInfo(Matroska::Properties *properties) +{ + if(info) { + info->parse(properties); + } +} + +void EBML::MkSegment::parseTracks(Matroska::Properties *properties) +{ + if (tracks) { + tracks->parse(properties); + } +} diff --git a/taglib/matroska/ebml/ebmlmksegment.h b/taglib/matroska/ebml/ebmlmksegment.h index 5ea0960c..15ab7db3 100644 --- a/taglib/matroska/ebml/ebmlmksegment.h +++ b/taglib/matroska/ebml/ebmlmksegment.h @@ -23,6 +23,8 @@ #ifndef DO_NOT_DOCUMENT #include "ebmlmasterelement.h" +#include "ebmlmkinfo.h" +#include "ebmlmktracks.h" #include "taglib.h" namespace TagLib { @@ -49,11 +51,15 @@ namespace TagLib { Matroska::Attachments *parseAttachments(); Matroska::SeekHead *parseSeekHead(); Matroska::Segment *parseSegment(); + void parseInfo(Matroska::Properties *properties); + void parseTracks(Matroska::Properties *properties); private: MkTags *tags = nullptr; MkAttachments *attachments = nullptr; MkSeekHead *seekHead = nullptr; + MkInfo *info = nullptr; + MkTracks *tracks = nullptr; }; } } diff --git a/taglib/matroska/ebml/ebmlmktracks.cpp b/taglib/matroska/ebml/ebmlmktracks.cpp new file mode 100644 index 00000000..552b07c3 --- /dev/null +++ b/taglib/matroska/ebml/ebmlmktracks.cpp @@ -0,0 +1,73 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include "ebmlmktracks.h" +#include "ebmlstringelement.h" +#include "ebmluintelement.h" +#include "ebmlfloatelement.h" +#include "matroskaproperties.h" + +using namespace TagLib; + +void EBML::MkTracks::parse(Matroska::Properties *properties) +{ + if(!properties) + return; + + for(auto element : elements) { + if(element->getId() != ElementIDs::MkTrackEntry) + continue; + + String codecId; + double samplingFrequency = 0.0; + unsigned long long bitDepth = 0; + unsigned long long channels = 0; + auto trackEntry = static_cast(element); + for(auto trackEntryChild : *trackEntry) { + Id trackEntryChildId = trackEntryChild->getId(); + if(trackEntryChildId == ElementIDs::MkCodecID) + codecId = static_cast(trackEntryChild)->getValue(); + else if(trackEntryChildId == ElementIDs::MkAudio) { + auto audio = static_cast(trackEntryChild); + for(auto audioChild : *audio) { + Id audioChildId = audioChild->getId(); + if(audioChildId == ElementIDs::MkSamplingFrequency) + samplingFrequency = static_cast(audioChild)->getValueAsDouble(); + else if(audioChildId == ElementIDs::MkBitDepth) + bitDepth = static_cast(audioChild)->getValue(); + else if(audioChildId == ElementIDs::MkChannels) + channels = static_cast(audioChild)->getValue(); + } + } + } + if(bitDepth || channels) { + properties->setSampleRate(static_cast(samplingFrequency)); + properties->setBitsPerSample(static_cast(bitDepth)); + properties->setChannels(static_cast(channels)); + properties->setCodecName(codecId); + return; + } + } +} diff --git a/taglib/matroska/ebml/ebmlmktracks.h b/taglib/matroska/ebml/ebmlmktracks.h new file mode 100644 index 00000000..53754fb7 --- /dev/null +++ b/taglib/matroska/ebml/ebmlmktracks.h @@ -0,0 +1,56 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_EBMLMKTRACKS_H +#define TAGLIB_EBMLMKTRACKS_H +#ifndef DO_NOT_DOCUMENT + +#include "ebmlmasterelement.h" +#include "ebmlutils.h" +#include "taglib.h" + +namespace TagLib { + namespace Matroska { + class Properties; + } + namespace EBML { + class MkTracks : public MasterElement + { + public: + MkTracks(int sizeLength, offset_t dataSize, offset_t offset) : + MasterElement(ElementIDs::MkTracks, sizeLength, dataSize, offset) + { + } + MkTracks() : + MasterElement(ElementIDs::MkTracks, 0, 0, 0) + { + } + void parse(Matroska::Properties *properties); + }; + } +} + +#endif +#endif diff --git a/taglib/matroska/matroskafile.cpp b/taglib/matroska/matroskafile.cpp index b0b02c60..7e9e19f5 100644 --- a/taglib/matroska/matroskafile.cpp +++ b/taglib/matroska/matroskafile.cpp @@ -44,6 +44,7 @@ public: delete tag; delete attachments; delete seekHead; + delete segment; } FilePrivate(const FilePrivate &) = delete; @@ -52,6 +53,7 @@ public: Attachments *attachments = nullptr; SeekHead *seekHead = nullptr; Segment *segment = nullptr; + std::unique_ptr properties; }; //////////////////////////////////////////////////////////////////////////////// @@ -96,6 +98,11 @@ Matroska::File::File(IOStream *stream, bool readProperties, Matroska::File::~File() = default; +AudioProperties *Matroska::File::audioProperties() const +{ + return d->properties.get(); +} + Tag *Matroska::File::tag() const { return tag(true); @@ -123,7 +130,7 @@ Matroska::Attachments *Matroska::File::attachments(bool create) const } } -void Matroska::File::read(bool, Properties::ReadStyle) +void Matroska::File::read(bool readProperties, Properties::ReadStyle) { offset_t fileLength = length(); @@ -162,6 +169,12 @@ void Matroska::File::read(bool, Properties::ReadStyle) d->attachments = segment->parseAttachments(); setValid(true); + + if(readProperties) { + d->properties = std::make_unique(this); + segment->parseInfo(d->properties.get()); + segment->parseTracks(d->properties.get()); + } } bool Matroska::File::save() diff --git a/taglib/matroska/matroskafile.h b/taglib/matroska/matroskafile.h index f126fb61..cb9088b9 100644 --- a/taglib/matroska/matroskafile.h +++ b/taglib/matroska/matroskafile.h @@ -40,7 +40,7 @@ namespace TagLib::Matroska { ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; - AudioProperties *audioProperties() const override { return nullptr; } + AudioProperties *audioProperties() const override; TagLib::Tag *tag() const override; Attachments *attachments(bool create = false) const; Tag *tag(bool create) const; @@ -60,6 +60,7 @@ namespace TagLib::Matroska { private: void read(bool readProperties, Properties::ReadStyle readStyle); class FilePrivate; + friend class Properties; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; }; diff --git a/taglib/matroska/matroskaproperties.cpp b/taglib/matroska/matroskaproperties.cpp index 8827db36..9bb9d0fc 100644 --- a/taglib/matroska/matroskaproperties.cpp +++ b/taglib/matroska/matroskaproperties.cpp @@ -25,23 +25,36 @@ #include "matroskaproperties.h" +#include "matroskafile.h" + using namespace TagLib; class Matroska::Properties::PropertiesPrivate { public: + explicit PropertiesPrivate(File *file) : file(file) {} + ~PropertiesPrivate() = default; + + PropertiesPrivate(const PropertiesPrivate &) = delete; + PropertiesPrivate &operator=(const PropertiesPrivate &) = delete; + + File *file; + String codecName; int length { 0 }; - int bitrate { 0 }; + int bitrate { -1 }; int sampleRate { 0 }; - int bitsPerSample { 0 }; int channels { 0 }; + int bitsPerSample { 0 }; }; +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + Matroska::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style), - d(std::make_unique()) + d(std::make_unique(file)) { - read(file); } Matroska::Properties::~Properties() = default; @@ -53,6 +66,9 @@ int Matroska::Properties::lengthInMilliseconds() const int Matroska::Properties::bitrate() const { + if (d->bitrate == -1) { + d->bitrate = d->length != 0 ? static_cast(d->file->length() * 8 / d->length) : 0; + } return d->bitrate; } @@ -66,11 +82,46 @@ int Matroska::Properties::channels() const return d->channels; } +int Matroska::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + +String Matroska::Properties::codecName() const +{ + return d->codecName; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// -void Matroska::Properties::read(File *) +void Matroska::Properties::setLengthInMilliseconds(int length) { - // TODO implement. + d->length = length; +} + +void Matroska::Properties::setBitrate(int bitrate) +{ + d->bitrate = bitrate; +} + +void Matroska::Properties::setSampleRate(int sampleRate) +{ + d->sampleRate = sampleRate; +} + +void Matroska::Properties::setChannels(int channels) +{ + d->channels = channels; +} + +void Matroska::Properties::setBitsPerSample(int bitsPerSample) +{ + d->bitsPerSample = bitsPerSample; +} + +void Matroska::Properties::setCodecName(const String &codecName) +{ + d->codecName = codecName; } diff --git a/taglib/matroska/matroskaproperties.h b/taglib/matroska/matroskaproperties.h index 7a4f36e4..805d3dbe 100644 --- a/taglib/matroska/matroskaproperties.h +++ b/taglib/matroska/matroskaproperties.h @@ -1,3 +1,8 @@ +/*************************************************************************** + copyright : (C) 2025 by Urs Fleisch + email : ufleisch@users.sourceforge.net + ***************************************************************************/ + /*************************************************************************** * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License version * @@ -24,15 +29,25 @@ #include "taglib_export.h" #include "audioproperties.h" +namespace TagLib::EBML { + class MkTracks; + class MkInfo; +} namespace TagLib::Matroska { - class File; //! An implementation of Matroska audio properties class TAGLIB_EXPORT Properties : public AudioProperties { public: + /*! + * Creates an instance of Matroska::Properties. + */ explicit Properties(File *file, ReadStyle style = Average); + + /*! + * Destroys this Matroska::Properties instance. + */ ~Properties() override; Properties(const Properties &) = delete; @@ -60,10 +75,29 @@ namespace TagLib::Matroska { */ int channels() const override; - private: - void read(File *file); + /*! + * Returns the number of bits per audio sample. + */ + int bitsPerSample() const; + /*! + * Returns the concrete codec name, for example "A_MPEG/L3" + * used in the file if available, otherwise an empty string. + */ + String codecName() const; + + private: class PropertiesPrivate; + friend class EBML::MkInfo; + friend class EBML::MkTracks; + + void setLengthInMilliseconds(int length); + void setBitrate(int bitrate); + void setSampleRate(int sampleRate); + void setChannels(int channels); + void setBitsPerSample(int bitsPerSample); + void setCodecName(const String &codecName); + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; };