diff --git a/bindings/c/CMakeLists.txt b/bindings/c/CMakeLists.txt index 34c8993c..d825fde8 100644 --- a/bindings/c/CMakeLists.txt +++ b/bindings/c/CMakeLists.txt @@ -63,6 +63,12 @@ if(WITH_SHORTEN) ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/shorten ) endif() +if(WITH_MATROSKA) + set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/matroska + ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/matroska/ebml + ) +endif() include_directories(${tag_c_HDR_DIRS}) set(tag_c_HDRS tag_c.h) diff --git a/bindings/c/tag_c.cpp b/bindings/c/tag_c.cpp index 35453a63..48afec7d 100644 --- a/bindings/c/tag_c.cpp +++ b/bindings/c/tag_c.cpp @@ -80,6 +80,9 @@ #ifdef TAGLIB_WITH_SHORTEN #include "shortenfile.h" #endif +#ifdef TAGLIB_WITH_MATROSKA +#include "matroskafile.h" +#endif using namespace TagLib; @@ -241,6 +244,11 @@ TagLib_File *taglib_file_new_type_any_char(const T *filename, TagLib_File_Type t case TagLib_File_SHORTEN: file = new Shorten::File(filename); break; +#endif +#ifdef TAGLIB_WITH_MATROSKA + case TagLib_File_MATROSKA: + file = new Matroska::File(filename); + break; #endif default: break; diff --git a/bindings/c/tag_c.h b/bindings/c/tag_c.h index 88c8d67b..59f7163e 100644 --- a/bindings/c/tag_c.h +++ b/bindings/c/tag_c.h @@ -132,7 +132,8 @@ typedef enum { TagLib_File_Opus, TagLib_File_DSF, TagLib_File_DSDIFF, - TagLib_File_SHORTEN + TagLib_File_SHORTEN, + TagLib_File_MATROSKA } TagLib_File_Type; /*! diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index 701f30a7..a8c3404f 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -296,6 +296,10 @@ namespace else if(Shorten::File::isSupported(stream)) file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle); #endif +#ifdef TAGLIB_WITH_MATROSKA + else if(Matroska::File::isSupported(stream)) + file = new Matroska::File(stream, readAudioProperties, audioPropertiesStyle); +#endif // isSupported() only does a quick check, so double check the file here. @@ -520,6 +524,11 @@ StringList FileRef::defaultFileExtensions() #ifdef TAGLIB_WITH_SHORTEN l.append("shn"); #endif +#ifdef TAGLIB_WITH_MATROSKA + l.append("mkv"); + l.append("mka"); + l.append("webm"); +#endif return l; } diff --git a/taglib/matroska/ebml/ebmlmksegment.h b/taglib/matroska/ebml/ebmlmksegment.h index 41f68983..f34df50c 100644 --- a/taglib/matroska/ebml/ebmlmksegment.h +++ b/taglib/matroska/ebml/ebmlmksegment.h @@ -51,11 +51,9 @@ namespace TagLib { Matroska::Segment* parseSegment(); private: - offset_t dataOffset = 0; MkTags *tags = nullptr; MkAttachments *attachments = nullptr; MkSeekHead *seekHead = nullptr; - }; } } diff --git a/taglib/matroska/ebml/ebmlstringelement.cpp b/taglib/matroska/ebml/ebmlstringelement.cpp index f7bb4430..7c272352 100644 --- a/taglib/matroska/ebml/ebmlstringelement.cpp +++ b/taglib/matroska/ebml/ebmlstringelement.cpp @@ -54,7 +54,7 @@ ByteVector EBML::StringElement::render() std::string string = value.to8Bit(t == String::UTF8); dataSize = string.size(); buffer.append(renderVINT(dataSize, 0)); - buffer.append(ByteVector(string.data(), dataSize)); + buffer.append(ByteVector(string.data(), static_cast(dataSize))); return buffer; } template ByteVector EBML::StringElement::render(); diff --git a/taglib/matroska/ebml/ebmlutils.cpp b/taglib/matroska/ebml/ebmlutils.cpp index cd41667c..5c288b8b 100644 --- a/taglib/matroska/ebml/ebmlutils.cpp +++ b/taglib/matroska/ebml/ebmlutils.cpp @@ -64,7 +64,7 @@ std::pair EBML::readVINT(File &file) if(nb_bytes > 1) buffer.append(file.readBlock(nb_bytes - 1)); - int bits_to_shift = (sizeof(T) * 8) - (7 * nb_bytes); + int bits_to_shift = static_cast(sizeof(T) * 8) - (7 * nb_bytes); offset_t mask = 0xFFFFFFFFFFFFFFFF >> bits_to_shift; return { nb_bytes, static_cast(buffer.toLongLong(true)) & mask }; } @@ -83,7 +83,7 @@ std::pair EBML::parseVINT(const ByteVector &buffer) if(!numBytes) return {0, 0}; - int bits_to_shift = (sizeof(T) * 8) - (7 * numBytes); + int bits_to_shift = static_cast(sizeof(T) * 8) - (7 * numBytes); offset_t mask = 0xFFFFFFFFFFFFFFFF >> bits_to_shift; return { numBytes, static_cast(buffer.toLongLong(true)) & mask }; } diff --git a/taglib/matroska/ebml/ebmlvoidelement.cpp b/taglib/matroska/ebml/ebmlvoidelement.cpp index 2eae2fed..9cb54f23 100644 --- a/taglib/matroska/ebml/ebmlvoidelement.cpp +++ b/taglib/matroska/ebml/ebmlvoidelement.cpp @@ -32,12 +32,12 @@ ByteVector EBML::VoidElement::render() offset_t bytesNeeded = targetSize; ByteVector buffer = renderId(); bytesNeeded -= buffer.size(); - sizeLength = std::min(bytesNeeded, static_cast(8)); + sizeLength = static_cast(std::min(bytesNeeded, static_cast(8))); bytesNeeded -= sizeLength; dataSize = bytesNeeded; buffer.append(renderVINT(dataSize, sizeLength)); if (dataSize) - buffer.append(ByteVector(dataSize, 0)); + buffer.append(ByteVector(static_cast(dataSize), 0)); return buffer; } diff --git a/taglib/matroska/matroskaattachedfile.h b/taglib/matroska/matroskaattachedfile.h index acb62839..142196b3 100644 --- a/taglib/matroska/matroskaattachedfile.h +++ b/taglib/matroska/matroskaattachedfile.h @@ -48,10 +48,10 @@ namespace TagLib { private: class AttachedFilePrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; - }; } } -#endif \ No newline at end of file +#endif diff --git a/taglib/matroska/matroskaattachments.h b/taglib/matroska/matroskaattachments.h index dde699a0..7f75bb74 100644 --- a/taglib/matroska/matroskaattachments.h +++ b/taglib/matroska/matroskaattachments.h @@ -55,10 +55,10 @@ namespace TagLib { friend class EBML::MkAttachments; friend class Matroska::File; class AttachmentsPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; - }; } } -#endif \ No newline at end of file +#endif diff --git a/taglib/matroska/matroskaelement.cpp b/taglib/matroska/matroskaelement.cpp index 5d406a38..76181429 100644 --- a/taglib/matroska/matroskaelement.cpp +++ b/taglib/matroska/matroskaelement.cpp @@ -141,7 +141,7 @@ bool Matroska::Element::sizeChanged(Element &caller, offset_t delta) return true; } -bool Matroska::Element::offsetChanged(Element &caller, offset_t delta) +bool Matroska::Element::offsetChanged(Element &, offset_t) { // Most elements don't need to handle this return true; diff --git a/taglib/matroska/matroskaelement.h b/taglib/matroska/matroskaelement.h index d8697931..ec3e7863 100644 --- a/taglib/matroska/matroskaelement.h +++ b/taglib/matroska/matroskaelement.h @@ -64,8 +64,8 @@ namespace TagLib { private: class ElementPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr e; - }; namespace ElementIDs { inline constexpr Element::ID MkTags = 0x1254C367; diff --git a/taglib/matroska/matroskafile.cpp b/taglib/matroska/matroskafile.cpp index dc9f2f6a..eabc96e4 100644 --- a/taglib/matroska/matroskafile.cpp +++ b/taglib/matroska/matroskafile.cpp @@ -55,7 +55,22 @@ public: Segment *segment = nullptr; }; -Matroska::File::File(FileName file, bool readProperties) +//////////////////////////////////////////////////////////////////////////////// +// static members +//////////////////////////////////////////////////////////////////////////////// + +bool Matroska::File::isSupported(IOStream *) +{ + // TODO implement + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +Matroska::File::File(FileName file, bool readProperties, + Properties::ReadStyle readStyle) : TagLib::File(file), d(std::make_unique()) { @@ -64,10 +79,11 @@ Matroska::File::File(FileName file, bool readProperties) setValid(false); return; } - read(readProperties); + read(readProperties, readStyle); } -Matroska::File::File(IOStream *stream, bool readProperties) +Matroska::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle readStyle) : TagLib::File(stream), d(std::make_unique()) { @@ -76,7 +92,7 @@ Matroska::File::File(IOStream *stream, bool readProperties) setValid(false); return; } - read(readProperties); + read(readProperties, readStyle); } Matroska::File::~File() = default; @@ -108,7 +124,7 @@ Matroska::Attachments* Matroska::File::attachments(bool create) const } } -void Matroska::File::read(bool readProperties) +void Matroska::File::read(bool, Properties::ReadStyle) { offset_t fileLength = length(); @@ -139,7 +155,7 @@ void Matroska::File::read(bool readProperties) setValid(false); return; } - + // Parse the elements d->segment = segment->parseSegment(); d->seekHead = segment->parseSeekHead(); diff --git a/taglib/matroska/matroskafile.h b/taglib/matroska/matroskafile.h index a8e99e19..b368412c 100644 --- a/taglib/matroska/matroskafile.h +++ b/taglib/matroska/matroskafile.h @@ -24,6 +24,7 @@ #include "taglib_export.h" #include "tfile.h" #include "tag.h" +#include "matroskaproperties.h" namespace TagLib { @@ -34,8 +35,10 @@ namespace TagLib { class TAGLIB_EXPORT File : public TagLib::File { public: - File(FileName file, bool readProperties = true); - File(IOStream *stream, bool readProperties = true); + File(FileName file, bool readProperties = true, + Properties::ReadStyle readStyle = Properties::Average); + File(IOStream *stream, bool readProperties = true, + Properties::ReadStyle readStyle = Properties::Average); ~File() override; File(const File &) = delete; File &operator=(const File &) = delete; @@ -46,19 +49,23 @@ namespace TagLib { bool save() override; //PropertyMap properties() const override { return PropertyMap(); } //void removeUnsupportedProperties(const StringList &properties) override { } - private: - void read(bool readProperties); - class FilePrivate; - std::unique_ptr d; + /*! + * Returns whether or not the given \a stream can be opened as a Matroska + * file. + * + * \note This method is designed to do a quick check. The result may + * not necessarily be correct. + */ + static bool isSupported(IOStream *stream); + + private: + void read(bool readProperties, Properties::ReadStyle readStyle); + class FilePrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE + std::unique_ptr d; }; } } - - - - - - - #endif \ No newline at end of file + #endif diff --git a/taglib/matroska/matroskaproperties.cpp b/taglib/matroska/matroskaproperties.cpp new file mode 100644 index 00000000..3712856e --- /dev/null +++ b/taglib/matroska/matroskaproperties.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + 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 "matroskaproperties.h" + +using namespace TagLib; + +class Matroska::Properties::PropertiesPrivate +{ +public: + int length { 0 }; + int bitrate { 0 }; + int sampleRate { 0 }; + int bitsPerSample { 0 }; + int channels { 0 }; +}; + +Matroska::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(std::make_unique()) +{ + read(file); +} + +Matroska::Properties::~Properties() = default; + +int Matroska::Properties::lengthInMilliseconds() const +{ + return d->length; +} + +int Matroska::Properties::bitrate() const +{ + return d->bitrate; +} + +int Matroska::Properties::sampleRate() const +{ + return d->sampleRate; +} + +int Matroska::Properties::channels() const +{ + return d->channels; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void Matroska::Properties::read(File *file) +{ + // TODO implement. +} diff --git a/taglib/matroska/matroskaproperties.h b/taglib/matroska/matroskaproperties.h index a3a735f3..33340be6 100644 --- a/taglib/matroska/matroskaproperties.h +++ b/taglib/matroska/matroskaproperties.h @@ -27,25 +27,17 @@ namespace TagLib { - namespace MPEG { + namespace Matroska { class File; - //! An implementation of audio property reading for MP3 - - /*! - * This reads the data from an MPEG Layer III stream found in the - * AudioProperties API. - */ - + //! An implementation of Matroska audio properties class TAGLIB_EXPORT Properties : public AudioProperties { public: - /*! - * Destroys this MPEG Properties instance. - */ - ~Properties() override {} + Properties(File *file, ReadStyle style = Average); + ~Properties() override; Properties(const Properties &) = delete; Properties &operator=(const Properties &) = delete; @@ -55,29 +47,31 @@ namespace TagLib { * * \see lengthInSeconds() */ - int lengthInMilliseconds() const override {} + int lengthInMilliseconds() const override; /*! * Returns the average bit rate of the file in kb/s. */ - int bitrate() const override {} + int bitrate() const override; /*! * Returns the sample rate in Hz. */ - int sampleRate() const override {} + int sampleRate() const override; /*! * Returns the number of audio channels. */ - int channels() const override {} + int channels() const override; private: + void read(File *file); class PropertiesPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; }; - } // namespace MPEG + } // namespace Matroska } // namespace TagLib #endif diff --git a/taglib/matroska/matroskasegment.cpp b/taglib/matroska/matroskasegment.cpp index f047e1b9..6afcac71 100644 --- a/taglib/matroska/matroskasegment.cpp +++ b/taglib/matroska/matroskasegment.cpp @@ -25,12 +25,12 @@ using namespace TagLib; bool Matroska::Segment::render() { - auto data = EBML::renderVINT(dataSize, sizeLength); + auto data = EBML::renderVINT(dataSize, static_cast(sizeLength)); if (data.size() != sizeLength) { sizeLength = 8; if (!emitSizeChanged(sizeLength - data.size())) return false; - data = EBML::renderVINT(dataSize, sizeLength); + data = EBML::renderVINT(dataSize, static_cast(sizeLength)); if (data.size() != sizeLength) return false; } @@ -38,8 +38,8 @@ bool Matroska::Segment::render() return true; } -bool Matroska::Segment::sizeChanged(Element &caller, offset_t delta) +bool Matroska::Segment::sizeChanged(Element &, offset_t delta) { dataSize += delta; return true; -} \ No newline at end of file +} diff --git a/taglib/matroska/matroskasimpletag.h b/taglib/matroska/matroskasimpletag.h index 74553a97..484decd5 100644 --- a/taglib/matroska/matroskasimpletag.h +++ b/taglib/matroska/matroskasimpletag.h @@ -54,6 +54,7 @@ namespace TagLib { private: class SimpleTagPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; protected: @@ -70,6 +71,7 @@ namespace TagLib { private: class SimpleTagStringPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr dd; }; @@ -83,6 +85,7 @@ namespace TagLib { private: class SimpleTagBinaryPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr dd; }; diff --git a/taglib/matroska/matroskatag.h b/taglib/matroska/matroskatag.h index 2370faec..5424010e 100644 --- a/taglib/matroska/matroskatag.h +++ b/taglib/matroska/matroskatag.h @@ -125,6 +125,7 @@ namespace TagLib { bool setTag(const String &key, const String &value); const String* getTag(const String &key) const; class TagPrivate; + TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE std::unique_ptr d; }; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 25c6e1da..2d4a0c5d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -65,6 +65,11 @@ IF(WITH_SHORTEN) ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/shorten ) ENDIF() +IF(WITH_MATROSKA) + SET(test_HDR_DIRS ${test_HDR_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/matroska + ) +ENDIF() INCLUDE_DIRECTORIES(${test_HDR_DIRS}) SET(test_runner_SRCS @@ -152,6 +157,11 @@ IF(WITH_SHORTEN) test_shorten.cpp ) ENDIF() +IF(WITH_MATROSKA) + SET(test_runner_SRCS ${test_runner_SRCS} + test_matroska.cpp + ) +ENDIF() IF(BUILD_BINDINGS) SET(test_runner_SRCS ${test_runner_SRCS} test_tag_c.cpp diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index 5c3e4e0d..34adfd78 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -472,6 +472,11 @@ public: #endif #ifdef TAGLIB_WITH_SHORTEN CPPUNIT_ASSERT(extensions.contains("shn")); +#endif +#ifdef TAGLIB_WITH_MATROSKA + CPPUNIT_ASSERT(extensions.contains("mkv")); + CPPUNIT_ASSERT(extensions.contains("mka")); + CPPUNIT_ASSERT(extensions.contains("webm")); #endif } diff --git a/tests/test_matroska.cpp b/tests/test_matroska.cpp new file mode 100644 index 00000000..d1fc3c0a --- /dev/null +++ b/tests/test_matroska.cpp @@ -0,0 +1,28 @@ +#include +#include + +#include "tbytevectorlist.h" +#include "tpropertymap.h" +#include "tag.h" +#include "matroskafile.h" +#include "plainfile.h" +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class TestMatroska : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestMatroska); + CPPUNIT_TEST(testTags); + CPPUNIT_TEST_SUITE_END(); + +public: + void testTags() + { + // TODO implement + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMatroska); diff --git a/tests/test_sizes.cpp b/tests/test_sizes.cpp index 0bf03cce..9bf0ee93 100644 --- a/tests/test_sizes.cpp +++ b/tests/test_sizes.cpp @@ -148,6 +148,11 @@ #include "trueaudiofile.h" #include "trueaudioproperties.h" #endif +#ifdef TAGLIB_WITH_MATROSKA +#include "matroskafile.h" +#include "matroskaproperties.h" +#include "matroskatag.h" +#endif #include @@ -297,6 +302,12 @@ public: #ifdef TAGLIB_WITH_TRUEAUDIO CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::TrueAudio::File)); CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::TrueAudio::Properties)); +#endif +#ifdef TAGLIB_WITH_MATROSKA + CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Matroska::File)); + CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Matroska::Properties)); + // TODO move non pimpl fields into private class. + // CPPUNIT_ASSERT_EQUAL(classSize(1, true), sizeof(TagLib::Matroska::Tag)); #endif }