mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 14:59:24 -04:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78298769de | ||
|
|
c43d2b3fc1 | ||
|
|
3db0d48f4b | ||
|
|
de6e2b15c8 | ||
|
|
b7f0225f0d | ||
|
|
f4117f873c | ||
|
|
cf86f20b36 | ||
|
|
74d93db166 | ||
|
|
397b6c1de3 | ||
|
|
51f431c96a | ||
|
|
11e3eb05bd | ||
|
|
2c01b63433 | ||
|
|
d83d751443 | ||
|
|
f4e7a742c3 | ||
|
|
68a514f4a0 | ||
|
|
607cd5bdf4 | ||
|
|
b625be5690 | ||
|
|
d2bf7e72d2 | ||
|
|
280d6306d2 | ||
|
|
aef78068b3 | ||
|
|
81815e1091 | ||
|
|
b9122afaca | ||
|
|
1d3a375765 | ||
|
|
241b3b2921 | ||
|
|
48104959b2 | ||
|
|
c817cc0a47 | ||
|
|
7a5a10102e | ||
|
|
d47d28f0f8 | ||
|
|
3566b00596 | ||
|
|
98bc68d16e | ||
|
|
8d02484804 | ||
|
|
cfade6d082 | ||
|
|
f7ab162514 | ||
|
|
5a6f1f96f8 | ||
|
|
6fa3db1e51 | ||
|
|
4546c00417 | ||
|
|
f94843614f | ||
|
|
975eaac3ca | ||
|
|
6d019a894c | ||
|
|
6342f00e8b | ||
|
|
b4e79a4a27 | ||
|
|
80837485cf | ||
|
|
47e9b9a17c | ||
|
|
8c7d3368da | ||
|
|
b4a4b42254 | ||
|
|
70c6a0c75f | ||
|
|
c67e434939 | ||
|
|
5d44f3eeac | ||
|
|
9c042984d2 | ||
|
|
49be371caa | ||
|
|
cbb3de6836 | ||
|
|
11efd2dd10 | ||
|
|
ab31d11c8d | ||
|
|
3c917caf52 | ||
|
|
fa96a6458e | ||
|
|
e831f0929f | ||
|
|
7d86716194 | ||
|
|
eb749ac55b | ||
|
|
148cc9a921 | ||
|
|
88d6b18b4f | ||
|
|
d61a333f27 | ||
|
|
e73517d058 | ||
|
|
6563ceaafa | ||
|
|
c57431e903 | ||
|
|
42dcfec86b | ||
|
|
d48f02030d | ||
|
|
3ccc390155 | ||
|
|
fbbead3efd | ||
|
|
ee1931b811 | ||
|
|
648f5e5882 | ||
|
|
c1c60ebeea | ||
|
|
3bc0ea0ecb | ||
|
|
225c73e181 | ||
|
|
90f62a3c94 | ||
|
|
5b6f9ef848 | ||
|
|
e3de03501f | ||
|
|
1bd0d711ca | ||
|
|
7c85fcaa81 | ||
|
|
cbe54d2f40 | ||
|
|
c4ed590032 | ||
|
|
f3fb4d83a4 | ||
|
|
3d4428726e | ||
|
|
ebf4c5bbb1 | ||
|
|
20cec27ac0 | ||
|
|
99bc87ccff | ||
|
|
7951f572b5 | ||
|
|
59ff35772e | ||
|
|
3784628155 | ||
|
|
e60df53152 | ||
|
|
1ae8e18db5 | ||
|
|
0896fb9092 | ||
|
|
920d97606b | ||
|
|
0d2c31b102 | ||
|
|
c8c4e5faec |
37
.github/workflows/build.yml
vendored
37
.github/workflows/build.yml
vendored
@@ -9,14 +9,18 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest, windows-2025]
|
||||
include:
|
||||
- os: windows-latest
|
||||
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
|
||||
- os: windows-2025
|
||||
cmake_extra_args: '-G "MinGW Makefiles" -DCPPUNIT_INCLUDE_DIR="$env:GITHUB_WORKSPACE/pkg/usr/local/include" -DCPPUNIT_LIBRARIES="$($env:GITHUB_WORKSPACE -replace "\\", "/")/pkg/usr/local/lib/libcppunit.dll.a" -DCPPUNIT_INSTALLED_VERSION="1.15.1"'
|
||||
# The windows-2025 runner is used for MinGW.
|
||||
name: ${{ matrix.os == 'windows-2025' && 'mingw' || matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Ubuntu
|
||||
run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev
|
||||
@@ -32,6 +36,24 @@ jobs:
|
||||
run: vcpkg install cppunit utfcpp zlib --triplet x64-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Set up MinGW
|
||||
shell: bash
|
||||
run: |
|
||||
# Fetch utf8cpp, build cppunit
|
||||
git submodule update --init
|
||||
# Fails with fatal error: cppunit/config-auto.h: No such file or directory
|
||||
# vcpkg install cppunit utfcpp zlib --triplet x64-mingw-dynamic
|
||||
# Probably not working with CMake and MinGW, so we have to build it ourselves.
|
||||
curl -sL "https://dev-www.libreoffice.org/src/cppunit-1.15.1.tar.gz" | tar xz
|
||||
mkdir -p build_cppunit
|
||||
(cd build_cppunit && MAKE=mingw32-make ../cppunit-1.15.1/configure \
|
||||
--enable-shared=yes --enable-static=no \
|
||||
--disable-dependency-tracking --disable-doxygen)
|
||||
find build_cppunit -name Makefile -exec sed -i 's/\($[({]SHELL[)}]\)/"\1"/' {} \;
|
||||
sed -i 's/^\(SUBDIRS.*\) examples\(.*\)$/\1\2/' build_cppunit/Makefile
|
||||
(cd build_cppunit && mingw32-make -j$(nproc) install DESTDIR=$(cd .. && pwd)/pkg)
|
||||
if: matrix.os == 'windows-2025'
|
||||
|
||||
- name: Configure
|
||||
run: >
|
||||
cmake -B${{github.workspace}}/build
|
||||
@@ -41,12 +63,12 @@ jobs:
|
||||
${{ matrix.cmake_extra_args }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os != 'windows-latest'
|
||||
if: matrix.os != 'windows-latest' && matrix.os != 'windows-2025'
|
||||
|
||||
- name: Test Windows
|
||||
working-directory: ${{github.workspace}}/build
|
||||
@@ -57,3 +79,10 @@ jobs:
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\zlib_x64-windows\bin"
|
||||
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Test MinGW
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: |
|
||||
$env:Path += ";$PWD/taglib;$PWD/bindings/c;${{github.workspace}}/pkg/usr/local/bin"
|
||||
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os == 'windows-2025'
|
||||
|
||||
65
CHANGELOG.md
65
CHANGELOG.md
@@ -1,8 +1,69 @@
|
||||
TagLib 2.2.1 (Mar 7, 2026)
|
||||
==========================
|
||||
|
||||
* Support edition, chapter and attachment UIDs in Matroska simple tags.
|
||||
* Avoid duplicates in Matroska complex property keys.
|
||||
|
||||
TagLib 2.2 (Feb 18, 2026)
|
||||
=========================
|
||||
|
||||
* Support for Matroska (MKA, MKV) and WebM files.
|
||||
* Support for NI STEM in MP4 files.
|
||||
* New method isDsd() in WavPack Properties.
|
||||
* Stricter verification of ID3v2 frames.
|
||||
* Fix setting the last header flag in Ogg FLAC files.
|
||||
* Fix reading of the last page in Ogg streams.
|
||||
* Avoid corrupting invalid Ogg FLAC files without Vorbis comment.
|
||||
* Windows: Support MP4 files with 64-bit atoms.
|
||||
* Fix use of property keys with non-ASCII characters in C bindings.
|
||||
* Fix building with Android NDK 29.
|
||||
|
||||
TagLib 2.1.1 (June 30, 2025)
|
||||
============================
|
||||
|
||||
* Map ID3v2.3 IPLS frames to both ID3v2.4 TIPL and TMCL to have a consistent
|
||||
behavior when using MusicBrainz tags with the property map interface.
|
||||
* Fix missing include for `wchar_t` when using C bindings with MinGW.
|
||||
|
||||
TagLib 2.1 (May 31, 2025)
|
||||
=========================
|
||||
|
||||
* Support for Shorten (SHN) files.
|
||||
* Compile time configuration of supported formats: WITH_APE, WITH_ASF, ...
|
||||
* Compile time configuration of data and temporary directories for unit tests:
|
||||
TESTS_DIR and TESTS_TMPDIR.
|
||||
* C bindings: Added taglib_file_new_wchar() and taglib_file_new_type_wchar().
|
||||
* Preserve unicode encoding when downgrading to ID3v2.3.
|
||||
* Do not store FLAC metadata blocks which are too large.
|
||||
* Fix segfaults with String and ByteVector nullptr arguments.
|
||||
|
||||
TagLib 2.0.2 (Aug 24, 2024)
|
||||
===========================
|
||||
|
||||
* Fix parsing of ID3v2.2 frames.
|
||||
* Tolerate MP4 files with unknown atom types as generated by Android tools.
|
||||
* Support setting properties with arbitrary names in MP4 tags.
|
||||
* Windows: Fix "-p" option in tagwriter example.
|
||||
* Support building with older utfcpp versions.
|
||||
|
||||
TagLib 2.0.1 (Apr 9, 2024)
|
||||
==========================
|
||||
|
||||
* Fix aborting when _GLIBCXX_ASSERTIONS are enabled.
|
||||
* Fall back to utf8cpp header detection in the case that its CMake
|
||||
configuration is removed.
|
||||
* Improve compatibility with the SWIG interface compiler.
|
||||
* Build system fixes for testing without bindings, Emscripten and Illumos.
|
||||
* C bindings: Fix setting UTF-8 encoded property values.
|
||||
* Windows: Fix opening long paths.
|
||||
|
||||
TagLib 2.0 (Jan 24, 2024)
|
||||
=========================
|
||||
|
||||
* New major version, binary incompatible, but source-compatible with the
|
||||
latest 1.x release if no deprecated features are used.
|
||||
* New major version, binary incompatible, but mostly source-compatible
|
||||
with the latest 1.x release if no deprecated features are used.
|
||||
Simple applications should build without changes, more complex
|
||||
applications (e.g. extending classes of TagLib) will have to be adapted.
|
||||
* Requires a C++17 compiler and uses features of C++17.
|
||||
* Major code cleanup, fixed warnings issued by compilers and static analyzers.
|
||||
* Made methods virtual which should have been virtual but could not be
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
|
||||
cmake_minimum_required(VERSION 3.10...3.31)
|
||||
|
||||
project(taglib)
|
||||
|
||||
@@ -12,16 +12,13 @@ include(CMakePackageConfigHelpers)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
option(BUILD_FRAMEWORK "Build a macOS framework" OFF)
|
||||
if(BUILD_FRAMEWORK)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
#set(CMAKE_MACOSX_RPATH 1)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif()
|
||||
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
|
||||
|
||||
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
|
||||
@@ -48,7 +45,10 @@ set(TAGLIB_INSTALL_SUFFIX "" CACHE STRING
|
||||
"Suffix added to installed files (include directory, libraries, .pc)")
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/" CACHE STRING
|
||||
"Tests directory, is path to unit test data when 'data' is appended")
|
||||
set(TESTS_TMPDIR "" CACHE STRING
|
||||
"Directory for temporary files created during unit tests, system tmpdir is used if undefined")
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
@@ -92,11 +92,22 @@ endif()
|
||||
# Minor version: increase it if you add ABI compatible features.
|
||||
# Patch version: increase it for bug fix releases.
|
||||
set(TAGLIB_SOVERSION_MAJOR 2)
|
||||
set(TAGLIB_SOVERSION_MINOR 0)
|
||||
set(TAGLIB_SOVERSION_PATCH 0)
|
||||
set(TAGLIB_SOVERSION_MINOR 2)
|
||||
set(TAGLIB_SOVERSION_PATCH 1)
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
option(WITH_APE "Build with APE, MPC, WavPack" ON)
|
||||
option(WITH_ASF "Build with ASF" ON)
|
||||
option(WITH_DSF "Build with DSF" ON)
|
||||
option(WITH_MATROSKA "Build with Matroska" ON)
|
||||
option(WITH_MOD "Build with Tracker modules" ON)
|
||||
option(WITH_MP4 "Build with MP4" ON)
|
||||
option(WITH_RIFF "Build with AIFF, RIFF, WAV" ON)
|
||||
option(WITH_SHORTEN "Build with Shorten" ON)
|
||||
option(WITH_TRUEAUDIO "Build with TrueAudio" ON)
|
||||
option(WITH_VORBIS "Build with Vorbis, FLAC, Ogg, Opus" ON)
|
||||
|
||||
# Determine whether zlib is installed.
|
||||
option(WITH_ZLIB "Build with ZLIB" ON)
|
||||
|
||||
@@ -149,20 +160,68 @@ if(TRACE_IN_RELEASE)
|
||||
endif()
|
||||
|
||||
find_package(utf8cpp QUIET)
|
||||
if(NOT utf8cpp_FOUND)
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt)
|
||||
add_subdirectory("3rdparty/utfcpp")
|
||||
message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) "
|
||||
"or fetch the git submodule using\n"
|
||||
"git submodule update --init")
|
||||
endif()
|
||||
else()
|
||||
if(utf8cpp_FOUND)
|
||||
message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}")
|
||||
else()
|
||||
find_path(utf8cpp_INCLUDE_DIR NAMES utf8.h PATH_SUFFIXES utf8cpp
|
||||
DOC "utf8cpp include directory")
|
||||
mark_as_advanced(utf8cpp_INCLUDE_DIR)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(utf8cpp REQUIRED_VARS utf8cpp_INCLUDE_DIR)
|
||||
if(utf8cpp_FOUND)
|
||||
set(utf8cpp_INCLUDE_DIRS "${utf8cpp_INCLUDE_DIR}")
|
||||
if(NOT TARGET utf8::cpp)
|
||||
add_library(utf8::cpp INTERFACE IMPORTED)
|
||||
set_target_properties(utf8::cpp PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${utf8cpp_INCLUDE_DIR}")
|
||||
endif()
|
||||
message(STATUS "Using utfcpp from ${utf8cpp_INCLUDE_DIR}")
|
||||
else()
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt)
|
||||
add_subdirectory("3rdparty/utfcpp")
|
||||
message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) "
|
||||
"or fetch the git submodule using\n"
|
||||
"git submodule update --init")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_APE)
|
||||
set(TAGLIB_WITH_APE TRUE)
|
||||
endif()
|
||||
if(WITH_ASF)
|
||||
set(TAGLIB_WITH_ASF TRUE)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(TAGLIB_WITH_DSF TRUE)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(TAGLIB_WITH_MATROSKA TRUE)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(TAGLIB_WITH_MOD TRUE)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(TAGLIB_WITH_RIFF TRUE)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(TAGLIB_WITH_SHORTEN TRUE)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(TAGLIB_WITH_TRUEAUDIO TRUE)
|
||||
endif()
|
||||
if(WITH_VORBIS)
|
||||
set(TAGLIB_WITH_VORBIS TRUE)
|
||||
endif()
|
||||
|
||||
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
|
||||
|
||||
add_subdirectory(taglib)
|
||||
|
||||
if(BUILD_BINDINGS)
|
||||
|
||||
@@ -20,7 +20,7 @@ if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
|
||||
endif()
|
||||
|
||||
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
||||
if(${SIZEOF_WCHAR_T} LESS 2)
|
||||
if(NOT ${SIZEOF_WCHAR_T} GREATER 1)
|
||||
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
||||
endif()
|
||||
|
||||
|
||||
79
INSTALL.md
79
INSTALL.md
@@ -42,6 +42,9 @@ CMakeLists.txt file.
|
||||
| `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... |
|
||||
| `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically |
|
||||
| `BUILD_FRAMEWORK` | Build a macOS framework |
|
||||
| `TESTS_DIR` | Where to find unit test data (with data appended) |
|
||||
| `TESTS_TMPDIR` | Where to create temporary files in unit tests |
|
||||
|
||||
|
||||
If you want to install TagLib 2 alongside TagLib 1, you can use
|
||||
`-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON`
|
||||
@@ -49,6 +52,31 @@ for both versions. The installed files will then include bin/taglib-2-config,
|
||||
include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc,
|
||||
pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2".
|
||||
|
||||
### Compile Time Configuration of Supported Formats
|
||||
|
||||
To reduce the size of the library, it is possible to switch off supported file
|
||||
formats. By default, all formats are enabled. Support for MPEG files (MP3, AAC)
|
||||
and ID3 tags cannot be disabled. The following CMake options are available:
|
||||
|
||||
| Option | Description |
|
||||
|-------------------------|----------------------------------------------------|
|
||||
| `WITH_APE` | Build with APE, MPC, WavPack (default ON) |
|
||||
| `WITH_ASF` | Build with ASF (default ON) |
|
||||
| `WITH_DSF` | Build with DSF (default ON) |
|
||||
| `WITH_MATROSKA` | Build with Matroska, WebM (default ON) |
|
||||
| `WITH_MOD` | Build with Tracker modules (default ON) |
|
||||
| `WITH_MP4` | Build with MP4 (default ON) |
|
||||
| `WITH_RIFF` | Build with AIFF, RIFF, WAV (default ON) |
|
||||
| `WITH_SHORTEN` | Build with Shorten (default ON) |
|
||||
| `WITH_TRUEAUDIO` | Build with TrueAudio (default ON) |
|
||||
| `WITH_VORBIS` | Build with FLAC, Ogg, Opus, Speex (default ON) |
|
||||
|
||||
Note that disabling formats will remove exported symbols from the library and
|
||||
thus break binary compatibility. These options should therefore only be used
|
||||
if the library is built specifically for a certain project. The public header
|
||||
files still contain the full API, if you use TagLib with a reduced set of
|
||||
formats, you can include taglib_config.h and use its definitions (prefixed with
|
||||
`TAGLIB_`, e.g. `TAGLIB_WITH_APE`), as it is done in examples/framelist.cpp.
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -438,3 +466,54 @@ cmake --build build_mingw --config Release
|
||||
PATH=$PATH:$TAGLIB_PREFIX/bin
|
||||
build_mingw/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
## Android
|
||||
|
||||
### Using vcpkg
|
||||
|
||||
The bash script below can be used to build TagLib for Android using vcpkg.
|
||||
It must be started in the parent folder of the taglib source folder and will
|
||||
build in a folder _android_build_ and install into _android_pkg_.
|
||||
The package and the unit tests are then transferred to an Android device
|
||||
and the unit tests are run on the device.
|
||||
|
||||
Note that `TESTS_TMPDIR` is set because there is no system-wide temporary folder
|
||||
on Android. `TESTS_DIR` is set to run the tests on the target.
|
||||
|
||||
```
|
||||
# You may have to adapt the NDK and vcpkg paths and the ABI/triplet.
|
||||
|
||||
export ANDROID_NDK_HOME=$HOME/Development/android-sdk/ndk/23.1.7779620
|
||||
export VCPKG_ROOT=$HOME/Development/vcpkg
|
||||
PATH=$PATH:$VCPKG_ROOT
|
||||
|
||||
# armeabi-v7a/arm-android or arm64-v8a/arm64-android or x86/x86-android or x86_64/x64-android
|
||||
android_abi=armeabi-v7a
|
||||
vcpkg_target_triplet=arm-android
|
||||
|
||||
vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
|
||||
android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
|
||||
|
||||
vcpkg install --triplet $vcpkg_target_triplet utfcpp zlib cppunit
|
||||
|
||||
cmake -B android_build -S taglib \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$vcpkg_toolchain_file \
|
||||
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$android_toolchain_file \
|
||||
-DVCPKG_TARGET_TRIPLET=$vcpkg_target_triplet \
|
||||
-DANDROID_ABI=$android_abi \
|
||||
-GNinja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
|
||||
-DVISIBILITY_HIDDEN=ON -DENABLE_CCACHE=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTING=ON \
|
||||
-DTESTS_DIR=/data/local/tmp/tests/ -DTESTS_TMPDIR=/data/local/tmp
|
||||
cmake --build android_build --config Release
|
||||
cmake --install android_build --config Release --prefix android_pkg --strip
|
||||
cp -a android_build/tests/test_runner android_pkg/bin/
|
||||
|
||||
if hash adb 2>/dev/null; then
|
||||
adb push android_pkg /data/local/tmp/
|
||||
adb push android_build/tests/test_runner /data/local/tmp/tests/test_runner
|
||||
adb push taglib/tests/data /data/local/tmp/tests/
|
||||
adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/tests/test_runner"
|
||||
# You could also try an example binary:
|
||||
# adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/android_pkg/bin/tagreader '/sdcard/Music/Some Album/A Track.mp3'"
|
||||
fi
|
||||
```
|
||||
|
||||
25
README.md
25
README.md
@@ -7,10 +7,11 @@
|
||||
https://taglib.org/
|
||||
|
||||
TagLib is a library for reading and editing the metadata of several
|
||||
popular audio formats. Currently, it supports both ID3v1 and [ID3v2][]
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
|
||||
and ASF files.
|
||||
popular audio formats. Currently, it supports various metadata containers such
|
||||
as [ID3v1][], [ID3v2][] and [Vorbis][] comments for MP3, MP4, AAC,
|
||||
[Ogg][], [Opus][], [FLAC][], [Speex][], [APE][], [MPC][], [WavPack][],
|
||||
[WAV][], [AIFF][], [TrueAudio][], [Matroska][], [WebM][], ASF, WMA, DSF, DFF and
|
||||
tracker (MOD, XM, S3M, IT) files.
|
||||
|
||||
TagLib is distributed under the [GNU Lesser General Public License][]
|
||||
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
|
||||
@@ -18,8 +19,20 @@ it may be used in proprietary applications, but if changes are made to
|
||||
TagLib they must be contributed back to the project. Please review the
|
||||
licenses if you are considering using TagLib in your project.
|
||||
|
||||
[ID3v2]: https://id3.org/
|
||||
[Ogg Vorbis]: https://xiph.org/vorbis/
|
||||
[ID3v1]: https://id3.org/ID3v1
|
||||
[ID3v2]: https://id3.org/Home
|
||||
[Vorbis]: https://xiph.org/vorbis/
|
||||
[Ogg]: https://www.xiph.org/ogg/
|
||||
[Opus]: https://opus-codec.org/
|
||||
[FLAC]: https://xiph.org/flac/
|
||||
[Speex]: https://www.speex.org/
|
||||
[APE]: https://www.monkeysaudio.com/
|
||||
[MPC]: https://musepack.net/
|
||||
[WavPack]: https://www.wavpack.com/
|
||||
[WAV]: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
|
||||
[AIFF]: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/AIFF.html
|
||||
[TrueAudio]: https://sourceforge.net/projects/tta/
|
||||
[Matroska]: https://www.matroska.org/
|
||||
[WebM]: https://www.webmproject.org/
|
||||
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html
|
||||
|
||||
@@ -1,31 +1,75 @@
|
||||
include_directories(
|
||||
set(tag_c_HDR_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
|
||||
)
|
||||
if(WITH_ASF)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
|
||||
)
|
||||
endif()
|
||||
if(WITH_VORBIS)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
|
||||
)
|
||||
endif()
|
||||
if(WITH_APE)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
|
||||
)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
|
||||
)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
|
||||
)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
|
||||
)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
|
||||
)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
|
||||
)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${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)
|
||||
|
||||
@@ -74,6 +118,9 @@ set_target_properties(tag_c PROPERTIES
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(tag_c PUBLIC TAGLIB_STATIC)
|
||||
endif()
|
||||
|
||||
if(TAGLIB_INSTALL_SUFFIX)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
|
||||
@@ -29,34 +29,60 @@
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "taglib_config.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevectorstream.h"
|
||||
#include "tiostream.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "tag.h"
|
||||
#include "id3v2framefactory.h"
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
#include "asffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
#include "vorbisfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "opusfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "mpcfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
#include "trueaudiofile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
#include "mp4file.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "apefile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "opusfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#include "tag.h"
|
||||
#include "id3v2framefactory.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
#include "shortenfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
#include "matroskafile.h"
|
||||
#endif
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -126,49 +152,73 @@ TagLib_File *taglib_file_new(const char *filename)
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
#ifdef _WIN32
|
||||
TagLib_File *taglib_file_new_wchar(const wchar_t *filename)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
TagLib_File *taglib_file_new_type_any_char(const T *filename, TagLib_File_Type type)
|
||||
{
|
||||
File *file = NULL;
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
file = new MPEG::File(filename);
|
||||
break;
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
case TagLib_File_OggVorbis:
|
||||
file = new Ogg::Vorbis::File(filename);
|
||||
break;
|
||||
case TagLib_File_FLAC:
|
||||
file = new FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_MPC:
|
||||
file = new MPC::File(filename);
|
||||
break;
|
||||
case TagLib_File_OggFlac:
|
||||
file = new Ogg::FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_WavPack:
|
||||
file = new WavPack::File(filename);
|
||||
break;
|
||||
case TagLib_File_Speex:
|
||||
file = new Ogg::Speex::File(filename);
|
||||
break;
|
||||
case TagLib_File_Opus:
|
||||
file = new Ogg::Opus::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
case TagLib_File_MPC:
|
||||
file = new MPC::File(filename);
|
||||
break;
|
||||
case TagLib_File_WavPack:
|
||||
file = new WavPack::File(filename);
|
||||
break;
|
||||
case TagLib_File_APE:
|
||||
file = new APE::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
case TagLib_File_TrueAudio:
|
||||
file = new TrueAudio::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
case TagLib_File_MP4:
|
||||
file = new MP4::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
case TagLib_File_ASF:
|
||||
file = new ASF::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
case TagLib_File_AIFF:
|
||||
file = new RIFF::AIFF::File(filename);
|
||||
break;
|
||||
case TagLib_File_WAV:
|
||||
file = new RIFF::WAV::File(filename);
|
||||
break;
|
||||
case TagLib_File_APE:
|
||||
file = new APE::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
case TagLib_File_IT:
|
||||
file = new IT::File(filename);
|
||||
break;
|
||||
@@ -181,21 +231,43 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
case TagLib_File_XM:
|
||||
file = new XM::File(filename);
|
||||
break;
|
||||
case TagLib_File_Opus:
|
||||
file = new Ogg::Opus::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
case TagLib_File_DSF:
|
||||
file = new DSF::File(filename);
|
||||
break;
|
||||
case TagLib_File_DSDIFF:
|
||||
file = new DSDIFF::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
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;
|
||||
}
|
||||
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
return taglib_file_new_type_any_char(filename, type);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type)
|
||||
{
|
||||
return taglib_file_new_type_any_char(filename, type);
|
||||
}
|
||||
#endif
|
||||
|
||||
TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(
|
||||
@@ -405,24 +477,25 @@ void _taglib_property_set(TagLib_File *file, const char* prop, const char* value
|
||||
return;
|
||||
|
||||
auto tfile = reinterpret_cast<FileRef *>(file);
|
||||
auto propStr = charArrayToString(prop);
|
||||
PropertyMap map = tfile->tag()->properties();
|
||||
|
||||
if(value) {
|
||||
auto property = map.find(prop);
|
||||
auto property = map.find(propStr);
|
||||
if(property == map.end()) {
|
||||
map.insert(prop, StringList(value));
|
||||
map.insert(propStr, StringList(charArrayToString(value)));
|
||||
}
|
||||
else {
|
||||
if(append) {
|
||||
property->second.append(value);
|
||||
property->second.append(charArrayToString(value));
|
||||
}
|
||||
else {
|
||||
property->second = StringList(value);
|
||||
property->second = StringList(charArrayToString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.erase(prop);
|
||||
map.erase(propStr);
|
||||
}
|
||||
|
||||
tfile->setProperties(map);
|
||||
@@ -467,7 +540,7 @@ char **taglib_property_get(const TagLib_File *file, const char *prop)
|
||||
|
||||
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
|
||||
|
||||
auto property = map.find(prop);
|
||||
auto property = map.find(charArrayToString(prop));
|
||||
if(property == map.end())
|
||||
return NULL;
|
||||
|
||||
@@ -509,16 +582,17 @@ bool _taglib_complex_property_set(
|
||||
return false;
|
||||
|
||||
auto tfile = reinterpret_cast<FileRef *>(file);
|
||||
auto keyStr = charArrayToString(key);
|
||||
|
||||
if(value == NULL) {
|
||||
return tfile->setComplexProperties(key, {});
|
||||
return tfile->setComplexProperties(keyStr, {});
|
||||
}
|
||||
|
||||
VariantMap map;
|
||||
const TagLib_Complex_Property_Attribute** attrPtr = value;
|
||||
while(*attrPtr) {
|
||||
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
String attrKey(attr->key);
|
||||
auto attrKey = charArrayToString(attr->key);
|
||||
switch(attr->value.type) {
|
||||
case TagLib_Variant_Void:
|
||||
map.insert(attrKey, Variant());
|
||||
@@ -542,14 +616,14 @@ bool _taglib_complex_property_set(
|
||||
map.insert(attrKey, attr->value.value.doubleValue);
|
||||
break;
|
||||
case TagLib_Variant_String:
|
||||
map.insert(attrKey, attr->value.value.stringValue);
|
||||
map.insert(attrKey, charArrayToString(attr->value.value.stringValue));
|
||||
break;
|
||||
case TagLib_Variant_StringList: {
|
||||
StringList strs;
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **s = attr->value.value.stringListValue;;
|
||||
while(*s) {
|
||||
strs.append(*s++);
|
||||
strs.append(charArrayToString(*s++));
|
||||
}
|
||||
}
|
||||
map.insert(attrKey, strs);
|
||||
@@ -563,8 +637,8 @@ bool _taglib_complex_property_set(
|
||||
++attrPtr;
|
||||
}
|
||||
|
||||
return append ? tfile->setComplexProperties(key, tfile->complexProperties(key).append(map))
|
||||
: tfile->setComplexProperties(key, {map});
|
||||
return append ? tfile->setComplexProperties(keyStr, tfile->complexProperties(keyStr).append(map))
|
||||
: tfile->setComplexProperties(keyStr, {map});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -612,7 +686,7 @@ TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(key);
|
||||
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(charArrayToString(key));
|
||||
if(variantMaps.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ extern "C" {
|
||||
#define TAGLIB_C_EXPORT
|
||||
#endif
|
||||
|
||||
#include <wchar.h>
|
||||
#ifdef _MSC_VER
|
||||
/* minwindef.h contains typedef int BOOL */
|
||||
#include <windows.h>
|
||||
@@ -130,7 +131,9 @@ typedef enum {
|
||||
TagLib_File_XM,
|
||||
TagLib_File_Opus,
|
||||
TagLib_File_DSF,
|
||||
TagLib_File_DSDIFF
|
||||
TagLib_File_DSDIFF,
|
||||
TagLib_File_SHORTEN,
|
||||
TagLib_File_MATROSKA
|
||||
} TagLib_File_Type;
|
||||
|
||||
/*!
|
||||
@@ -141,12 +144,18 @@ typedef enum {
|
||||
* be opened.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
|
||||
#ifdef _WIN32
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_wchar(const wchar_t *filename);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Creates a TagLib file based on \a filename. Rather than attempting to guess
|
||||
* the type, it will use the one specified by \a type.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
|
||||
#ifdef _WIN32
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Creates a TagLib file from a \a stream.
|
||||
|
||||
@@ -20,5 +20,6 @@
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
#cmakedefine TESTS_TMPDIR "@TESTS_TMPDIR@"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/matroska
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
@@ -38,6 +39,18 @@ target_link_libraries(framelist tag)
|
||||
add_executable(strip-id3v1 strip-id3v1.cpp)
|
||||
target_link_libraries(strip-id3v1 tag)
|
||||
|
||||
if(WITH_MATROSKA)
|
||||
########### next target ###############
|
||||
|
||||
add_executable(matroskareader matroskareader.cpp)
|
||||
target_link_libraries(matroskareader tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
add_executable(matroskawriter matroskawriter.cpp)
|
||||
target_link_libraries(matroskawriter tag)
|
||||
endif()
|
||||
|
||||
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "taglib_config.h"
|
||||
#include "tbytevector.h"
|
||||
#include "mpegfile.h"
|
||||
#include "id3v2tag.h"
|
||||
@@ -32,9 +33,10 @@
|
||||
#include "id3v2header.h"
|
||||
#include "commentsframe.h"
|
||||
#include "id3v1tag.h"
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "apetag.h"
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -44,7 +46,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
|
||||
cout << "******************** \"" << argv[i] << "\"********************" << endl;
|
||||
std::cout << "******************** \"" << argv[i] << "\"********************" << std::endl;
|
||||
|
||||
MPEG::File f(argv[i]);
|
||||
|
||||
@@ -52,62 +54,64 @@ int main(int argc, char *argv[])
|
||||
|
||||
if(id3v2tag) {
|
||||
|
||||
cout << "ID3v2."
|
||||
std::cout << "ID3v2."
|
||||
<< id3v2tag->header()->majorVersion()
|
||||
<< "."
|
||||
<< id3v2tag->header()->revisionNumber()
|
||||
<< ", "
|
||||
<< id3v2tag->header()->tagSize()
|
||||
<< " bytes in tag"
|
||||
<< endl;
|
||||
<< std::endl;
|
||||
|
||||
const auto &frames = id3v2tag->frameList();
|
||||
for(auto it = frames.begin(); it != frames.end(); it++) {
|
||||
cout << (*it)->frameID();
|
||||
std::cout << (*it)->frameID();
|
||||
|
||||
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
cout << " [" << comment->description() << "]";
|
||||
std::cout << " [" << comment->description() << "]";
|
||||
|
||||
cout << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
std::cout << " - \"" << (*it)->toString() << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v2 tag" << endl;
|
||||
std::cout << "file does not have a valid id3v2 tag" << std::endl;
|
||||
|
||||
cout << endl << "ID3v1" << endl;
|
||||
std::cout << std::endl << "ID3v1" << std::endl;
|
||||
|
||||
ID3v1::Tag *id3v1tag = f.ID3v1Tag();
|
||||
|
||||
if(id3v1tag) {
|
||||
cout << "title - \"" << id3v1tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << id3v1tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << id3v1tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << id3v1tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << id3v1tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << id3v1tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << id3v1tag->genre() << "\"" << endl;
|
||||
std::cout << "title - \"" << id3v1tag->title() << "\"" << std::endl;
|
||||
std::cout << "artist - \"" << id3v1tag->artist() << "\"" << std::endl;
|
||||
std::cout << "album - \"" << id3v1tag->album() << "\"" << std::endl;
|
||||
std::cout << "year - \"" << id3v1tag->year() << "\"" << std::endl;
|
||||
std::cout << "comment - \"" << id3v1tag->comment() << "\"" << std::endl;
|
||||
std::cout << "track - \"" << id3v1tag->track() << "\"" << std::endl;
|
||||
std::cout << "genre - \"" << id3v1tag->genre() << "\"" << std::endl;
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v1 tag" << endl;
|
||||
std::cout << "file does not have a valid id3v1 tag" << std::endl;
|
||||
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
APE::Tag *ape = f.APETag();
|
||||
|
||||
cout << endl << "APE" << endl;
|
||||
std::cout << std::endl << "APE" << std::endl;
|
||||
|
||||
if(ape) {
|
||||
const auto &items = ape->itemListMap();
|
||||
for(auto it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
if((*it).second.type() != APE::Item::Binary)
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
std::cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << std::endl;
|
||||
else
|
||||
cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << endl;
|
||||
std::cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid APE tag" << endl;
|
||||
std::cout << "file does not have a valid APE tag" << std::endl;
|
||||
#endif
|
||||
|
||||
cout << endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
139
examples/matroskareader.cpp
Normal file
139
examples/matroskareader.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <cstdio>
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskasimpletag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "tstring.h"
|
||||
#include "tutils.h"
|
||||
#include "tbytevector.h"
|
||||
#define GREEN_TEXT(s) "[1;32m" s "[0m"
|
||||
#define PRINT_PRETTY(label, value) printf(" " GREEN_TEXT(label) ": %s\n", value)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 2) {
|
||||
printf("Usage: matroskareader FILE\n");
|
||||
return 1;
|
||||
}
|
||||
TagLib::Matroska::File file(argv[1]);
|
||||
if(!file.isValid()) {
|
||||
printf("File is not valid\n");
|
||||
return 1;
|
||||
}
|
||||
auto tag = dynamic_cast<TagLib::Matroska::Tag*>(file.tag());
|
||||
if(!tag) {
|
||||
printf("File has no tag\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TagLib::Matroska::SimpleTagsList &list = tag->simpleTagsList();
|
||||
printf("Found %u tag(s):\n", list.size());
|
||||
|
||||
for(const TagLib::Matroska::SimpleTag &t : list) {
|
||||
PRINT_PRETTY("Tag Name", t.name().toCString(true));
|
||||
|
||||
if(t.type() == TagLib::Matroska::SimpleTag::StringType)
|
||||
PRINT_PRETTY("Tag Value", t.toString().toCString(true));
|
||||
else if(t.type() == TagLib::Matroska::SimpleTag::BinaryType)
|
||||
PRINT_PRETTY("Tag Value",
|
||||
TagLib::Utils::formatString("Binary with size %i", t.toByteVector().size()).toCString(false)
|
||||
);
|
||||
|
||||
auto targetTypeValue = static_cast<unsigned int>(t.targetTypeValue());
|
||||
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)
|
||||
);
|
||||
}
|
||||
if(auto editionUid = t.editionUid()) {
|
||||
PRINT_PRETTY("Edition UID",
|
||||
TagLib::Utils::formatString("%llu",editionUid).toCString(false)
|
||||
);
|
||||
}
|
||||
if(auto chapterUid = t.chapterUid()) {
|
||||
PRINT_PRETTY("Chapter UID",
|
||||
TagLib::Utils::formatString("%llu",chapterUid).toCString(false)
|
||||
);
|
||||
}
|
||||
if(auto attachmentUid = t.attachmentUid()) {
|
||||
PRINT_PRETTY("Attachment UID",
|
||||
TagLib::Utils::formatString("%llu",attachmentUid).toCString(false)
|
||||
);
|
||||
}
|
||||
const TagLib::String &language = t.language();
|
||||
PRINT_PRETTY("Language", !language.isEmpty() ? language.toCString(false) : "Not set");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
TagLib::Matroska::Attachments *attachments = file.attachments();
|
||||
if(attachments) {
|
||||
const TagLib::Matroska::Attachments::AttachedFileList &list = attachments->attachedFileList();
|
||||
printf("Found %u attachment(s)\n", list.size());
|
||||
for(const auto &attachedFile : list) {
|
||||
PRINT_PRETTY("Filename", attachedFile.fileName().toCString(true));
|
||||
const TagLib::String &description = attachedFile.description();
|
||||
PRINT_PRETTY("Description", !description.isEmpty() ? description.toCString(true) : "None");
|
||||
const TagLib::String &mediaType = attachedFile.mediaType();
|
||||
PRINT_PRETTY("Media Type", !mediaType.isEmpty() ? mediaType.toCString(false) : "None");
|
||||
PRINT_PRETTY("Data Size",
|
||||
TagLib::Utils::formatString("%u byte(s)", attachedFile.data().size()).toCString(false)
|
||||
);
|
||||
PRINT_PRETTY("UID",
|
||||
TagLib::Utils::formatString("%llu", attachedFile.uid()).toCString(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("File has no attachments\n");
|
||||
|
||||
TagLib::Matroska::Chapters *chapters = file.chapters();
|
||||
if(chapters) {
|
||||
printf("Chapters:\n");
|
||||
const TagLib::Matroska::Chapters::ChapterEditionList &editions = chapters->chapterEditionList();
|
||||
for(const auto &edition : editions) {
|
||||
if(edition.uid()) {
|
||||
PRINT_PRETTY("Edition UID", TagLib::Utils::formatString("%llu", edition.uid())
|
||||
.toCString(false));
|
||||
}
|
||||
PRINT_PRETTY("Edition Flags", TagLib::Utils::formatString("default=%d, ordered=%d",
|
||||
edition.isDefault(), edition.isOrdered())
|
||||
.toCString(false));
|
||||
printf("\n");
|
||||
for(const auto &chapter : edition.chapterList()) {
|
||||
PRINT_PRETTY("Chapter UID", TagLib::Utils::formatString("%llu", chapter.uid())
|
||||
.toCString(false));
|
||||
PRINT_PRETTY("Chapter flags", TagLib::Utils::formatString("hidden=%d", chapter.isHidden())
|
||||
.toCString(false));
|
||||
PRINT_PRETTY("Start-End", TagLib::Utils::formatString("%llu-%llu",
|
||||
chapter.timeStart(), chapter.timeEnd()).toCString(false));
|
||||
for(const auto &display : chapter.displayList()) {
|
||||
PRINT_PRETTY("Display", display.string().toCString(false));
|
||||
PRINT_PRETTY("Language", !display.language().isEmpty()
|
||||
? display.language().toCString(false) : "Not set");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("File has no chapters\n");
|
||||
|
||||
if(auto properties = dynamic_cast<const TagLib::Matroska::Properties *>(file.audioProperties())) {
|
||||
printf("Properties:\n");
|
||||
PRINT_PRETTY("Doc Type", properties->docType().toCString(false));
|
||||
PRINT_PRETTY("Doc Type Version", TagLib::String::number(properties->docTypeVersion()).toCString(false));
|
||||
PRINT_PRETTY("Codec Name", properties->codecName().toCString(true));
|
||||
PRINT_PRETTY("Bitrate", TagLib::String::number(properties->bitrate()).toCString(false));
|
||||
PRINT_PRETTY("Sample Rate", TagLib::String::number(properties->sampleRate()).toCString(false));
|
||||
PRINT_PRETTY("Channels", TagLib::String::number(properties->channels()).toCString(false));
|
||||
PRINT_PRETTY("Length [ms]", TagLib::String::number(properties->lengthInMilliseconds()).toCString(false));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
45
examples/matroskawriter.cpp
Normal file
45
examples/matroskawriter.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <cstdio>
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskasimpletag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "tfilestream.h"
|
||||
#include "tstring.h"
|
||||
#include "tutils.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 3) {
|
||||
printf("Usage: matroskawriter FILE ARTWORK\n");
|
||||
return 1;
|
||||
}
|
||||
TagLib::Matroska::File file(argv[1]);
|
||||
if(!file.isValid()) {
|
||||
printf("File is not valid\n");
|
||||
return 1;
|
||||
}
|
||||
auto tag = file.tag(true);
|
||||
tag->clearSimpleTags();
|
||||
|
||||
tag->addSimpleTag(TagLib::Matroska::SimpleTag(
|
||||
"Test Name 1", TagLib::String("Test Value 1"),
|
||||
TagLib::Matroska::SimpleTag::TargetTypeValue::Track, "en"));
|
||||
|
||||
tag->addSimpleTag(TagLib::Matroska::SimpleTag(
|
||||
"Test Name 2", TagLib::String("Test Value 2"),
|
||||
TagLib::Matroska::SimpleTag::TargetTypeValue::Album));
|
||||
tag->setTitle("Test title");
|
||||
tag->setArtist("Test artist");
|
||||
tag->setYear(1969);
|
||||
|
||||
TagLib::FileStream image(argv[2]);
|
||||
auto data = image.readBlock(image.length());
|
||||
auto attachments = file.attachments(true);
|
||||
attachments->addAttachedFile(TagLib::Matroska::AttachedFile(
|
||||
data, "cover.jpg", "image/jpeg"));
|
||||
|
||||
file.save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -32,13 +32,11 @@
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
for(int i = 1; i < argc; i++) {
|
||||
|
||||
cout << "******************** \"" << argv[i] << "\" ********************" << endl;
|
||||
std::cout << "******************** \"" << argv[i] << "\" ********************" << std::endl;
|
||||
|
||||
TagLib::FileRef f(argv[i]);
|
||||
|
||||
@@ -46,14 +44,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
TagLib::Tag *tag = f.tag();
|
||||
|
||||
cout << "-- TAG (basic) --" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
std::cout << "-- TAG (basic) --" << std::endl;
|
||||
std::cout << "title - \"" << tag->title() << "\"" << std::endl;
|
||||
std::cout << "artist - \"" << tag->artist() << "\"" << std::endl;
|
||||
std::cout << "album - \"" << tag->album() << "\"" << std::endl;
|
||||
std::cout << "year - \"" << tag->year() << "\"" << std::endl;
|
||||
std::cout << "comment - \"" << tag->comment() << "\"" << std::endl;
|
||||
std::cout << "track - \"" << tag->track() << "\"" << std::endl;
|
||||
std::cout << "genre - \"" << tag->genre() << "\"" << std::endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.properties();
|
||||
if(!tags.isEmpty()) {
|
||||
@@ -64,10 +62,10 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
std::cout << "-- TAG (properties) --" << std::endl;
|
||||
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
|
||||
for(auto k = j->second.begin(); k != j->second.end(); ++k) {
|
||||
cout << left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << endl;
|
||||
std::cout << std::left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,13 +74,13 @@ int main(int argc, char *argv[])
|
||||
for(const auto &name : names) {
|
||||
const auto& properties = f.complexProperties(name);
|
||||
for(const auto &property : properties) {
|
||||
cout << name << ":" << endl;
|
||||
std::cout << name << ":" << std::endl;
|
||||
for(const auto &[key, value] : property) {
|
||||
cout << " " << left << std::setfill(' ') << std::setw(11) << key << " - ";
|
||||
std::cout << " " << std::left << std::setfill(' ') << std::setw(11) << key << " - ";
|
||||
if(value.type() == TagLib::Variant::ByteVector) {
|
||||
cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << endl;
|
||||
std::cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << std::endl;
|
||||
/* The picture could be extracted using:
|
||||
ofstream picture;
|
||||
std::ofstream picture;
|
||||
TagLib::String fn(argv[i]);
|
||||
int slashPos = fn.rfind('/');
|
||||
int dotPos = fn.rfind('.');
|
||||
@@ -90,13 +88,13 @@ int main(int argc, char *argv[])
|
||||
fn = fn.substr(slashPos + 1, dotPos - slashPos - 1);
|
||||
}
|
||||
fn += ".jpg";
|
||||
picture.open(fn.toCString(), ios_base::out | ios_base::binary);
|
||||
picture.open(fn.toCString(), std::ios_base::out | std::ios_base::binary);
|
||||
picture << value.value<TagLib::ByteVector>();
|
||||
picture.close();
|
||||
*/
|
||||
}
|
||||
else {
|
||||
cout << value << endl;
|
||||
std::cout << value << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,12 +108,13 @@ int main(int argc, char *argv[])
|
||||
int seconds = properties->lengthInSeconds() % 60;
|
||||
int minutes = (properties->lengthInSeconds() - seconds) / 60;
|
||||
|
||||
cout << "-- AUDIO --" << endl;
|
||||
cout << "bitrate - " << properties->bitrate() << endl;
|
||||
cout << "sample rate - " << properties->sampleRate() << endl;
|
||||
cout << "channels - " << properties->channels() << endl;
|
||||
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << right << seconds << endl;
|
||||
std::cout << "-- AUDIO --" << std::endl;
|
||||
std::cout << "bitrate - " << properties->bitrate() << std::endl;
|
||||
std::cout << "sample rate - " << properties->sampleRate() << std::endl;
|
||||
std::cout << "channels - " << properties->channels() << std::endl;
|
||||
std::cout << "length - " << minutes << ":" << std::setfill('0') << std::setw(2) << std::right << seconds << std::endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
#endif
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool isArgument(const char *s)
|
||||
{
|
||||
return strlen(s) == 2 && s[0] == '-';
|
||||
@@ -48,32 +46,34 @@ bool isArgument(const char *s)
|
||||
|
||||
bool isFile(const char *s)
|
||||
{
|
||||
struct stat st;
|
||||
#ifdef _WIN32
|
||||
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG));
|
||||
struct _stat64 st;
|
||||
return ::_stat64(s, &st) == 0 && (st.st_mode & S_IFREG);
|
||||
#else
|
||||
struct stat st;
|
||||
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG | S_IFLNK));
|
||||
#endif
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
cout << endl;
|
||||
cout << "Usage: tagwriter <fields> <files>" << endl;
|
||||
cout << endl;
|
||||
cout << "Where the valid fields are:" << endl;
|
||||
cout << " -t <title>" << endl;
|
||||
cout << " -a <artist>" << endl;
|
||||
cout << " -A <album>" << endl;
|
||||
cout << " -c <comment>" << endl;
|
||||
cout << " -g <genre>" << endl;
|
||||
cout << " -y <year>" << endl;
|
||||
cout << " -T <track>" << endl;
|
||||
cout << " -R <tagname> <tagvalue>" << endl;
|
||||
cout << " -I <tagname> <tagvalue>" << endl;
|
||||
cout << " -D <tagname>" << endl;
|
||||
cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << endl;
|
||||
cout << endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Usage: tagwriter <fields> <files>" << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Where the valid fields are:" << std::endl;
|
||||
std::cout << " -t <title>" << std::endl;
|
||||
std::cout << " -a <artist>" << std::endl;
|
||||
std::cout << " -A <album>" << std::endl;
|
||||
std::cout << " -c <comment>" << std::endl;
|
||||
std::cout << " -g <genre>" << std::endl;
|
||||
std::cout << " -y <year>" << std::endl;
|
||||
std::cout << " -T <track>" << std::endl;
|
||||
std::cout << " -R <tagname> <tagvalue>" << std::endl;
|
||||
std::cout << " -I <tagname> <tagvalue>" << std::endl;
|
||||
std::cout << " -D <tagname>" << std::endl;
|
||||
std::cout << " -C <complex-property-key> <key1=val1,key2=val2,...>" << std::endl;
|
||||
std::cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
exit(1);
|
||||
}
|
||||
@@ -87,15 +87,111 @@ void checkForRejectedProperties(const TagLib::PropertyMap &tags)
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
cout << "-- rejected TAGs (properties) --" << endl;
|
||||
std::cout << "-- rejected TAGs (properties) --" << std::endl;
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
|
||||
std::cout << std::left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a list of variant maps from a string.
|
||||
* The shorthand syntax in the string is kept simple, but should be sufficient
|
||||
* for testing. Multiple maps are separated by ';', values within a map are
|
||||
* assigned with key=value and separated by a ','. Types are detected, use
|
||||
* double quotes to force a string. A ByteVector can be constructed from the
|
||||
* contents of a file, the path is given after "file://". There is no escape
|
||||
* character, use hex codes for ',' (\x2C) or ';' (\x3B).
|
||||
*/
|
||||
TagLib::List<TagLib::VariantMap> parseComplexPropertyValues(const TagLib::String &str)
|
||||
{
|
||||
if(str.isEmpty() || str == "\"\"" || str == "''") {
|
||||
return {};
|
||||
}
|
||||
TagLib::List<TagLib::VariantMap> values;
|
||||
const auto valueStrs = str.split(";");
|
||||
for(const auto &valueStr : valueStrs) {
|
||||
TagLib::VariantMap value;
|
||||
const auto keyValStrs = valueStr.split(",");
|
||||
for(const auto &keyValStr : keyValStrs) {
|
||||
if(int equalPos = keyValStr.find('='); equalPos != -1) {
|
||||
TagLib::String key = keyValStr.substr(0, equalPos);
|
||||
TagLib::String valStr = keyValStr.substr(equalPos + 1);
|
||||
bool hasDot = false;
|
||||
bool hasNonNumeric = false;
|
||||
bool hasSign = false;
|
||||
for(auto it = valStr.cbegin(); it != valStr.cend(); ++it) {
|
||||
if(it == valStr.cbegin() && (*it == '-' || *it == '+')) {
|
||||
hasSign = true;
|
||||
}
|
||||
else if(*it == '.') {
|
||||
hasDot = true;
|
||||
}
|
||||
else if(*it < '0' || *it > '9') {
|
||||
hasNonNumeric = true;
|
||||
}
|
||||
}
|
||||
TagLib::Variant val;
|
||||
if(valStr == "null") {
|
||||
// keep empty variant
|
||||
}
|
||||
else if(valStr == "true" || valStr == "false") {
|
||||
val = TagLib::Variant(valStr == "true");
|
||||
}
|
||||
else if(!hasNonNumeric && hasDot) {
|
||||
val = TagLib::Variant(std::stod(valStr.to8Bit()));
|
||||
}
|
||||
else if(!hasNonNumeric && hasSign) {
|
||||
val = valStr.toLongLong(nullptr);
|
||||
}
|
||||
else if(!hasNonNumeric) {
|
||||
val = valStr.toULongLong(nullptr);
|
||||
}
|
||||
else if(valStr.startsWith("file://")) {
|
||||
auto filePath = valStr.substr(7 );
|
||||
if(isFile(filePath.toCString())) {
|
||||
std::ifstream fs;
|
||||
fs.open(filePath.toCString(), std::ios::in | std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
buffer << fs.rdbuf();
|
||||
fs.close();
|
||||
TagLib::String buf(buffer.str());
|
||||
val = TagLib::Variant(buf.data(TagLib::String::Latin1));
|
||||
}
|
||||
else {
|
||||
std::cout << filePath.toCString() << " not found." << std::endl;
|
||||
val = TagLib::Variant(TagLib::ByteVector());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int len = valStr.size();
|
||||
if(len >= 2 && valStr[0] == '"' && valStr[len - 1] == '"') {
|
||||
valStr = valStr.substr(1, len - 2);
|
||||
}
|
||||
int hexPos = 0;
|
||||
while((hexPos = valStr.find("\\x", hexPos)) != -1) {
|
||||
char ch;
|
||||
bool ok;
|
||||
if(static_cast<int>(valStr.length()) < hexPos + 4 ||
|
||||
(ch = static_cast<char>(
|
||||
valStr.substr(hexPos + 2, 2).toLongLong(&ok, 16)), !ok)) {
|
||||
break;
|
||||
}
|
||||
valStr = valStr.substr(0, hexPos) + ch + valStr.substr(hexPos + 4);
|
||||
++hexPos;
|
||||
}
|
||||
val = TagLib::Variant(valStr);
|
||||
}
|
||||
value.insert(key, val);
|
||||
}
|
||||
}
|
||||
values.append(value);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TagLib::List<TagLib::FileRef> fileList;
|
||||
@@ -171,17 +267,30 @@ int main(int argc, char *argv[])
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
if(i + 2 < argc) {
|
||||
numArgsConsumed = 3;
|
||||
if(!value.isEmpty()) {
|
||||
TagLib::List<TagLib::VariantMap> values = parseComplexPropertyValues(argv[i + 2]);
|
||||
f.setComplexProperties(value, values);
|
||||
}
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
if(i + 2 < argc) {
|
||||
numArgsConsumed = 3;
|
||||
if(!value.isEmpty()) {
|
||||
if(!isFile(value.toCString())) {
|
||||
cout << value.toCString() << " not found." << endl;
|
||||
std::cout << value.toCString() << " not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
ifstream picture;
|
||||
picture.open(value.toCString());
|
||||
stringstream buffer;
|
||||
std::ifstream picture;
|
||||
picture.open(value.toCString(), std::ios::in | std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
buffer << picture.rdbuf();
|
||||
picture.close();
|
||||
TagLib::String buf(buffer.str());
|
||||
|
||||
@@ -1,38 +1,84 @@
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
include_directories(
|
||||
set(tag_HDR_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/asf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mp4
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
)
|
||||
if(WITH_ASF)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/asf
|
||||
)
|
||||
endif()
|
||||
if(WITH_VORBIS)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
|
||||
)
|
||||
endif()
|
||||
if(WITH_APE)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
|
||||
)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mp4
|
||||
)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
|
||||
)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
|
||||
)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shorten
|
||||
)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/matroska
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/matroska/ebml
|
||||
)
|
||||
endif()
|
||||
include_directories(${tag_HDR_DIRS})
|
||||
|
||||
set(tag_PRIVATE_HDRS)
|
||||
set(tag_HDRS
|
||||
tag.h
|
||||
fileref.h
|
||||
audioproperties.h
|
||||
taglib_export.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
|
||||
toolkit/taglib.h
|
||||
toolkit/tstring.h
|
||||
toolkit/tlist.h
|
||||
@@ -82,66 +128,141 @@ set(tag_HDRS
|
||||
mpeg/id3v2/frames/chapterframe.h
|
||||
mpeg/id3v2/frames/tableofcontentsframe.h
|
||||
mpeg/id3v2/frames/podcastframe.h
|
||||
ogg/oggfile.h
|
||||
ogg/oggpage.h
|
||||
ogg/oggpageheader.h
|
||||
ogg/xiphcomment.h
|
||||
ogg/vorbis/vorbisfile.h
|
||||
ogg/vorbis/vorbisproperties.h
|
||||
ogg/flac/oggflacfile.h
|
||||
ogg/speex/speexfile.h
|
||||
ogg/speex/speexproperties.h
|
||||
ogg/opus/opusfile.h
|
||||
ogg/opus/opusproperties.h
|
||||
flac/flacfile.h
|
||||
flac/flacpicture.h
|
||||
flac/flacproperties.h
|
||||
flac/flacmetadatablock.h
|
||||
ape/apefile.h
|
||||
ape/apeproperties.h
|
||||
ape/apetag.h
|
||||
ape/apefooter.h
|
||||
ape/apeitem.h
|
||||
mpc/mpcfile.h
|
||||
mpc/mpcproperties.h
|
||||
wavpack/wavpackfile.h
|
||||
wavpack/wavpackproperties.h
|
||||
trueaudio/trueaudiofile.h
|
||||
trueaudio/trueaudioproperties.h
|
||||
riff/rifffile.h
|
||||
riff/aiff/aifffile.h
|
||||
riff/aiff/aiffproperties.h
|
||||
riff/wav/wavfile.h
|
||||
riff/wav/wavproperties.h
|
||||
riff/wav/infotag.h
|
||||
asf/asffile.h
|
||||
asf/asfproperties.h
|
||||
asf/asftag.h
|
||||
asf/asfattribute.h
|
||||
asf/asfpicture.h
|
||||
mp4/mp4file.h
|
||||
mp4/mp4atom.h
|
||||
mp4/mp4tag.h
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mp4/mp4itemfactory.h
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
mod/modproperties.h
|
||||
it/itfile.h
|
||||
it/itproperties.h
|
||||
s3m/s3mfile.h
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
if(WITH_VORBIS)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
ogg/oggfile.h
|
||||
ogg/oggpage.h
|
||||
ogg/oggpageheader.h
|
||||
ogg/xiphcomment.h
|
||||
ogg/vorbis/vorbisfile.h
|
||||
ogg/vorbis/vorbisproperties.h
|
||||
ogg/flac/oggflacfile.h
|
||||
ogg/speex/speexfile.h
|
||||
ogg/speex/speexproperties.h
|
||||
ogg/opus/opusfile.h
|
||||
ogg/opus/opusproperties.h
|
||||
flac/flacfile.h
|
||||
flac/flacpicture.h
|
||||
flac/flacproperties.h
|
||||
flac/flacmetadatablock.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_APE)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
ape/apefile.h
|
||||
ape/apeproperties.h
|
||||
ape/apetag.h
|
||||
ape/apefooter.h
|
||||
ape/apeitem.h
|
||||
mpc/mpcfile.h
|
||||
mpc/mpcproperties.h
|
||||
wavpack/wavpackfile.h
|
||||
wavpack/wavpackproperties.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
trueaudio/trueaudiofile.h
|
||||
trueaudio/trueaudioproperties.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
riff/rifffile.h
|
||||
riff/aiff/aifffile.h
|
||||
riff/aiff/aiffproperties.h
|
||||
riff/wav/wavfile.h
|
||||
riff/wav/wavproperties.h
|
||||
riff/wav/infotag.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_ASF)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
asf/asffile.h
|
||||
asf/asfproperties.h
|
||||
asf/asftag.h
|
||||
asf/asfattribute.h
|
||||
asf/asfpicture.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
mp4/mp4file.h
|
||||
mp4/mp4atom.h
|
||||
mp4/mp4tag.h
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mp4/mp4stem.h
|
||||
mp4/mp4itemfactory.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
mod/modproperties.h
|
||||
it/itfile.h
|
||||
it/itproperties.h
|
||||
s3m/s3mfile.h
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
shorten/shortenfile.h
|
||||
shorten/shortenproperties.h
|
||||
shorten/shortentag.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
matroska/matroskaattachedfile.h
|
||||
matroska/matroskaattachments.h
|
||||
matroska/matroskachapter.h
|
||||
matroska/matroskachapteredition.h
|
||||
matroska/matroskachapters.h
|
||||
matroska/matroskaelement.h
|
||||
matroska/matroskafile.h
|
||||
matroska/matroskaproperties.h
|
||||
matroska/matroskasimpletag.h
|
||||
matroska/matroskatag.h
|
||||
)
|
||||
set(tag_PRIVATE_HDRS ${tag_PRIVATE_HDRS}
|
||||
matroska/ebml/ebmlbinaryelement.h
|
||||
matroska/ebml/ebmlelement.h
|
||||
matroska/ebml/ebmlmasterelement.h
|
||||
matroska/ebml/ebmlmkattachments.h
|
||||
matroska/ebml/ebmlmkchapters.h
|
||||
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
|
||||
matroska/matroskacues.h
|
||||
matroska/matroskaseekhead.h
|
||||
matroska/matroskasegment.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(mpeg_SRCS
|
||||
mpeg/mpegfile.cpp
|
||||
@@ -185,128 +306,190 @@ set(frames_SRCS
|
||||
mpeg/id3v2/frames/podcastframe.cpp
|
||||
)
|
||||
|
||||
set(ogg_SRCS
|
||||
ogg/oggfile.cpp
|
||||
ogg/oggpage.cpp
|
||||
ogg/oggpageheader.cpp
|
||||
ogg/xiphcomment.cpp
|
||||
)
|
||||
if(WITH_VORBIS)
|
||||
set(ogg_SRCS
|
||||
ogg/oggfile.cpp
|
||||
ogg/oggpage.cpp
|
||||
ogg/oggpageheader.cpp
|
||||
ogg/xiphcomment.cpp
|
||||
)
|
||||
|
||||
set(vorbis_SRCS
|
||||
ogg/vorbis/vorbisfile.cpp
|
||||
ogg/vorbis/vorbisproperties.cpp
|
||||
)
|
||||
set(vorbis_SRCS
|
||||
ogg/vorbis/vorbisfile.cpp
|
||||
ogg/vorbis/vorbisproperties.cpp
|
||||
)
|
||||
|
||||
set(flacs_SRCS
|
||||
flac/flacfile.cpp
|
||||
flac/flacpicture.cpp
|
||||
flac/flacproperties.cpp
|
||||
flac/flacmetadatablock.cpp
|
||||
flac/flacunknownmetadatablock.cpp
|
||||
)
|
||||
set(flacs_SRCS
|
||||
flac/flacfile.cpp
|
||||
flac/flacpicture.cpp
|
||||
flac/flacproperties.cpp
|
||||
flac/flacmetadatablock.cpp
|
||||
flac/flacunknownmetadatablock.cpp
|
||||
)
|
||||
|
||||
set(oggflacs_SRCS
|
||||
ogg/flac/oggflacfile.cpp
|
||||
)
|
||||
set(oggflacs_SRCS
|
||||
ogg/flac/oggflacfile.cpp
|
||||
)
|
||||
|
||||
set(mpc_SRCS
|
||||
mpc/mpcfile.cpp
|
||||
mpc/mpcproperties.cpp
|
||||
)
|
||||
set(speex_SRCS
|
||||
ogg/speex/speexfile.cpp
|
||||
ogg/speex/speexproperties.cpp
|
||||
)
|
||||
|
||||
set(mp4_SRCS
|
||||
mp4/mp4file.cpp
|
||||
mp4/mp4atom.cpp
|
||||
mp4/mp4tag.cpp
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
mp4/mp4itemfactory.cpp
|
||||
)
|
||||
set(opus_SRCS
|
||||
ogg/opus/opusfile.cpp
|
||||
ogg/opus/opusproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(ape_SRCS
|
||||
ape/apetag.cpp
|
||||
ape/apefooter.cpp
|
||||
ape/apeitem.cpp
|
||||
ape/apefile.cpp
|
||||
ape/apeproperties.cpp
|
||||
)
|
||||
if(WITH_APE)
|
||||
set(mpc_SRCS
|
||||
mpc/mpcfile.cpp
|
||||
mpc/mpcproperties.cpp
|
||||
)
|
||||
|
||||
set(wavpack_SRCS
|
||||
wavpack/wavpackfile.cpp
|
||||
wavpack/wavpackproperties.cpp
|
||||
)
|
||||
set(ape_SRCS
|
||||
ape/apetag.cpp
|
||||
ape/apefooter.cpp
|
||||
ape/apeitem.cpp
|
||||
ape/apefile.cpp
|
||||
ape/apeproperties.cpp
|
||||
)
|
||||
|
||||
set(speex_SRCS
|
||||
ogg/speex/speexfile.cpp
|
||||
ogg/speex/speexproperties.cpp
|
||||
)
|
||||
set(wavpack_SRCS
|
||||
wavpack/wavpackfile.cpp
|
||||
wavpack/wavpackproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(opus_SRCS
|
||||
ogg/opus/opusfile.cpp
|
||||
ogg/opus/opusproperties.cpp
|
||||
)
|
||||
if(WITH_MP4)
|
||||
set(mp4_SRCS
|
||||
mp4/mp4file.cpp
|
||||
mp4/mp4atom.cpp
|
||||
mp4/mp4tag.cpp
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
mp4/mp4stem.cpp
|
||||
mp4/mp4itemfactory.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(trueaudio_SRCS
|
||||
trueaudio/trueaudiofile.cpp
|
||||
trueaudio/trueaudioproperties.cpp
|
||||
)
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(trueaudio_SRCS
|
||||
trueaudio/trueaudiofile.cpp
|
||||
trueaudio/trueaudioproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(asf_SRCS
|
||||
asf/asftag.cpp
|
||||
asf/asffile.cpp
|
||||
asf/asfproperties.cpp
|
||||
asf/asfattribute.cpp
|
||||
asf/asfpicture.cpp
|
||||
)
|
||||
if(WITH_ASF)
|
||||
set(asf_SRCS
|
||||
asf/asftag.cpp
|
||||
asf/asffile.cpp
|
||||
asf/asfproperties.cpp
|
||||
asf/asfattribute.cpp
|
||||
asf/asfpicture.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(riff_SRCS
|
||||
riff/rifffile.cpp
|
||||
)
|
||||
if(WITH_RIFF)
|
||||
set(riff_SRCS
|
||||
riff/rifffile.cpp
|
||||
)
|
||||
|
||||
set(aiff_SRCS
|
||||
riff/aiff/aifffile.cpp
|
||||
riff/aiff/aiffproperties.cpp
|
||||
)
|
||||
set(aiff_SRCS
|
||||
riff/aiff/aifffile.cpp
|
||||
riff/aiff/aiffproperties.cpp
|
||||
)
|
||||
|
||||
set(wav_SRCS
|
||||
riff/wav/wavfile.cpp
|
||||
riff/wav/wavproperties.cpp
|
||||
riff/wav/infotag.cpp
|
||||
)
|
||||
set(wav_SRCS
|
||||
riff/wav/wavfile.cpp
|
||||
riff/wav/wavproperties.cpp
|
||||
riff/wav/infotag.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(mod_SRCS
|
||||
mod/modfilebase.cpp
|
||||
mod/modfile.cpp
|
||||
mod/modtag.cpp
|
||||
mod/modproperties.cpp
|
||||
)
|
||||
if(WITH_MOD)
|
||||
set(mod_SRCS
|
||||
mod/modfilebase.cpp
|
||||
mod/modfile.cpp
|
||||
mod/modtag.cpp
|
||||
mod/modproperties.cpp
|
||||
)
|
||||
|
||||
set(s3m_SRCS
|
||||
s3m/s3mfile.cpp
|
||||
s3m/s3mproperties.cpp
|
||||
)
|
||||
set(s3m_SRCS
|
||||
s3m/s3mfile.cpp
|
||||
s3m/s3mproperties.cpp
|
||||
)
|
||||
|
||||
set(it_SRCS
|
||||
it/itfile.cpp
|
||||
it/itproperties.cpp
|
||||
)
|
||||
set(it_SRCS
|
||||
it/itfile.cpp
|
||||
it/itproperties.cpp
|
||||
)
|
||||
|
||||
set(xm_SRCS
|
||||
xm/xmfile.cpp
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
set(xm_SRCS
|
||||
xm/xmfile.cpp
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
if(WITH_DSF)
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_SHORTEN)
|
||||
set(shorten_SRCS
|
||||
shorten/shortenfile.cpp
|
||||
shorten/shortenproperties.cpp
|
||||
shorten/shortentag.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_MATROSKA)
|
||||
set(matroska_SRCS
|
||||
matroska/matroskaattachedfile.cpp
|
||||
matroska/matroskaattachments.cpp
|
||||
matroska/matroskachapter.cpp
|
||||
matroska/matroskachapteredition.cpp
|
||||
matroska/matroskachapters.cpp
|
||||
matroska/matroskacues.cpp
|
||||
matroska/matroskaelement.cpp
|
||||
matroska/matroskafile.cpp
|
||||
matroska/matroskaproperties.cpp
|
||||
matroska/matroskaseekhead.cpp
|
||||
matroska/matroskasegment.cpp
|
||||
matroska/matroskasimpletag.cpp
|
||||
matroska/matroskatag.cpp
|
||||
)
|
||||
|
||||
set(ebml_SRCS
|
||||
matroska/ebml/ebmlbinaryelement.cpp
|
||||
matroska/ebml/ebmlelement.cpp
|
||||
matroska/ebml/ebmlmasterelement.cpp
|
||||
matroska/ebml/ebmlmkattachments.cpp
|
||||
matroska/ebml/ebmlmkchapters.cpp
|
||||
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
|
||||
)
|
||||
endif()
|
||||
|
||||
set(toolkit_SRCS
|
||||
toolkit/tstring.cpp
|
||||
@@ -331,7 +514,7 @@ set(tag_LIB_SRCS
|
||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${dsf_SRCS} ${dsdiff_SRCS}
|
||||
${dsf_SRCS} ${dsdiff_SRCS} ${shorten_SRCS} ${matroska_SRCS} ${ebml_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
@@ -339,7 +522,7 @@ set(tag_LIB_SRCS
|
||||
tagutils.cpp
|
||||
)
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS} ${tag_PRIVATE_HDRS})
|
||||
|
||||
target_include_directories(tag INTERFACE
|
||||
$<INSTALL_INTERFACE:include>
|
||||
@@ -347,7 +530,7 @@ target_include_directories(tag INTERFACE
|
||||
)
|
||||
|
||||
target_link_libraries(tag
|
||||
PRIVATE $<$<TARGET_EXISTS:utf8::cpp>:utf8::cpp>
|
||||
PRIVATE $<IF:$<TARGET_EXISTS:utf8::cpp>,utf8::cpp,$<$<TARGET_EXISTS:utf8cpp>:utf8cpp>>
|
||||
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
|
||||
)
|
||||
|
||||
@@ -359,6 +542,9 @@ set_target_properties(tag PROPERTIES
|
||||
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}"
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(tag PUBLIC TAGLIB_STATIC)
|
||||
endif()
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(tag PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
@@ -139,7 +139,7 @@ void APE::Properties::read(File *file, offset_t streamLength)
|
||||
if(d->sampleFrames > 0 && d->sampleRate > 0) {
|
||||
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
|
||||
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -302,16 +302,16 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
|
||||
if(kind == 0) {
|
||||
data = renderString(name, true) +
|
||||
ByteVector::fromShort(static_cast<int>(d->type), false) +
|
||||
ByteVector::fromShort(data.size(), false) +
|
||||
ByteVector::fromShort(static_cast<short>(d->type), false) +
|
||||
ByteVector::fromShort(static_cast<short>(data.size()), false) +
|
||||
data;
|
||||
}
|
||||
else {
|
||||
ByteVector nameData = renderString(name);
|
||||
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
|
||||
ByteVector::fromShort(d->stream, false) +
|
||||
ByteVector::fromShort(nameData.size(), false) +
|
||||
ByteVector::fromShort(static_cast<int>(d->type), false) +
|
||||
data = ByteVector::fromShort(static_cast<short>(kind == 2 ? d->language : 0), false) +
|
||||
ByteVector::fromShort(static_cast<short>(d->stream), false) +
|
||||
ByteVector::fromShort(static_cast<short>(nameData.size()), false) +
|
||||
ByteVector::fromShort(static_cast<short>(d->type), false) +
|
||||
ByteVector::fromUInt(data.size(), false) +
|
||||
nameData +
|
||||
data;
|
||||
|
||||
@@ -221,7 +221,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long l
|
||||
|
||||
const long long duration = data.toLongLong(40, false);
|
||||
const long long preroll = data.toLongLong(56, false);
|
||||
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
|
||||
file->d->properties->setLengthInMilliseconds(
|
||||
static_cast<int>(static_cast<double>(duration) / 10000.0 - static_cast<double>(preroll) + 0.5));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
@@ -271,11 +272,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
|
||||
const ByteVector v4 = renderString(file->d->tag->comment());
|
||||
const ByteVector v5 = renderString(file->d->tag->rating());
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(v1.size(), false));
|
||||
data.append(ByteVector::fromShort(v2.size(), false));
|
||||
data.append(ByteVector::fromShort(v3.size(), false));
|
||||
data.append(ByteVector::fromShort(v4.size(), false));
|
||||
data.append(ByteVector::fromShort(v5.size(), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v1.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v2.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v3.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v4.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v5.size()), false));
|
||||
data.append(v1);
|
||||
data.append(v2);
|
||||
data.append(v3);
|
||||
@@ -302,7 +303,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
@@ -325,7 +326,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
@@ -348,7 +349,7 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace TagLib
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
data = ByteVector::fromShort(static_cast<short>(data.size()), false) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -65,12 +65,8 @@ namespace
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return std::none_of(name.cbegin(), name.cend(),
|
||||
[](unsigned char c) { return c < 32 || c > 126; });
|
||||
}
|
||||
|
||||
enum {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#ifndef TAGLIB_DSDIFFFILE_H
|
||||
#define TAGLIB_DSDIFFFILE_H
|
||||
|
||||
#include "rifffile.h"
|
||||
#include "tfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsdiffproperties.h"
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
@@ -58,7 +58,7 @@ DSDIFF::Properties::Properties(unsigned int sampleRate,
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
? static_cast<int>(d->sampleCount * 1000.0 / d->sampleRate + 0.5)
|
||||
? static_cast<int>(static_cast<double>(d->sampleCount) * 1000.0 / d->sampleRate + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ bool DSF::File::isSupported(IOStream *stream)
|
||||
return id.startsWith("DSD ");
|
||||
}
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
DSF::File::File(FileName file, bool,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
@@ -69,7 +69,7 @@ DSF::File::File(FileName file, bool readProperties,
|
||||
read(propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
DSF::File::File(IOStream *stream, bool,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
|
||||
@@ -129,6 +129,6 @@ void DSF::Properties::read(const ByteVector &data)
|
||||
d->bitrate = static_cast<unsigned int>(
|
||||
d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
|
||||
d->length = d->samplingFrequency > 0
|
||||
? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5)
|
||||
? static_cast<unsigned int>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
@@ -32,31 +32,54 @@
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "taglib_config.h"
|
||||
#include "tfilestream.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "tdebug.h"
|
||||
#include "aifffile.h"
|
||||
#include "apefile.h"
|
||||
#include "asffile.h"
|
||||
#include "flacfile.h"
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "mpcfile.h"
|
||||
#include "mpegfile.h"
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "apefile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
#include "asffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
#include "flacfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "wavfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "s3mfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
#include "mp4file.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
#include "trueaudiofile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
#include "shortenfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
#include "matroskafile.h"
|
||||
#endif
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -135,6 +158,7 @@ namespace
|
||||
|
||||
if(ext == "MP3" || ext == "MP2" || ext == "AAC")
|
||||
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
else if(ext == "OGG")
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OGA") {
|
||||
@@ -147,26 +171,38 @@ namespace
|
||||
}
|
||||
else if(ext == "FLAC")
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "MPC")
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WV")
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "SPX")
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OPUS")
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
else if(ext == "MPC")
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WV")
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "APE")
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
else if(ext == "TTA")
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
else if(ext == "WMA" || ext == "ASF")
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WAV")
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "APE")
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
@@ -176,10 +212,21 @@ namespace
|
||||
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "XM")
|
||||
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
else if(ext == "DSF")
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "DFF" || ext == "DSDIFF")
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
else if(ext == "SHN")
|
||||
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
else if(ext == "MKA" || ext == "MKV" || ext == "WEBM")
|
||||
file = new Matroska::File(stream, readAudioProperties);
|
||||
#endif
|
||||
|
||||
// if file is not valid, leave it to content-based detection.
|
||||
|
||||
@@ -201,36 +248,58 @@ namespace
|
||||
|
||||
if(MPEG::File::isSupported(stream))
|
||||
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
else if(Ogg::Vorbis::File::isSupported(stream))
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::FLAC::File::isSupported(stream))
|
||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(FLAC::File::isSupported(stream))
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(MPC::File::isSupported(stream))
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(WavPack::File::isSupported(stream))
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Speex::File::isSupported(stream))
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Opus::File::isSupported(stream))
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
else if(MPC::File::isSupported(stream))
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(WavPack::File::isSupported(stream))
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
else if(TrueAudio::File::isSupported(stream))
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
else if(MP4::File::isSupported(stream))
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
else if(ASF::File::isSupported(stream))
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
else if(RIFF::AIFF::File::isSupported(stream))
|
||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(RIFF::WAV::File::isSupported(stream))
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
else if(DSF::File::isSupported(stream))
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSDIFF::File::isSupported(stream))
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
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.
|
||||
|
||||
@@ -263,7 +332,7 @@ public:
|
||||
return !file || !file->isValid();
|
||||
}
|
||||
|
||||
bool isNullWithDebugMessage(const String &methodName) const
|
||||
bool isNullWithDebugMessage([[maybe_unused]] const String &methodName) const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::" + methodName + "() - Called without a valid file.");
|
||||
@@ -400,17 +469,25 @@ StringList FileRef::defaultFileExtensions()
|
||||
{
|
||||
StringList l;
|
||||
|
||||
l.append("mp3");
|
||||
l.append("mp2");
|
||||
l.append("aac");
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
l.append("ogg");
|
||||
l.append("flac");
|
||||
l.append("oga");
|
||||
l.append("opus");
|
||||
l.append("mp3");
|
||||
l.append("mp2");
|
||||
l.append("spx");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
l.append("mpc");
|
||||
l.append("wv");
|
||||
l.append("spx");
|
||||
l.append("ape");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
l.append("tta");
|
||||
l.append("aac");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
l.append("m4a");
|
||||
l.append("m4r");
|
||||
l.append("m4b");
|
||||
@@ -418,14 +495,19 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("3g2");
|
||||
l.append("mp4");
|
||||
l.append("m4v");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
l.append("wma");
|
||||
l.append("asf");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
l.append("aif");
|
||||
l.append("aiff");
|
||||
l.append("afc");
|
||||
l.append("aifc");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
l.append("mod");
|
||||
l.append("module"); // alias for "mod"
|
||||
l.append("nst"); // alias for "mod"
|
||||
@@ -433,9 +515,20 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("s3m");
|
||||
l.append("it");
|
||||
l.append("xm");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
l.append("shn");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
l.append("mkv");
|
||||
l.append("mka");
|
||||
l.append("webm");
|
||||
#endif
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this StreamTypeResolver instance.
|
||||
*/
|
||||
~StreamTypeResolver() override = 0;
|
||||
virtual ~StreamTypeResolver() override = 0; // virtual is needed by SWIG
|
||||
|
||||
StreamTypeResolver(const StreamTypeResolver &) = delete;
|
||||
StreamTypeResolver &operator=(const StreamTypeResolver &) = delete;
|
||||
|
||||
@@ -265,12 +265,19 @@ bool FLAC::File::save()
|
||||
// Render data for the metadata blocks
|
||||
|
||||
ByteVector data;
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
ByteVector blockData = block->render();
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
ByteVector blockData = (*it)->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
blockHeader[0] = block->code();
|
||||
if(blockHeader[0] != 0) {
|
||||
debug("FLAC::File::save() -- Removing too large block.");
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
continue;
|
||||
}
|
||||
blockHeader[0] = static_cast<char>((*it)->code());
|
||||
data.append(blockHeader);
|
||||
data.append(blockData);
|
||||
++it;
|
||||
}
|
||||
|
||||
// Compute the amount of padding, and append that to data.
|
||||
|
||||
@@ -134,7 +134,7 @@ void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
if(d->sampleFrames > 0 && d->sampleRate > 0) {
|
||||
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
|
||||
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
|
||||
}
|
||||
|
||||
if(data.size() >= pos + 16)
|
||||
|
||||
@@ -167,7 +167,7 @@ bool IT::File::save()
|
||||
if(messageOffset + messageLength >= fileSize) {
|
||||
// append new message
|
||||
seek(54);
|
||||
writeU16L(message.size());
|
||||
writeU16L(static_cast<unsigned short>(message.size()));
|
||||
writeU32L(messageOffset);
|
||||
seek(messageOffset);
|
||||
writeBlock(message);
|
||||
|
||||
70
taglib/matroska/ebml/ebmlbinaryelement.cpp
Normal file
70
taglib/matroska/ebml/ebmlbinaryelement.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlbinaryelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tfile.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::BinaryElement::BinaryElement(Id id):
|
||||
Element(id, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
const ByteVector& EBML::BinaryElement::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void EBML::BinaryElement::setValue(const ByteVector& val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
|
||||
bool EBML::BinaryElement::read(File &file)
|
||||
{
|
||||
value = file.readBlock(dataSize);
|
||||
if(value.size() != dataSize) {
|
||||
debug("Failed to read binary element");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteVector EBML::BinaryElement::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
dataSize = value.size();
|
||||
buffer.append(renderVINT(dataSize, 0));
|
||||
buffer.append(value);
|
||||
return buffer;
|
||||
}
|
||||
51
taglib/matroska/ebml/ebmlbinaryelement.h
Normal file
51
taglib/matroska/ebml/ebmlbinaryelement.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLBINARYELEMENT_H
|
||||
#define TAGLIB_EBMLBINARYELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class BinaryElement : public Element
|
||||
{
|
||||
public:
|
||||
BinaryElement(Id id, int sizeLength, offset_t dataSize);
|
||||
BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit BinaryElement(Id id);
|
||||
|
||||
const ByteVector &getValue() const;
|
||||
void setValue(const ByteVector &val);
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
|
||||
private:
|
||||
ByteVector value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
216
taglib/matroska/ebml/ebmlelement.cpp
Normal file
216
taglib/matroska/ebml/ebmlelement.cpp
Normal file
@@ -0,0 +1,216 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlelement.h"
|
||||
#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 "ebmlmkchapters.h"
|
||||
#include "ebmlmktracks.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tfile.h"
|
||||
#include "tdebug.h"
|
||||
#include "tutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
#define RETURN_ELEMENT_FOR_CASE(eid) \
|
||||
case (eid): return make_unique_element<eid>(id, sizeLength, dataSize, offset)
|
||||
|
||||
std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
|
||||
{
|
||||
// Get the element ID
|
||||
const offset_t offset = file.tell();
|
||||
unsigned int uintId = readId(file);
|
||||
if(!uintId) {
|
||||
debug("Failed to parse EMBL ElementID");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the size length and data length
|
||||
const auto &[sizeLength, dataSize] = readVINT(file);
|
||||
if(!sizeLength)
|
||||
return nullptr;
|
||||
|
||||
// Return the subclass
|
||||
// The enum switch without default will give us a warning if an ID is missing
|
||||
auto id = static_cast<Id>(uintId);
|
||||
switch(id) {
|
||||
RETURN_ELEMENT_FOR_CASE(Id::EBMLHeader);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::DocType);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::DocTypeVersion);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSegment);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkInfo);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTracks);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTags);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachments);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTag);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagTargets);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSimpleTag);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFile);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeek);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTrackEntry);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAudio);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagName);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagString);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileName);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileDescription);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagLanguage);
|
||||
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::MkTagEditionUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagChapterUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagAttachmentUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagsLanguageDefault);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeekPosition);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTimestampScale);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkBitDepth);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChannels);
|
||||
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);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCluster);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCodecState);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagBinary);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCues);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCuePoint);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueTime);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueTrackPositions);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueTrack);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueClusterPosition);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueRelativePosition);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueDuration);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueBlockNumber);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueCodecState);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueReference);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueRefTime);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapters);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionEntry);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagDefault);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagOrdered);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterAtom);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeStart);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeEnd);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterFlagHidden);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterDisplay);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapString);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapLanguage);
|
||||
}
|
||||
return std::make_unique<Element>(id, sizeLength, dataSize);
|
||||
}
|
||||
|
||||
unsigned int EBML::Element::readId(File &file)
|
||||
{
|
||||
auto buffer = file.readBlock(1);
|
||||
if(buffer.size() != 1) {
|
||||
debug("Failed to read VINT size");
|
||||
return 0;
|
||||
}
|
||||
const unsigned int numBytes = VINTSizeLength<4>(*buffer.begin());
|
||||
if(!numBytes)
|
||||
return 0;
|
||||
if(numBytes > 1)
|
||||
buffer.append(file.readBlock(numBytes - 1));
|
||||
if(buffer.size() != numBytes) {
|
||||
debug("Failed to read VINT data");
|
||||
return 0;
|
||||
}
|
||||
return buffer.toUInt(true);
|
||||
}
|
||||
|
||||
EBML::Element::Element(Id id, int sizeLength, offset_t dataSize):
|
||||
id(id), sizeLength(sizeLength), dataSize(dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element::Element(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
id(id), sizeLength(sizeLength), dataSize(dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element::~Element() = default;
|
||||
|
||||
bool EBML::Element::read(File& file)
|
||||
{
|
||||
skipData(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EBML::Element::skipData(File &file)
|
||||
{
|
||||
file.seek(dataSize, File::Position::Current);
|
||||
}
|
||||
|
||||
EBML::Element::Id EBML::Element::getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
offset_t EBML::Element::headSize() const
|
||||
{
|
||||
return idSize(id) + sizeLength;
|
||||
}
|
||||
|
||||
offset_t EBML::Element::getSize() const
|
||||
{
|
||||
return headSize() + dataSize;
|
||||
}
|
||||
|
||||
int EBML::Element::getSizeLength() const
|
||||
{
|
||||
return sizeLength;
|
||||
}
|
||||
|
||||
int64_t EBML::Element::getDataSize() const
|
||||
{
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
buffer.append(renderVINT(0, 0));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::renderId() const
|
||||
{
|
||||
const int numBytes = idSize(id);
|
||||
static const auto byteOrder = Utils::systemByteOrder();
|
||||
const auto uintId = static_cast<uint32_t>(id);
|
||||
uint32_t data = byteOrder == Utils::LittleEndian ? Utils::byteSwap(uintId) : uintId;
|
||||
return ByteVector(reinterpret_cast<char *>(&data) + (4 - numBytes), numBytes);
|
||||
}
|
||||
255
taglib/matroska/ebml/ebmlelement.h
Normal file
255
taglib/matroska/ebml/ebmlelement.h
Normal file
@@ -0,0 +1,255 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLELEMENT_H
|
||||
#define TAGLIB_EBMLELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
class File;
|
||||
class ByteVector;
|
||||
|
||||
namespace EBML {
|
||||
|
||||
class Element
|
||||
{
|
||||
public:
|
||||
enum class Id : unsigned int
|
||||
{
|
||||
EBMLHeader = 0x1A45DFA3,
|
||||
DocType = 0x4282,
|
||||
DocTypeVersion = 0x4287,
|
||||
VoidElement = 0xEC,
|
||||
MkSegment = 0x18538067,
|
||||
MkTags = 0x1254C367,
|
||||
MkTag = 0x7373,
|
||||
MkTagTargets = 0x63C0,
|
||||
MkTagTargetTypeValue = 0x68CA,
|
||||
MkTagTrackUID = 0x63C5,
|
||||
MkTagEditionUID = 0x63C9,
|
||||
MkTagChapterUID = 0x63C4,
|
||||
MkTagAttachmentUID = 0x63C6,
|
||||
MkSimpleTag = 0x67C8,
|
||||
MkTagName = 0x45A3,
|
||||
MkTagLanguage = 0x447A,
|
||||
MkTagBinary = 0x4485,
|
||||
MkTagString = 0x4487,
|
||||
MkTagsTagLanguage = 0x447A,
|
||||
MkTagsLanguageDefault = 0x4484,
|
||||
MkAttachments = 0x1941A469,
|
||||
MkAttachedFile = 0x61A7,
|
||||
MkAttachedFileDescription = 0x467E,
|
||||
MkAttachedFileName = 0x466E,
|
||||
MkAttachedFileMediaType = 0x4660,
|
||||
MkAttachedFileData = 0x465C,
|
||||
MkAttachedFileUID = 0x46AE,
|
||||
MkSeekHead = 0x114D9B74,
|
||||
MkSeek = 0x4DBB,
|
||||
MkSeekID = 0x53AB,
|
||||
MkSeekPosition = 0x53AC,
|
||||
MkCluster = 0x1F43B675,
|
||||
MkCodecState = 0xA4,
|
||||
MkCues = 0x1C53BB6B,
|
||||
MkCuePoint = 0xBB,
|
||||
MkCueTime = 0xB3,
|
||||
MkCueTrackPositions = 0xB7,
|
||||
MkCueTrack = 0xF7,
|
||||
MkCueClusterPosition = 0xF1,
|
||||
MkCueRelativePosition = 0xF0,
|
||||
MkCueDuration = 0xB2,
|
||||
MkCueBlockNumber = 0x5378,
|
||||
MkCueCodecState = 0xEA,
|
||||
MkCueReference = 0xDB,
|
||||
MkCueRefTime = 0x96,
|
||||
MkInfo = 0x1549A966,
|
||||
MkTimestampScale = 0x2AD7B1,
|
||||
MkDuration = 0x4489,
|
||||
MkTitle = 0x7BA9,
|
||||
MkTracks = 0x1654AE6B,
|
||||
MkTrackEntry = 0xAE,
|
||||
MkCodecID = 0x86,
|
||||
MkAudio = 0xE1,
|
||||
MkSamplingFrequency = 0xB5,
|
||||
MkBitDepth = 0x6264,
|
||||
MkChannels = 0x9F,
|
||||
MkChapters = 0x1043A770,
|
||||
MkEditionEntry = 0x45B9,
|
||||
MkEditionUID = 0x45BC,
|
||||
MkEditionFlagDefault = 0x45DB,
|
||||
MkEditionFlagOrdered = 0x45DD,
|
||||
MkChapterAtom = 0xB6,
|
||||
MkChapterUID = 0x73C4,
|
||||
MkChapterTimeStart = 0x91,
|
||||
MkChapterTimeEnd = 0x92,
|
||||
MkChapterFlagHidden = 0x98,
|
||||
MkChapterDisplay = 0x80,
|
||||
MkChapString = 0x85,
|
||||
MkChapLanguage = 0x437C,
|
||||
};
|
||||
|
||||
Element(Id id, int sizeLength, offset_t dataSize);
|
||||
Element(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
virtual ~Element();
|
||||
|
||||
virtual bool read(File &file);
|
||||
void skipData(File &file);
|
||||
Id getId() const;
|
||||
offset_t headSize() const;
|
||||
offset_t getSize() const;
|
||||
int getSizeLength() const;
|
||||
int64_t getDataSize() const;
|
||||
ByteVector renderId() const;
|
||||
virtual ByteVector render();
|
||||
static std::unique_ptr<Element> factory(File &file);
|
||||
static unsigned int readId(File &file);
|
||||
|
||||
protected:
|
||||
Id id;
|
||||
int sizeLength;
|
||||
offset_t dataSize;
|
||||
};
|
||||
|
||||
// Template specializations to ensure that elements for the different IDs
|
||||
// are consistently created and cast. They provide a mapping between IDs
|
||||
// and Element subclasses. The switch in factory() makes sure that the
|
||||
// template for all IDs are instantiated, i.e. that every ID has its Element
|
||||
// subclass mapped.
|
||||
class MasterElement;
|
||||
class UIntElement;
|
||||
class BinaryElement;
|
||||
class FloatElement;
|
||||
class MkSegment;
|
||||
class MkInfo;
|
||||
class MkTracks;
|
||||
class MkTags;
|
||||
class MkAttachments;
|
||||
class MkSeekHead;
|
||||
class MkChapters;
|
||||
class MkCues;
|
||||
class VoidElement;
|
||||
class UTF8StringElement;
|
||||
class Latin1StringElement;
|
||||
|
||||
template <Element::Id ID>
|
||||
struct GetElementTypeById;
|
||||
|
||||
template <> struct GetElementTypeById<Element::Id::EBMLHeader> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::DocType> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::DocTypeVersion> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSegment> { using type = MkSegment; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkInfo> { using type = MkInfo; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTracks> { using type = MkTracks; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTags> { using type = MkTags; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachments> { using type = MkAttachments; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTag> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTargets> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSimpleTag> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFile> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeek> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTrackEntry> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAudio> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCuePoint> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueTrackPositions> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueReference> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCluster> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCues> { using type = MkCues; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagName> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagString> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileName> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileDescription> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagLanguage> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileMediaType> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCodecID> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTargetTypeValue> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTrackUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagEditionUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagChapterUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagAttachmentUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekPosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTimestampScale> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkBitDepth> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChannels> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueTime> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueTrack> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueClusterPosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueRelativePosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueDuration> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueBlockNumber> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueCodecState> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueRefTime> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagsLanguageDefault> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileData> { using type = BinaryElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekID> { using type = BinaryElement; };
|
||||
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::MkChapters> { using type = MkChapters; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionEntry> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionFlagDefault> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionFlagOrdered> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterAtom> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterTimeStart> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterTimeEnd> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterFlagHidden> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterDisplay> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapString> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapLanguage> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::VoidElement> { using type = VoidElement; };
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
const T *element_cast(const std::unique_ptr<Element> &ptr)
|
||||
{
|
||||
return static_cast<const T *>(ptr.get());
|
||||
}
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
std::unique_ptr<T> element_cast(std::unique_ptr<Element> &&ptr)
|
||||
{
|
||||
return std::unique_ptr<T>(static_cast<T *>(ptr.release()));
|
||||
}
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
std::unique_ptr<T> make_unique_element(Element::Id id, int sizeLength, offset_t dataSize, offset_t offset)
|
||||
{
|
||||
return std::make_unique<T>(id, sizeLength, dataSize, offset);
|
||||
}
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
std::unique_ptr<T> make_unique_element()
|
||||
{
|
||||
return std::make_unique<T>(ID, 0, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
109
taglib/matroska/ebml/ebmlfloatelement.cpp
Normal file
109
taglib/matroska/ebml/ebmlfloatelement.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
|
||||
EBML::FloatElement::FloatElement(Id id, int sizeLength, offset_t dataSize):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::FloatElement::FloatElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::FloatElement::FloatElement(Id id):
|
||||
FloatElement(id, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::FloatElement::FloatVariantType EBML::FloatElement::getValue() const
|
||||
{ return value; }
|
||||
|
||||
double EBML::FloatElement::getValueAsDouble(double defaultValue) const
|
||||
{
|
||||
if(std::holds_alternative<double>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
return *std::get_if<double>(&value);
|
||||
}
|
||||
if(std::holds_alternative<float>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
return *std::get_if<float>(&value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void EBML::FloatElement::setValue(FloatVariantType val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
|
||||
bool EBML::FloatElement::read(File &file)
|
||||
{
|
||||
const 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<double>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
data = ByteVector::fromFloat64BE(*std::get_if<double>(&value));
|
||||
}
|
||||
else if(std::holds_alternative<float>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
data = ByteVector::fromFloat32BE(*std::get_if<float>(&value));
|
||||
}
|
||||
ByteVector buffer = renderId();
|
||||
buffer.append(renderVINT(data.size(), 0));
|
||||
buffer.append(data);
|
||||
return buffer;
|
||||
}
|
||||
59
taglib/matroska/ebml/ebmlfloatelement.h
Normal file
59
taglib/matroska/ebml/ebmlfloatelement.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/***************************************************************************
|
||||
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 <variant>
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class FloatElement : public Element
|
||||
{
|
||||
public:
|
||||
using FloatVariantType = std::variant<std::monostate, float, double>;
|
||||
|
||||
FloatElement(Id id, int sizeLength, offset_t dataSize);
|
||||
FloatElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit FloatElement(Id id);
|
||||
|
||||
FloatVariantType getValue() const;
|
||||
double getValueAsDouble(double defaultValue = 0.0) const;
|
||||
void setValue(FloatVariantType val);
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
|
||||
private:
|
||||
FloatVariantType value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
127
taglib/matroska/ebml/ebmlmasterelement.cpp
Normal file
127
taglib/matroska/ebml/ebmlmasterelement.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmasterelement.h"
|
||||
#include "ebmlvoidelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MasterElement::MasterElement(Id id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
Element(id, sizeLength, dataSize), offset(offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MasterElement::MasterElement(Id id):
|
||||
Element(id, 0, 0), offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MasterElement::~MasterElement() = default;
|
||||
|
||||
offset_t EBML::MasterElement::getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
void EBML::MasterElement::appendElement(std::unique_ptr<Element> &&element)
|
||||
{
|
||||
elements.push_back(std::move(element));
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::iterator EBML::MasterElement::begin()
|
||||
{
|
||||
return elements.begin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::iterator EBML::MasterElement::end()
|
||||
{
|
||||
return elements.end();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::begin() const
|
||||
{
|
||||
return elements.begin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::end() const
|
||||
{
|
||||
return elements.end();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::cbegin() const
|
||||
{
|
||||
return elements.cbegin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::cend() const
|
||||
{
|
||||
return elements.cend();
|
||||
}
|
||||
|
||||
offset_t EBML::MasterElement::getPadding() const
|
||||
{
|
||||
return padding;
|
||||
}
|
||||
|
||||
void EBML::MasterElement::setPadding(offset_t numBytes)
|
||||
{
|
||||
padding = numBytes;
|
||||
}
|
||||
|
||||
offset_t EBML::MasterElement::getMinRenderSize() const
|
||||
{
|
||||
return minRenderSize;
|
||||
}
|
||||
|
||||
void EBML::MasterElement::setMinRenderSize(offset_t minimumSize)
|
||||
{
|
||||
minRenderSize = minimumSize;
|
||||
}
|
||||
|
||||
bool EBML::MasterElement::read(File &file)
|
||||
{
|
||||
const offset_t maxOffset = file.tell() + dataSize;
|
||||
std::unique_ptr<Element> element;
|
||||
while((element = findNextElement(file, maxOffset))) {
|
||||
if(!element->read(file))
|
||||
return false;
|
||||
elements.push_back(std::move(element));
|
||||
}
|
||||
return file.tell() == maxOffset;
|
||||
}
|
||||
|
||||
ByteVector EBML::MasterElement::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
ByteVector data;
|
||||
for(const auto &element : elements)
|
||||
data.append(element->render());
|
||||
dataSize = data.size();
|
||||
buffer.append(renderVINT(dataSize, 0));
|
||||
buffer.append(data);
|
||||
if(minRenderSize) {
|
||||
if(const auto bufferSize = buffer.size();
|
||||
minRenderSize >= bufferSize + MIN_VOID_ELEMENT_SIZE)
|
||||
buffer.append(VoidElement::renderSize(minRenderSize - bufferSize));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
68
taglib/matroska/ebml/ebmlmasterelement.h
Normal file
68
taglib/matroska/ebml/ebmlmasterelement.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLMASTERELEMENT_H
|
||||
#define TAGLIB_EBMLMASTERELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
class ByteVector;
|
||||
|
||||
namespace EBML {
|
||||
class MasterElement : public Element
|
||||
{
|
||||
public:
|
||||
MasterElement(Id id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
explicit MasterElement(Id id);
|
||||
~MasterElement() override;
|
||||
|
||||
offset_t getOffset() const;
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
void appendElement(std::unique_ptr<Element> &&element);
|
||||
std::list<std::unique_ptr<Element>>::iterator begin();
|
||||
std::list<std::unique_ptr<Element>>::iterator end();
|
||||
std::list<std::unique_ptr<Element>>::const_iterator begin() const;
|
||||
std::list<std::unique_ptr<Element>>::const_iterator end() const;
|
||||
std::list<std::unique_ptr<Element>>::const_iterator cbegin() const;
|
||||
std::list<std::unique_ptr<Element>>::const_iterator cend() const;
|
||||
offset_t getPadding() const;
|
||||
void setPadding(offset_t numBytes);
|
||||
offset_t getMinRenderSize() const;
|
||||
void setMinRenderSize(offset_t minimumSize);
|
||||
|
||||
protected:
|
||||
offset_t offset;
|
||||
offset_t padding = 0;
|
||||
offset_t minRenderSize = 0;
|
||||
std::list<std::unique_ptr<Element>> elements;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
81
taglib/matroska/ebml/ebmlmkattachments.cpp
Normal file
81
taglib/matroska/ebml/ebmlmkattachments.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmkattachments.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MkAttachments::MkAttachments(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkAttachments, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkAttachments::MkAttachments(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkAttachments, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkAttachments::MkAttachments():
|
||||
MasterElement(Id::MkAttachments, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Attachments> EBML::MkAttachments::parse() const
|
||||
{
|
||||
auto attachments = std::make_unique<Matroska::Attachments>();
|
||||
attachments->setOffset(offset);
|
||||
attachments->setSize(getSize());
|
||||
|
||||
for(const auto &element : elements) {
|
||||
if(element->getId() != Id::MkAttachedFile)
|
||||
continue;
|
||||
|
||||
const String *filename = nullptr;
|
||||
const String *description = nullptr;
|
||||
const String *mediaType = nullptr;
|
||||
const ByteVector *data = nullptr;
|
||||
Matroska::AttachedFile::UID uid = 0;
|
||||
const auto attachedFile = element_cast<Id::MkAttachedFile>(element);
|
||||
for(const auto &attachedFileChild : *attachedFile) {
|
||||
if(const Id id = attachedFileChild->getId(); id == Id::MkAttachedFileName)
|
||||
filename = &element_cast<Id::MkAttachedFileName>(attachedFileChild)->getValue();
|
||||
else if(id == Id::MkAttachedFileData)
|
||||
data = &element_cast<Id::MkAttachedFileData>(attachedFileChild)->getValue();
|
||||
else if(id == Id::MkAttachedFileDescription)
|
||||
description = &element_cast<Id::MkAttachedFileDescription>(attachedFileChild)->getValue();
|
||||
else if(id == Id::MkAttachedFileMediaType)
|
||||
mediaType = &element_cast<Id::MkAttachedFileMediaType>(attachedFileChild)->getValue();
|
||||
else if(id == Id::MkAttachedFileUID)
|
||||
uid = element_cast<Id::MkAttachedFileUID>(attachedFileChild)->getValue();
|
||||
}
|
||||
if(!(filename && data))
|
||||
continue;
|
||||
|
||||
attachments->addAttachedFile(Matroska::AttachedFile(
|
||||
*data, *filename, mediaType ? *mediaType : String(),
|
||||
uid, description ? *description : String()));
|
||||
}
|
||||
return attachments;
|
||||
}
|
||||
47
taglib/matroska/ebml/ebmlmkattachments.h
Normal file
47
taglib/matroska/ebml/ebmlmkattachments.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLMKATTACHMENTS_H
|
||||
#define TAGLIB_EBMLMKATTACHMENTS_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Attachments;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkAttachments : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkAttachments(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkAttachments(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkAttachments();
|
||||
|
||||
std::unique_ptr<Matroska::Attachments> parse() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
114
taglib/matroska/ebml/ebmlmkchapters.cpp
Normal file
114
taglib/matroska/ebml/ebmlmkchapters.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
/***************************************************************************
|
||||
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 "ebmlmkchapters.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskachapteredition.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MkChapters::MkChapters(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkChapters, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkChapters::MkChapters(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkChapters, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkChapters::MkChapters():
|
||||
MasterElement(Id::MkChapters, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Chapters> EBML::MkChapters::parse() const
|
||||
{
|
||||
auto chapters = std::make_unique<Matroska::Chapters>();
|
||||
chapters->setOffset(offset);
|
||||
chapters->setSize(getSize());
|
||||
|
||||
for(const auto &element : elements) {
|
||||
if(element->getId() != Id::MkEditionEntry)
|
||||
continue;
|
||||
|
||||
List<Matroska::Chapter> editionChapters;
|
||||
Matroska::ChapterEdition::UID editionUid = 0;
|
||||
bool editionIsDefault = false;
|
||||
bool editionIsOrdered = false;
|
||||
const auto edition = element_cast<Id::MkEditionEntry>(element);
|
||||
for(const auto &editionChild : *edition) {
|
||||
if(const Id id = editionChild->getId(); id == Id::MkEditionUID)
|
||||
editionUid = element_cast<Id::MkEditionUID>(editionChild)->getValue();
|
||||
else if(id == Id::MkEditionFlagDefault)
|
||||
editionIsDefault = element_cast<Id::MkEditionFlagDefault>(editionChild)->getValue() != 0;
|
||||
else if(id == Id::MkEditionFlagOrdered)
|
||||
editionIsOrdered = element_cast<Id::MkEditionFlagOrdered>(editionChild)->getValue() != 0;
|
||||
else if(id == Id::MkChapterAtom) {
|
||||
Matroska::Chapter::UID chapterUid = 0;
|
||||
Matroska::Chapter::Time chapterTimeStart = 0;
|
||||
Matroska::Chapter::Time chapterTimeEnd = 0;
|
||||
List<Matroska::Chapter::Display> chapterDisplays;
|
||||
bool chapterHidden = false;
|
||||
const auto chapterAtom = element_cast<Id::MkChapterAtom>(editionChild);
|
||||
for(const auto &chapterChild : *chapterAtom) {
|
||||
if(const Id cid = chapterChild->getId(); cid == Id::MkChapterUID)
|
||||
chapterUid = element_cast<Id::MkChapterUID>(chapterChild)->getValue();
|
||||
else if(cid == Id::MkChapterTimeStart)
|
||||
chapterTimeStart = element_cast<Id::MkChapterTimeStart>(chapterChild)->getValue();
|
||||
else if(cid == Id::MkChapterTimeEnd)
|
||||
chapterTimeEnd = element_cast<Id::MkChapterTimeEnd>(chapterChild)->getValue();
|
||||
else if(cid == Id::MkChapterFlagHidden)
|
||||
chapterHidden = element_cast<Id::MkChapterFlagHidden>(chapterChild)->getValue() != 0;
|
||||
else if(cid == Id::MkChapterDisplay) {
|
||||
const auto display = element_cast<Id::MkChapterDisplay>(chapterChild);
|
||||
String displayString;
|
||||
String displayLanguage;
|
||||
for(const auto &displayChild : *display) {
|
||||
if(const Id did = displayChild->getId(); did == Id::MkChapString)
|
||||
displayString = element_cast<Id::MkChapString>(displayChild)->getValue();
|
||||
else if(did == Id::MkChapLanguage)
|
||||
displayLanguage = element_cast<Id::MkChapLanguage>(displayChild)->getValue();
|
||||
}
|
||||
if(!displayString.isEmpty()) {
|
||||
chapterDisplays.append(Matroska::Chapter::Display(displayString, displayLanguage));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(chapterUid) {
|
||||
editionChapters.append(Matroska::Chapter(
|
||||
chapterTimeStart, chapterTimeEnd, chapterDisplays, chapterUid, chapterHidden));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!editionChapters.isEmpty()) {
|
||||
chapters->addChapterEdition(Matroska::ChapterEdition(
|
||||
editionChapters, editionIsDefault, editionIsOrdered, editionUid));
|
||||
}
|
||||
}
|
||||
return chapters;
|
||||
}
|
||||
52
taglib/matroska/ebml/ebmlmkchapters.h
Normal file
52
taglib/matroska/ebml/ebmlmkchapters.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/***************************************************************************
|
||||
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_EBMLMKCHAPTERS_H
|
||||
#define TAGLIB_EBMLMKCHAPTERS_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Chapters;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkChapters : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkChapters(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkChapters(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkChapters();
|
||||
|
||||
std::unique_ptr<Matroska::Chapters> parse() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
89
taglib/matroska/ebml/ebmlmkcues.cpp
Normal file
89
taglib/matroska/ebml/ebmlmkcues.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmkcues.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "matroskacues.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MkCues::MkCues(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkCues, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkCues::MkCues(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkCues, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkCues::MkCues():
|
||||
MasterElement(Id::MkCues, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Cues> EBML::MkCues::parse(offset_t segmentDataOffset) const
|
||||
{
|
||||
auto cues = std::make_unique<Matroska::Cues>(segmentDataOffset);
|
||||
cues->setOffset(offset);
|
||||
cues->setSize(getSize());
|
||||
cues->setID(static_cast<Matroska::Element::ID>(id));
|
||||
|
||||
for(const auto &cuesChild : elements) {
|
||||
if(cuesChild->getId() != Id::MkCuePoint)
|
||||
continue;
|
||||
const auto cuePointElement = element_cast<Id::MkCuePoint>(cuesChild);
|
||||
auto cuePoint = std::make_unique<Matroska::CuePoint>();
|
||||
|
||||
for(const auto &cuePointChild : *cuePointElement) {
|
||||
if(const Id id = cuePointChild->getId(); id == Id::MkCueTime)
|
||||
cuePoint->setTime(element_cast<Id::MkCueTime>(cuePointChild)->getValue());
|
||||
else if(id == Id::MkCueTrackPositions) {
|
||||
auto cueTrack = std::make_unique<Matroska::CueTrack>();
|
||||
const auto cueTrackElement = element_cast<Id::MkCueTrackPositions>(cuePointChild);
|
||||
for(const auto &cueTrackChild : *cueTrackElement) {
|
||||
if(const Id trackId = cueTrackChild->getId(); trackId == Id::MkCueTrack)
|
||||
cueTrack->setTrackNumber(element_cast<Id::MkCueTrack>(cueTrackChild)->getValue());
|
||||
else if(trackId == Id::MkCueClusterPosition)
|
||||
cueTrack->setClusterPosition(element_cast<Id::MkCueClusterPosition>(cueTrackChild)->getValue());
|
||||
else if(trackId == Id::MkCueRelativePosition)
|
||||
cueTrack->setRelativePosition(element_cast<Id::MkCueRelativePosition>(cueTrackChild)->getValue());
|
||||
else if(trackId == Id::MkCueDuration)
|
||||
cueTrack->setDuration(element_cast<Id::MkCueDuration>(cueTrackChild)->getValue());
|
||||
else if(trackId == Id::MkCueBlockNumber)
|
||||
cueTrack->setBlockNumber(element_cast<Id::MkCueBlockNumber>(cueTrackChild)->getValue());
|
||||
else if(trackId == Id::MkCueCodecState)
|
||||
cueTrack->setCodecState(element_cast<Id::MkCueCodecState>(cueTrackChild)->getValue());
|
||||
else if(trackId == Id::MkCueReference) {
|
||||
const auto cueReference = element_cast<Id::MkCueReference>(cueTrackChild);
|
||||
for(const auto &cueReferenceChild : *cueReference) {
|
||||
if(cueReferenceChild->getId() != Id::MkCueRefTime)
|
||||
continue;
|
||||
cueTrack->addReferenceTime(element_cast<Id::MkCueRefTime>(cueReferenceChild)->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
cuePoint->addCueTrack(std::move(cueTrack));
|
||||
}
|
||||
}
|
||||
cues->addCuePoint(std::move(cuePoint));
|
||||
}
|
||||
return cues;
|
||||
}
|
||||
47
taglib/matroska/ebml/ebmlmkcues.h
Normal file
47
taglib/matroska/ebml/ebmlmkcues.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLMKCUES_H
|
||||
#define TAGLIB_EBMLMKCUES_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Cues;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkCues : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkCues(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkCues(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkCues();
|
||||
|
||||
std::unique_ptr<Matroska::Cues> parse(offset_t segmentDataOffset) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
72
taglib/matroska/ebml/ebmlmkinfo.cpp
Normal file
72
taglib/matroska/ebml/ebmlmkinfo.cpp
Normal file
@@ -0,0 +1,72 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
|
||||
EBML::MkInfo::MkInfo(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkInfo, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkInfo::MkInfo(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkInfo, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkInfo::MkInfo():
|
||||
MasterElement(Id::MkInfo, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
void EBML::MkInfo::parse(Matroska::Properties *properties) const
|
||||
{
|
||||
if(!properties)
|
||||
return;
|
||||
|
||||
unsigned long long timestampScale = 1000000;
|
||||
double duration = 0.0;
|
||||
String title;
|
||||
for(const auto &element : elements) {
|
||||
if(const Id id = element->getId(); id == Id::MkTimestampScale) {
|
||||
timestampScale = element_cast<Id::MkTimestampScale>(element)->getValue();
|
||||
}
|
||||
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);
|
||||
}
|
||||
52
taglib/matroska/ebml/ebmlmkinfo.h
Normal file
52
taglib/matroska/ebml/ebmlmkinfo.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/***************************************************************************
|
||||
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 "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Properties;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkInfo : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkInfo(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkInfo(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkInfo();
|
||||
|
||||
void parse(Matroska::Properties * properties) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
73
taglib/matroska/ebml/ebmlmkseekhead.cpp
Normal file
73
taglib/matroska/ebml/ebmlmkseekhead.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmkseekhead.h"
|
||||
#include "matroskaseekhead.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MkSeekHead::MkSeekHead(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkSeekHead, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkSeekHead::MkSeekHead(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkSeekHead, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkSeekHead::MkSeekHead():
|
||||
MasterElement(Id::MkSeekHead, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::SeekHead> EBML::MkSeekHead::parse(offset_t segmentDataOffset) const
|
||||
{
|
||||
auto seekHead = std::make_unique<Matroska::SeekHead>(segmentDataOffset);
|
||||
seekHead->setOffset(offset);
|
||||
seekHead->setSize(getSize() + padding);
|
||||
|
||||
for(const auto &element : elements) {
|
||||
if(element->getId() != Id::MkSeek)
|
||||
continue;
|
||||
const auto seekElement = element_cast<Id::MkSeek>(element);
|
||||
Matroska::Element::ID entryId = 0;
|
||||
offset_t offset = 0;
|
||||
for(const auto &seekElementChild : *seekElement) {
|
||||
if(const Id id = seekElementChild->getId(); id == Id::MkSeekID && !entryId) {
|
||||
if(auto data = element_cast<Id::MkSeekID>(seekElementChild)->getValue();
|
||||
data.size() == 4)
|
||||
entryId = data.toUInt(true);
|
||||
}
|
||||
else if(id == Id::MkSeekPosition && !offset)
|
||||
offset = element_cast<Id::MkSeekPosition>(seekElementChild)->getValue();
|
||||
}
|
||||
if(entryId && offset)
|
||||
seekHead->addEntry(entryId, offset);
|
||||
else {
|
||||
seekHead.reset();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return seekHead;
|
||||
}
|
||||
47
taglib/matroska/ebml/ebmlmkseekhead.h
Normal file
47
taglib/matroska/ebml/ebmlmkseekhead.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLMKSEEKHEAD_H
|
||||
#define TAGLIB_EBMLMKSEEKHEAD_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class SeekHead;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkSeekHead : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkSeekHead(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkSeekHead(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkSeekHead();
|
||||
|
||||
std::unique_ptr<Matroska::SeekHead> parse(offset_t segmentDataOffset) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
148
taglib/matroska/ebml/ebmlmksegment.cpp
Normal file
148
taglib/matroska/ebml/ebmlmksegment.cpp
Normal file
@@ -0,0 +1,148 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmksegment.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskacues.h"
|
||||
#include "matroskaseekhead.h"
|
||||
#include "matroskasegment.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MkSegment::MkSegment(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkSegment, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkSegment::MkSegment(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkSegment, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkSegment::~MkSegment() = default;
|
||||
|
||||
offset_t EBML::MkSegment::segmentDataOffset() const
|
||||
{
|
||||
return offset + idSize(id) + sizeLength;
|
||||
}
|
||||
|
||||
bool EBML::MkSegment::read(File &file)
|
||||
{
|
||||
const offset_t maxOffset = file.tell() + dataSize;
|
||||
std::unique_ptr<Element> element;
|
||||
int i = 0;
|
||||
int seekHeadIndex = -1;
|
||||
while((element = findNextElement(file, maxOffset))) {
|
||||
if(const Id id = element->getId(); id == Id::MkSeekHead) {
|
||||
seekHeadIndex = i;
|
||||
seekHead = element_cast<Id::MkSeekHead>(std::move(element));
|
||||
if(!seekHead->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkCues) {
|
||||
cues = element_cast<Id::MkCues>(std::move(element));
|
||||
if(!cues->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkInfo) {
|
||||
info = element_cast<Id::MkInfo>(std::move(element));
|
||||
if(!info->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkTracks) {
|
||||
tracks = element_cast<Id::MkTracks>(std::move(element));
|
||||
if(!tracks->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkTags) {
|
||||
tags = element_cast<Id::MkTags>(std::move(element));
|
||||
if(!tags->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkAttachments) {
|
||||
attachments = element_cast<Id::MkAttachments>(std::move(element));
|
||||
if(!attachments->read(file))
|
||||
return false;
|
||||
}
|
||||
else if(id == Id::MkChapters) {
|
||||
chapters = element_cast<Id::MkChapters>(std::move(element));
|
||||
if(!chapters->read(file))
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
if(id == Id::VoidElement
|
||||
&& seekHead
|
||||
&& seekHeadIndex == i - 1)
|
||||
seekHead->setPadding(element->getSize());
|
||||
|
||||
element->skipData(file);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Tag> EBML::MkSegment::parseTag() const
|
||||
{
|
||||
return tags ? tags->parse() : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Attachments> EBML::MkSegment::parseAttachments() const
|
||||
{
|
||||
return attachments ? attachments->parse() : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Chapters> EBML::MkSegment::parseChapters() const
|
||||
{
|
||||
return chapters ? chapters->parse() : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::SeekHead> EBML::MkSegment::parseSeekHead() const
|
||||
{
|
||||
return seekHead ? seekHead->parse(segmentDataOffset()) : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Cues> EBML::MkSegment::parseCues() const
|
||||
{
|
||||
return cues ? cues->parse(segmentDataOffset()) : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Segment> EBML::MkSegment::parseSegment() const
|
||||
{
|
||||
return std::make_unique<Matroska::Segment>(sizeLength, dataSize, offset + idSize(id));
|
||||
}
|
||||
|
||||
void EBML::MkSegment::parseInfo(Matroska::Properties *properties) const
|
||||
{
|
||||
if(info) {
|
||||
info->parse(properties);
|
||||
}
|
||||
}
|
||||
|
||||
void EBML::MkSegment::parseTracks(Matroska::Properties *properties) const
|
||||
{
|
||||
if(tracks) {
|
||||
tracks->parse(properties);
|
||||
}
|
||||
}
|
||||
76
taglib/matroska/ebml/ebmlmksegment.h
Normal file
76
taglib/matroska/ebml/ebmlmksegment.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLMKSEGMENT_H
|
||||
#define TAGLIB_EBMLMKSEGMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "ebmlmktags.h"
|
||||
#include "ebmlmkattachments.h"
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmlmkseekhead.h"
|
||||
#include "ebmlmkcues.h"
|
||||
#include "ebmlmkinfo.h"
|
||||
#include "ebmlmktracks.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Tag;
|
||||
class Attachments;
|
||||
class Chapters;
|
||||
class SeekHead;
|
||||
class Segment;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkSegment : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkSegment(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkSegment(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
~MkSegment() override;
|
||||
|
||||
offset_t segmentDataOffset() const;
|
||||
bool read(File &file) override;
|
||||
std::unique_ptr<Matroska::Tag> parseTag() const;
|
||||
std::unique_ptr<Matroska::Attachments> parseAttachments() const;
|
||||
std::unique_ptr<Matroska::Chapters> parseChapters() const;
|
||||
std::unique_ptr<Matroska::SeekHead> parseSeekHead() const;
|
||||
std::unique_ptr<Matroska::Cues> parseCues() const;
|
||||
std::unique_ptr<Matroska::Segment> parseSegment() const;
|
||||
void parseInfo(Matroska::Properties *properties) const;
|
||||
void parseTracks(Matroska::Properties *properties) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<MkTags> tags;
|
||||
std::unique_ptr<MkAttachments> attachments;
|
||||
std::unique_ptr<MkChapters> chapters;
|
||||
std::unique_ptr<MkSeekHead> seekHead;
|
||||
std::unique_ptr<MkCues> cues;
|
||||
std::unique_ptr<MkInfo> info;
|
||||
std::unique_ptr<MkTracks> tracks;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
131
taglib/matroska/ebml/ebmlmktags.cpp
Normal file
131
taglib/matroska/ebml/ebmlmktags.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmktags.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskasimpletag.h"
|
||||
#include "tlist.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MkTags::MkTags(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkTags, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkTags::MkTags(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkTags, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkTags::MkTags():
|
||||
MasterElement(Id::MkTags, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<Matroska::Tag> EBML::MkTags::parse() const
|
||||
{
|
||||
auto mTag = std::make_unique<Matroska::Tag>();
|
||||
mTag->setOffset(offset);
|
||||
mTag->setSize(getSize());
|
||||
mTag->setID(static_cast<Matroska::Element::ID>(id));
|
||||
|
||||
// Loop through each <Tag> element
|
||||
for(const auto &tagsChild : elements) {
|
||||
if(tagsChild->getId() != Id::MkTag)
|
||||
continue;
|
||||
const auto tag = element_cast<Id::MkTag>(tagsChild);
|
||||
List<const MasterElement *> simpleTags;
|
||||
const MasterElement *targets = nullptr;
|
||||
|
||||
// Identify the <Targets> element and the <SimpleTag> elements
|
||||
for(const auto &tagChild : *tag) {
|
||||
if(const Id tagChildId = tagChild->getId(); !targets && tagChildId == Id::MkTagTargets)
|
||||
targets = element_cast<Id::MkTagTargets>(tagChild);
|
||||
else if(tagChildId == Id::MkSimpleTag)
|
||||
simpleTags.append(element_cast<Id::MkSimpleTag>(tagChild));
|
||||
}
|
||||
|
||||
// Parse the <Targets> element
|
||||
Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None;
|
||||
unsigned long long trackUid = 0;
|
||||
unsigned long long edtionUid = 0;
|
||||
unsigned long long chapterUid = 0;
|
||||
unsigned long long attachmentUid = 0;
|
||||
if(targets) {
|
||||
for(const auto &targetsChild : *targets) {
|
||||
if(const Id id = targetsChild->getId(); id == Id::MkTagTargetTypeValue
|
||||
&& targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None) {
|
||||
targetTypeValue = static_cast<Matroska::SimpleTag::TargetTypeValue>(
|
||||
element_cast<Id::MkTagTargetTypeValue>(targetsChild)->getValue()
|
||||
);
|
||||
}
|
||||
else if(id == Id::MkTagTrackUID) {
|
||||
trackUid = element_cast<Id::MkTagTrackUID>(targetsChild)->getValue();
|
||||
}
|
||||
else if(id == Id::MkTagEditionUID) {
|
||||
edtionUid = element_cast<Id::MkTagEditionUID>(targetsChild)->getValue();
|
||||
}
|
||||
else if(id == Id::MkTagChapterUID) {
|
||||
chapterUid = element_cast<Id::MkTagChapterUID>(targetsChild)->getValue();
|
||||
}
|
||||
else if(id == Id::MkTagAttachmentUID) {
|
||||
attachmentUid = element_cast<Id::MkTagAttachmentUID>(targetsChild)->getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse each <SimpleTag>
|
||||
for(const auto simpleTag : simpleTags) {
|
||||
const String *tagValueString = nullptr;
|
||||
const ByteVector *tagValueBinary = nullptr;
|
||||
String tagName;
|
||||
String language;
|
||||
bool defaultLanguageFlag = true;
|
||||
|
||||
for(const auto &simpleTagChild : *simpleTag) {
|
||||
if(const Id id = simpleTagChild->getId(); id == Id::MkTagName && tagName.isEmpty())
|
||||
tagName = element_cast<Id::MkTagName>(simpleTagChild)->getValue();
|
||||
else if(id == Id::MkTagString && !tagValueString)
|
||||
tagValueString = &element_cast<Id::MkTagString>(simpleTagChild)->getValue();
|
||||
else if(id == Id::MkTagBinary && !tagValueBinary)
|
||||
tagValueBinary = &element_cast<Id::MkTagBinary>(simpleTagChild)->getValue();
|
||||
else if(id == Id::MkTagsTagLanguage && language.isEmpty())
|
||||
language = element_cast<Id::MkTagsTagLanguage>(simpleTagChild)->getValue();
|
||||
else if(id == Id::MkTagsLanguageDefault)
|
||||
defaultLanguageFlag = element_cast<Id::MkTagsLanguageDefault>(simpleTagChild)->getValue() ? true : false;
|
||||
}
|
||||
if(tagName.isEmpty() || (tagValueString && tagValueBinary) || (!tagValueString && !tagValueBinary))
|
||||
continue;
|
||||
|
||||
mTag->addSimpleTag(tagValueString
|
||||
? Matroska::SimpleTag(tagName, *tagValueString,
|
||||
targetTypeValue, language, defaultLanguageFlag,
|
||||
trackUid, edtionUid, chapterUid, attachmentUid)
|
||||
: Matroska::SimpleTag(tagName, *tagValueBinary,
|
||||
targetTypeValue, language, defaultLanguageFlag,
|
||||
trackUid, edtionUid, chapterUid, attachmentUid));
|
||||
}
|
||||
}
|
||||
return mTag;
|
||||
}
|
||||
47
taglib/matroska/ebml/ebmlmktags.h
Normal file
47
taglib/matroska/ebml/ebmlmktags.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLMKTAGS_H
|
||||
#define TAGLIB_EBMLMKTAGS_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Tag;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkTags : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkTags(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkTags(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkTags();
|
||||
|
||||
std::unique_ptr<Matroska::Tag> parse() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
86
taglib/matroska/ebml/ebmlmktracks.cpp
Normal file
86
taglib/matroska/ebml/ebmlmktracks.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
|
||||
EBML::MkTracks::MkTracks(int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkTracks, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkTracks::MkTracks(Id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
MasterElement(Id::MkTracks, sizeLength, dataSize, offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MkTracks::MkTracks():
|
||||
MasterElement(Id::MkTracks, 0, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
void EBML::MkTracks::parse(Matroska::Properties *properties) const
|
||||
{
|
||||
if(!properties)
|
||||
return;
|
||||
|
||||
for(const auto &element : elements) {
|
||||
if(element->getId() != Id::MkTrackEntry)
|
||||
continue;
|
||||
|
||||
String codecId;
|
||||
double samplingFrequency = 0.0;
|
||||
unsigned long long bitDepth = 0;
|
||||
unsigned long long channels = 0;
|
||||
const auto trackEntry = element_cast<Id::MkTrackEntry>(element);
|
||||
for(const auto &trackEntryChild : *trackEntry) {
|
||||
if(const Id trackEntryChildId = trackEntryChild->getId(); trackEntryChildId == Id::MkCodecID)
|
||||
codecId = element_cast<Id::MkCodecID>(trackEntryChild)->getValue();
|
||||
else if(trackEntryChildId == Id::MkAudio) {
|
||||
const auto audio = element_cast<Id::MkAudio>(trackEntryChild);
|
||||
for(const auto &audioChild : *audio) {
|
||||
if(const Id audioChildId = audioChild->getId(); audioChildId == Id::MkSamplingFrequency)
|
||||
samplingFrequency = element_cast<Id::MkSamplingFrequency>(audioChild)->getValueAsDouble();
|
||||
else if(audioChildId == Id::MkBitDepth)
|
||||
bitDepth = element_cast<Id::MkBitDepth>(audioChild)->getValue();
|
||||
else if(audioChildId == Id::MkChannels)
|
||||
channels = element_cast<Id::MkChannels>(audioChild)->getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
if(bitDepth || channels) {
|
||||
properties->setSampleRate(static_cast<int>(samplingFrequency));
|
||||
properties->setBitsPerSample(static_cast<int>(bitDepth));
|
||||
properties->setChannels(static_cast<int>(channels));
|
||||
properties->setCodecName(codecId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
52
taglib/matroska/ebml/ebmlmktracks.h
Normal file
52
taglib/matroska/ebml/ebmlmktracks.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/***************************************************************************
|
||||
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 "taglib.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace Matroska {
|
||||
class Properties;
|
||||
}
|
||||
|
||||
namespace EBML {
|
||||
class MkTracks : public MasterElement
|
||||
{
|
||||
public:
|
||||
MkTracks(int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkTracks(Id, int sizeLength, offset_t dataSize, offset_t offset);
|
||||
MkTracks();
|
||||
|
||||
void parse(Matroska::Properties *properties) const;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
100
taglib/matroska/ebml/ebmlstringelement.cpp
Normal file
100
taglib/matroska/ebml/ebmlstringelement.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlstringelement.h"
|
||||
#include <string>
|
||||
#include "tfile.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tdebug.h"
|
||||
#include "ebmlutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::StringElement::StringElement(
|
||||
String::Type stringEncoding, Id id, int sizeLength, offset_t dataSize):
|
||||
Element(id, sizeLength, dataSize), encoding(stringEncoding)
|
||||
{
|
||||
}
|
||||
|
||||
const String& EBML::StringElement::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void EBML::StringElement::setValue(const String& val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
|
||||
bool EBML::StringElement::read(File &file)
|
||||
{
|
||||
ByteVector buffer = file.readBlock(dataSize);
|
||||
if(buffer.size() != dataSize) {
|
||||
debug("Failed to read string");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The EBML strings aren't supposed to be null-terminated,
|
||||
// but we'll check for it and strip the null terminator if found
|
||||
if(const int nullByte = buffer.find('\0'); nullByte >= 0)
|
||||
buffer = ByteVector(buffer.data(), nullByte);
|
||||
value = String(buffer, encoding);
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteVector EBML::StringElement::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
const std::string string = value.to8Bit(encoding == String::UTF8);
|
||||
dataSize = string.size();
|
||||
buffer.append(renderVINT(dataSize, 0));
|
||||
buffer.append(ByteVector(string.data(), static_cast<unsigned int>(dataSize)));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
EBML::UTF8StringElement::UTF8StringElement(Id id, int sizeLength, offset_t dataSize):
|
||||
StringElement(String::UTF8, id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::UTF8StringElement::UTF8StringElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
UTF8StringElement(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::UTF8StringElement::UTF8StringElement(Id id):
|
||||
UTF8StringElement(id, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Latin1StringElement::Latin1StringElement(Id id, int sizeLength, offset_t dataSize):
|
||||
StringElement(String::Latin1, id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Latin1StringElement::Latin1StringElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Latin1StringElement(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Latin1StringElement::Latin1StringElement(Id id):
|
||||
Latin1StringElement(id, 0, 0)
|
||||
{
|
||||
}
|
||||
65
taglib/matroska/ebml/ebmlstringelement.h
Normal file
65
taglib/matroska/ebml/ebmlstringelement.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLSTRINGELEMENT_H
|
||||
#define TAGLIB_EBMLSTRINGELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
class ByteVector;
|
||||
|
||||
namespace EBML {
|
||||
class StringElement : public Element
|
||||
{
|
||||
public:
|
||||
StringElement(String::Type stringEncoding, Id id, int sizeLength, offset_t dataSize);
|
||||
|
||||
const String &getValue() const;
|
||||
void setValue(const String &val);
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
|
||||
private:
|
||||
String value;
|
||||
String::Type encoding;
|
||||
};
|
||||
|
||||
class UTF8StringElement : public StringElement {
|
||||
public:
|
||||
UTF8StringElement(Id id, int sizeLength, offset_t dataSize);
|
||||
UTF8StringElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit UTF8StringElement(Id id);
|
||||
};
|
||||
|
||||
class Latin1StringElement : public StringElement {
|
||||
public:
|
||||
Latin1StringElement(Id id, int sizeLength, offset_t dataSize);
|
||||
Latin1StringElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit Latin1StringElement(Id id);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
95
taglib/matroska/ebml/ebmluintelement.cpp
Normal file
95
taglib/matroska/ebml/ebmluintelement.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tfile.h"
|
||||
#include "tutils.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::UIntElement::UIntElement(Id id, int sizeLength, offset_t dataSize):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::UIntElement::UIntElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::UIntElement::UIntElement(Id id):
|
||||
UIntElement(id, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
unsigned long long EBML::UIntElement::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void EBML::UIntElement::setValue(unsigned long long val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
|
||||
bool EBML::UIntElement::read(File &file)
|
||||
{
|
||||
const ByteVector buffer = file.readBlock(dataSize);
|
||||
if(buffer.size() != dataSize) {
|
||||
debug("Failed to read EBML Uint element");
|
||||
return false;
|
||||
}
|
||||
value = buffer.toULongLong(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteVector EBML::UIntElement::render()
|
||||
{
|
||||
int dataSize = 0;
|
||||
if(value <= 0xFFull)
|
||||
dataSize = 1;
|
||||
else if(value <= 0xFFFFull)
|
||||
dataSize = 2;
|
||||
else if(value <= 0xFFFFFFull)
|
||||
dataSize = 3;
|
||||
else if(value <= 0xFFFFFFFFull)
|
||||
dataSize = 4;
|
||||
else if(value <= 0xFFFFFFFFFFull)
|
||||
dataSize = 5;
|
||||
else if(value <= 0xFFFFFFFFFFFFull)
|
||||
dataSize = 6;
|
||||
else if(value <= 0xFFFFFFFFFFFFFFull)
|
||||
dataSize = 7;
|
||||
else if(value <= 0xFFFFFFFFFFFFFFFFull)
|
||||
dataSize = 8;
|
||||
|
||||
ByteVector buffer = renderId();
|
||||
buffer.append(renderVINT(dataSize, 0));
|
||||
uint64_t val = value;
|
||||
static const auto byteOrder = Utils::systemByteOrder();
|
||||
if(byteOrder == Utils::LittleEndian)
|
||||
val = Utils::byteSwap(val);
|
||||
|
||||
buffer.append(ByteVector(reinterpret_cast<char *>(&val) + (sizeof(val) - dataSize), dataSize));
|
||||
return buffer;
|
||||
}
|
||||
50
taglib/matroska/ebml/ebmluintelement.h
Normal file
50
taglib/matroska/ebml/ebmluintelement.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLUINTELEMENT_H
|
||||
#define TAGLIB_EBMLUINTELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class UIntElement : public Element
|
||||
{
|
||||
public:
|
||||
UIntElement(Id id, int sizeLength, offset_t dataSize);
|
||||
UIntElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit UIntElement(Id id);
|
||||
|
||||
unsigned long long getValue() const;
|
||||
void setValue(unsigned long long val);
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
|
||||
private:
|
||||
unsigned long long value = 0;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
122
taglib/matroska/ebml/ebmlutils.cpp
Normal file
122
taglib/matroska/ebml/ebmlutils.cpp
Normal file
@@ -0,0 +1,122 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlutils.h"
|
||||
#include <random>
|
||||
#include "tbytevector.h"
|
||||
#include "matroskafile.h"
|
||||
#include "tutils.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
std::unique_ptr<EBML::Element> EBML::findElement(File &file, Element::Id id, offset_t maxOffset)
|
||||
{
|
||||
std::unique_ptr<Element> element;
|
||||
while(file.tell() < maxOffset) {
|
||||
element = Element::factory(file);
|
||||
if(!element || element->getId() == id)
|
||||
return element;
|
||||
element->skipData(file);
|
||||
element.reset();
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
std::unique_ptr<EBML::Element> EBML::findNextElement(File &file, offset_t maxOffset)
|
||||
{
|
||||
return file.tell() < maxOffset ? Element::factory(file) : nullptr;
|
||||
}
|
||||
|
||||
template <int maxSizeLength>
|
||||
unsigned int EBML::VINTSizeLength(uint8_t firstByte)
|
||||
{
|
||||
static_assert(maxSizeLength >= 1 && maxSizeLength <= 8);
|
||||
if(!firstByte) {
|
||||
debug("VINT with greater than 8 bytes not allowed");
|
||||
return 0;
|
||||
}
|
||||
uint8_t mask = 0b10000000;
|
||||
unsigned int numBytes = 1;
|
||||
while(!(mask & firstByte)) {
|
||||
numBytes++;
|
||||
mask >>= 1;
|
||||
}
|
||||
if(numBytes > maxSizeLength) {
|
||||
debug(Utils::formatString("VINT size length exceeds %i bytes", maxSizeLength));
|
||||
return 0;
|
||||
}
|
||||
return numBytes;
|
||||
}
|
||||
|
||||
namespace TagLib::EBML {
|
||||
template unsigned int VINTSizeLength<4>(uint8_t firstByte);
|
||||
template unsigned int VINTSizeLength<8>(uint8_t firstByte);
|
||||
}
|
||||
|
||||
std::pair<unsigned int, uint64_t> EBML::readVINT(File &file)
|
||||
{
|
||||
auto buffer = file.readBlock(1);
|
||||
if(buffer.size() != 1) {
|
||||
debug("Failed to read VINT size");
|
||||
return {0, 0};
|
||||
}
|
||||
unsigned int numBytes = VINTSizeLength<8>(*buffer.begin());
|
||||
if(!numBytes)
|
||||
return {0, 0};
|
||||
|
||||
if(numBytes > 1)
|
||||
buffer.append(file.readBlock(numBytes - 1));
|
||||
const int bitsToShift = static_cast<int>(sizeof(uint64_t) * 8) - 7 * numBytes;
|
||||
const uint64_t mask = 0xFFFFFFFFFFFFFFFF >> bitsToShift;
|
||||
return { numBytes, buffer.toULongLong(true) & mask };
|
||||
}
|
||||
|
||||
std::pair<unsigned int, uint64_t> EBML::parseVINT(const ByteVector &buffer)
|
||||
{
|
||||
if(buffer.isEmpty())
|
||||
return {0, 0};
|
||||
|
||||
unsigned int numBytes = VINTSizeLength<8>(*buffer.begin());
|
||||
if(!numBytes)
|
||||
return {0, 0};
|
||||
|
||||
const int bitsToShift = static_cast<int>(sizeof(uint64_t) * 8) - 7 * numBytes;
|
||||
const uint64_t mask = 0xFFFFFFFFFFFFFFFF >> bitsToShift;
|
||||
return { numBytes, buffer.toULongLong(true) & mask };
|
||||
}
|
||||
|
||||
ByteVector EBML::renderVINT(uint64_t number, int minSizeLength)
|
||||
{
|
||||
const int numBytes = std::max(minSizeLength, minSize(number));
|
||||
number |= 1ULL << (numBytes * 7);
|
||||
static const auto byteOrder = Utils::systemByteOrder();
|
||||
if(byteOrder == Utils::LittleEndian)
|
||||
number = Utils::byteSwap(number);
|
||||
return ByteVector(reinterpret_cast<char *>(&number) + (sizeof(number) - numBytes), numBytes);
|
||||
}
|
||||
|
||||
unsigned long long EBML::randomUID()
|
||||
{
|
||||
static std::random_device device;
|
||||
static std::mt19937 generator(device());
|
||||
static std::uniform_int_distribution<unsigned long long> distribution;
|
||||
return distribution(generator);
|
||||
}
|
||||
78
taglib/matroska/ebml/ebmlutils.h
Normal file
78
taglib/matroska/ebml/ebmlutils.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLUTILS_H
|
||||
#define TAGLIB_EBMLUTILS_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <utility>
|
||||
#include "taglib.h"
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
class ByteVector;
|
||||
|
||||
namespace EBML {
|
||||
std::unique_ptr<Element> findElement(File &file, Element::Id id, offset_t maxOffset);
|
||||
std::unique_ptr<Element> findNextElement(File &file, offset_t maxOffset);
|
||||
|
||||
template <int maxSizeLength>
|
||||
unsigned int VINTSizeLength(uint8_t firstByte);
|
||||
|
||||
std::pair<unsigned int, uint64_t> readVINT(File &file);
|
||||
|
||||
std::pair<unsigned int, uint64_t> parseVINT(const ByteVector &buffer);
|
||||
|
||||
ByteVector renderVINT(uint64_t number, int minSizeLength);
|
||||
|
||||
unsigned long long randomUID();
|
||||
|
||||
constexpr int minSize(uint64_t data)
|
||||
{
|
||||
if(data <= 0x7Fu)
|
||||
return 1;
|
||||
if(data <= 0x3FFFu)
|
||||
return 2;
|
||||
if(data <= 0x1FFFFFu)
|
||||
return 3;
|
||||
if(data <= 0xFFFFFFFu)
|
||||
return 4;
|
||||
if(data <= 0x7FFFFFFFFu)
|
||||
return 5;
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr int idSize(Element::Id id)
|
||||
{
|
||||
const auto uintId = static_cast<unsigned int>(id);
|
||||
if(uintId <= 0xFF)
|
||||
return 1;
|
||||
if(uintId <= 0xFFFF)
|
||||
return 2;
|
||||
if(uintId <= 0xFFFFFF)
|
||||
return 3;
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
73
taglib/matroska/ebml/ebmlvoidelement.cpp
Normal file
73
taglib/matroska/ebml/ebmlvoidelement.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlvoidelement.h"
|
||||
#include <algorithm>
|
||||
#include "ebmlutils.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::VoidElement::VoidElement(int sizeLength, offset_t dataSize):
|
||||
Element(Id::VoidElement, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::VoidElement::VoidElement(Id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Element(Id::VoidElement, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::VoidElement::VoidElement():
|
||||
Element(Id::VoidElement, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVector EBML::VoidElement::render()
|
||||
{
|
||||
offset_t bytesNeeded = targetSize;
|
||||
ByteVector buffer = renderId();
|
||||
bytesNeeded -= buffer.size();
|
||||
sizeLength = static_cast<int>(std::min(bytesNeeded, static_cast<offset_t>(8)));
|
||||
bytesNeeded -= sizeLength;
|
||||
dataSize = bytesNeeded;
|
||||
buffer.append(renderVINT(dataSize, sizeLength));
|
||||
if(dataSize)
|
||||
buffer.append(ByteVector(static_cast<unsigned int>(dataSize), 0));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
offset_t EBML::VoidElement::getTargetSize() const
|
||||
{
|
||||
return targetSize;
|
||||
}
|
||||
|
||||
void EBML::VoidElement::setTargetSize(offset_t size)
|
||||
{
|
||||
this->targetSize = std::max(size, MIN_VOID_ELEMENT_SIZE);
|
||||
}
|
||||
|
||||
ByteVector EBML::VoidElement::renderSize(offset_t targetSize)
|
||||
{
|
||||
VoidElement element;
|
||||
element.setTargetSize(targetSize);
|
||||
return element.render();
|
||||
}
|
||||
51
taglib/matroska/ebml/ebmlvoidelement.h
Normal file
51
taglib/matroska/ebml/ebmlvoidelement.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLVOIDELEMENT_H
|
||||
#define TAGLIB_EBMLVOIDELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
inline constexpr offset_t MIN_VOID_ELEMENT_SIZE = 2;
|
||||
class VoidElement : public Element
|
||||
{
|
||||
public:
|
||||
VoidElement(int sizeLength, offset_t dataSize);
|
||||
VoidElement(Id, int sizeLength, offset_t dataSize, offset_t);
|
||||
VoidElement();
|
||||
|
||||
ByteVector render() override;
|
||||
offset_t getTargetSize() const;
|
||||
void setTargetSize(offset_t size);
|
||||
static ByteVector renderSize(offset_t targetSize);
|
||||
|
||||
private:
|
||||
offset_t targetSize = MIN_VOID_ELEMENT_SIZE;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
99
taglib/matroska/matroskaattachedfile.cpp
Normal file
99
taglib/matroska/matroskaattachedfile.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskaattachedfile.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::AttachedFile::AttachedFilePrivate
|
||||
{
|
||||
public:
|
||||
AttachedFilePrivate(const ByteVector &data, const String &fileName,
|
||||
const String &mediaType, UID uid, const String &description) :
|
||||
fileName(fileName), description(description), mediaType(mediaType),
|
||||
data(data), uid(uid) {}
|
||||
~AttachedFilePrivate() = default;
|
||||
String fileName;
|
||||
String description;
|
||||
String mediaType;
|
||||
ByteVector data;
|
||||
UID uid = 0;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::AttachedFile::AttachedFile(const ByteVector &data,
|
||||
const String &fileName, const String &mediaType, UID uid,
|
||||
const String &description) :
|
||||
d(std::make_unique<AttachedFilePrivate>(data, fileName, mediaType, uid, description))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::AttachedFile::AttachedFile(const AttachedFile &other) :
|
||||
d(std::make_unique<AttachedFilePrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::AttachedFile::AttachedFile(AttachedFile &&other) noexcept = default;
|
||||
|
||||
Matroska::AttachedFile::~AttachedFile() = default;
|
||||
|
||||
Matroska::AttachedFile &Matroska::AttachedFile::operator=(AttachedFile &&other) noexcept = default;
|
||||
|
||||
Matroska::AttachedFile &Matroska::AttachedFile::operator=(const AttachedFile &other)
|
||||
{
|
||||
AttachedFile(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Matroska::AttachedFile::swap(AttachedFile &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
const String &Matroska::AttachedFile::fileName() const
|
||||
{
|
||||
return d->fileName;
|
||||
}
|
||||
|
||||
const String &Matroska::AttachedFile::description() const
|
||||
{
|
||||
return d->description;
|
||||
}
|
||||
|
||||
const String &Matroska::AttachedFile::mediaType() const
|
||||
{
|
||||
return d->mediaType;
|
||||
}
|
||||
|
||||
const ByteVector &Matroska::AttachedFile::data() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
Matroska::AttachedFile::UID Matroska::AttachedFile::uid() const
|
||||
{
|
||||
return d->uid;
|
||||
}
|
||||
110
taglib/matroska/matroskaattachedfile.h
Normal file
110
taglib/matroska/matroskaattachedfile.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKAATTACHEDFILE_H
|
||||
#define TAGLIB_MATROSKAATTACHEDFILE_H
|
||||
|
||||
#include <memory>
|
||||
#include "tstring.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
class String;
|
||||
class ByteVector;
|
||||
|
||||
namespace Matroska {
|
||||
//! Attached file embedded into a Matroska file.
|
||||
class TAGLIB_EXPORT AttachedFile
|
||||
{
|
||||
public:
|
||||
//! Unique identifier.
|
||||
using UID = unsigned long long;
|
||||
|
||||
/*!
|
||||
* Construct an attached file.
|
||||
*/
|
||||
AttachedFile(const ByteVector &data, const String &fileName,
|
||||
const String &mediaType, UID uid = 0,
|
||||
const String &description = String());
|
||||
|
||||
/*!
|
||||
* Construct an attached file as a copy of \a other.
|
||||
*/
|
||||
AttachedFile(const AttachedFile &other);
|
||||
|
||||
/*!
|
||||
* Construct an attached file moving from \a other.
|
||||
*/
|
||||
AttachedFile(AttachedFile &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this attached file.
|
||||
*/
|
||||
~AttachedFile();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
AttachedFile &operator=(const AttachedFile &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
AttachedFile &operator=(AttachedFile &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(AttachedFile &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the filename of the attached file.
|
||||
*/
|
||||
const String &fileName() const;
|
||||
|
||||
/*!
|
||||
* Returns the human-friendly description for the attached file.
|
||||
*/
|
||||
const String &description() const;
|
||||
|
||||
/*!
|
||||
* Returns the media type of the attached file.
|
||||
*/
|
||||
const String &mediaType() const;
|
||||
|
||||
/*!
|
||||
* Returns the data of the attached file.
|
||||
*/
|
||||
const ByteVector &data() const;
|
||||
|
||||
/*!
|
||||
* Returns the UID of the attached file.
|
||||
*/
|
||||
UID uid() const;
|
||||
|
||||
private:
|
||||
class AttachedFilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AttachedFilePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
141
taglib/matroska/matroskaattachments.cpp
Normal file
141
taglib/matroska/matroskaattachments.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "ebmlmkattachments.h"
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Attachments::AttachmentsPrivate
|
||||
{
|
||||
public:
|
||||
AttachmentsPrivate() = default;
|
||||
~AttachmentsPrivate() = default;
|
||||
AttachmentsPrivate(const AttachmentsPrivate &) = delete;
|
||||
AttachmentsPrivate &operator=(const AttachmentsPrivate &) = delete;
|
||||
AttachedFileList files;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Attachments::Attachments() :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkAttachments)),
|
||||
d(std::make_unique<AttachmentsPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Attachments::~Attachments() = default;
|
||||
|
||||
void Matroska::Attachments::addAttachedFile(const AttachedFile &file)
|
||||
{
|
||||
d->files.append(file);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::Attachments::removeAttachedFile(unsigned long long uid)
|
||||
{
|
||||
const auto it = std::find_if(d->files.begin(), d->files.end(),
|
||||
[uid](const AttachedFile &file) {
|
||||
return file.uid() == uid;
|
||||
});
|
||||
if(it != d->files.end()) {
|
||||
d->files.erase(it);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Matroska::Attachments::clear()
|
||||
{
|
||||
d->files.clear();
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
const Matroska::Attachments::AttachedFileList &Matroska::Attachments::attachedFileList() const
|
||||
{
|
||||
return d->files;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Attachments::AttachedFileList &Matroska::Attachments::attachedFiles()
|
||||
{
|
||||
setNeedsRender(true);
|
||||
return d->files;
|
||||
}
|
||||
|
||||
ByteVector Matroska::Attachments::renderInternal()
|
||||
{
|
||||
if(d->files.isEmpty()) {
|
||||
// Avoid writing an Attachments element without AttachedFile element.
|
||||
return {};
|
||||
}
|
||||
|
||||
EBML::MkAttachments attachments;
|
||||
for(const auto &attachedFile : std::as_const(d->files)) {
|
||||
auto attachedFileElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFile>();
|
||||
|
||||
// Filename
|
||||
auto fileNameElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFileName>();
|
||||
fileNameElement->setValue(attachedFile.fileName());
|
||||
attachedFileElement->appendElement(std::move(fileNameElement));
|
||||
|
||||
// Media/MIME type
|
||||
auto mediaTypeElement =
|
||||
EBML::make_unique_element<EBML::Element::Id::MkAttachedFileMediaType>();
|
||||
mediaTypeElement->setValue(attachedFile.mediaType());
|
||||
attachedFileElement->appendElement(std::move(mediaTypeElement));
|
||||
|
||||
// Description
|
||||
if(const String &description = attachedFile.description(); !description.isEmpty()) {
|
||||
auto descriptionElement =
|
||||
EBML::make_unique_element<EBML::Element::Id::MkAttachedFileDescription>();
|
||||
descriptionElement->setValue(description);
|
||||
attachedFileElement->appendElement(std::move(descriptionElement));
|
||||
}
|
||||
|
||||
// Data
|
||||
auto dataElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFileData>();
|
||||
dataElement->setValue(attachedFile.data());
|
||||
attachedFileElement->appendElement(std::move(dataElement));
|
||||
|
||||
// UID
|
||||
auto uidElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFileUID>();
|
||||
AttachedFile::UID uid = attachedFile.uid();
|
||||
if(!uid)
|
||||
uid = EBML::randomUID();
|
||||
uidElement->setValue(uid);
|
||||
attachedFileElement->appendElement(std::move(uidElement));
|
||||
|
||||
attachments.appendElement(std::move(attachedFileElement));
|
||||
}
|
||||
return attachments.render();
|
||||
}
|
||||
83
taglib/matroska/matroskaattachments.h
Normal file
83
taglib/matroska/matroskaattachments.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKAATTACHMENTS_H
|
||||
#define TAGLIB_MATROSKAATTACHMENTS_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "tlist.h"
|
||||
#include "matroskaelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class MkAttachments;
|
||||
}
|
||||
|
||||
namespace Matroska {
|
||||
class AttachedFile;
|
||||
class File;
|
||||
|
||||
//! Collection of attached files.
|
||||
class TAGLIB_EXPORT Attachments
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
: private Element
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
//! List of attached files.
|
||||
using AttachedFileList = List<AttachedFile>;
|
||||
|
||||
//! Construct attachments.
|
||||
Attachments();
|
||||
|
||||
//! Destroy attachments.
|
||||
virtual ~Attachments();
|
||||
|
||||
//! Add an attached file.
|
||||
void addAttachedFile(const AttachedFile &file);
|
||||
|
||||
//! Remove an attached file.
|
||||
void removeAttachedFile(unsigned long long uid);
|
||||
|
||||
//! Remove all attached files.
|
||||
void clear();
|
||||
|
||||
//! Get list of all attached files.
|
||||
const AttachedFileList &attachedFileList() const;
|
||||
|
||||
private:
|
||||
friend class EBML::MkAttachments;
|
||||
friend class File;
|
||||
class AttachmentsPrivate;
|
||||
|
||||
// private Element implementation
|
||||
ByteVector renderInternal() override;
|
||||
AttachedFileList &attachedFiles();
|
||||
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AttachmentsPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
157
taglib/matroska/matroskachapter.cpp
Normal file
157
taglib/matroska/matroskachapter.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
/***************************************************************************
|
||||
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 "matroskachapter.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Chapter::Display::DisplayPrivate
|
||||
{
|
||||
public:
|
||||
DisplayPrivate() = default;
|
||||
~DisplayPrivate() = default;
|
||||
String string;
|
||||
String language;
|
||||
};
|
||||
|
||||
class Matroska::Chapter::ChapterPrivate
|
||||
{
|
||||
public:
|
||||
ChapterPrivate() = default;
|
||||
~ChapterPrivate() = default;
|
||||
UID uid = 0;
|
||||
Time timeStart = 0;
|
||||
Time timeEnd = 0;
|
||||
List<Display> displayList;
|
||||
bool hidden = false;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Chapter::Chapter(Time timeStart, Time timeEnd,
|
||||
const List<Display> &displayList, UID uid, bool hidden) :
|
||||
d(std::make_unique<ChapterPrivate>())
|
||||
{
|
||||
d->uid = uid;
|
||||
d->timeStart = timeStart;
|
||||
d->timeEnd = timeEnd;
|
||||
d->displayList = displayList;
|
||||
d->hidden = hidden;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Chapter(const Chapter &other) :
|
||||
d(std::make_unique<ChapterPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Chapter::Chapter(Chapter &&other) noexcept = default;
|
||||
|
||||
Matroska::Chapter::~Chapter() = default;
|
||||
|
||||
Matroska::Chapter &Matroska::Chapter::operator=(Chapter &&other) noexcept = default;
|
||||
|
||||
Matroska::Chapter &Matroska::Chapter::operator=(const Chapter &other)
|
||||
{
|
||||
Chapter(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Matroska::Chapter::swap(Chapter &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
Matroska::Chapter::UID Matroska::Chapter::uid() const
|
||||
{
|
||||
return d->uid;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Time Matroska::Chapter::timeStart() const
|
||||
{
|
||||
return d->timeStart;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Time Matroska::Chapter::timeEnd() const
|
||||
{
|
||||
return d->timeEnd;
|
||||
}
|
||||
|
||||
bool Matroska::Chapter::isHidden() const
|
||||
{
|
||||
return d->hidden;
|
||||
}
|
||||
|
||||
const List<Matroska::Chapter::Display> &Matroska::Chapter::displayList() const
|
||||
{
|
||||
return d->displayList;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display::Display(const String &string, const String &language) :
|
||||
d(std::make_unique<DisplayPrivate>())
|
||||
{
|
||||
d->string = string;
|
||||
d->language = language;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display::Display(const Display &other) :
|
||||
d(std::make_unique<DisplayPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display::Display(Display &&other) noexcept = default;
|
||||
|
||||
Matroska::Chapter::Display::~Display() = default;
|
||||
|
||||
Matroska::Chapter::Display &Matroska::Chapter::Display::operator=(const Display &other)
|
||||
{
|
||||
Display(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Matroska::Chapter::Display &Matroska::Chapter::Display::operator=(
|
||||
Display &&other) noexcept = default;
|
||||
|
||||
void Matroska::Chapter::Display::swap(Display &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
const String &Matroska::Chapter::Display::string() const
|
||||
{
|
||||
return d->string;
|
||||
}
|
||||
|
||||
const String &Matroska::Chapter::Display::language() const
|
||||
{
|
||||
return d->language;
|
||||
}
|
||||
178
taglib/matroska/matroskachapter.h
Normal file
178
taglib/matroska/matroskachapter.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/***************************************************************************
|
||||
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_MATROSKACHAPTER_H
|
||||
#define TAGLIB_MATROSKACHAPTER_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "tlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
class String;
|
||||
class ByteVector;
|
||||
|
||||
namespace EBML {
|
||||
class MkChapters;
|
||||
}
|
||||
|
||||
namespace Matroska {
|
||||
//! Matroska chapter.
|
||||
class TAGLIB_EXPORT Chapter
|
||||
{
|
||||
public:
|
||||
//! Unique identifier.
|
||||
using UID = unsigned long long;
|
||||
|
||||
//! Timestamp in nanoseconds.
|
||||
using Time = unsigned long long;
|
||||
|
||||
/*!
|
||||
* Contains all possible strings to use for the chapter display.
|
||||
*/
|
||||
class TAGLIB_EXPORT Display
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Construct a chapter display.
|
||||
*/
|
||||
Display(const String &string, const String &language);
|
||||
|
||||
/*!
|
||||
* Construct a chapter display as a copy of \a other.
|
||||
*/
|
||||
Display(const Display &other);
|
||||
|
||||
/*!
|
||||
* Construct a chapter display moving from \a other.
|
||||
*/
|
||||
Display(Display &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this chapter display.
|
||||
*/
|
||||
~Display();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
Display &operator=(const Display &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
Display &operator=(Display &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(Display &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns string representing the chapter.
|
||||
*/
|
||||
const String &string() const;
|
||||
|
||||
/*!
|
||||
* Returns language corresponding to the string.
|
||||
*/
|
||||
const String &language() const;
|
||||
|
||||
private:
|
||||
class DisplayPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<DisplayPrivate> d;
|
||||
};
|
||||
|
||||
/*!
|
||||
* Construct a chapter.
|
||||
*/
|
||||
Chapter(Time timeStart, Time timeEnd, const List<Display> &displayList,
|
||||
UID uid, bool hidden = false);
|
||||
|
||||
/*!
|
||||
* Construct a chapter as a copy of \a other.
|
||||
*/
|
||||
Chapter(const Chapter &other);
|
||||
|
||||
/*!
|
||||
* Construct a chapter moving from \a other.
|
||||
*/
|
||||
Chapter(Chapter &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this chapter.
|
||||
*/
|
||||
~Chapter();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
Chapter &operator=(const Chapter &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
Chapter &operator=(Chapter &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(Chapter &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the UID of the chapter.
|
||||
*/
|
||||
UID uid() const;
|
||||
|
||||
/*!
|
||||
* Returns the timestamp of the start of the chapter in nanoseconds.
|
||||
*/
|
||||
Time timeStart() const;
|
||||
|
||||
/*!
|
||||
* Returns the timestamp of the start of the chapter in nanoseconds.
|
||||
*/
|
||||
Time timeEnd() const;
|
||||
|
||||
/*!
|
||||
* Check if chapter is hidden.
|
||||
*/
|
||||
bool isHidden() const;
|
||||
|
||||
/*!
|
||||
* Returns strings with language.
|
||||
*/
|
||||
const List<Display> &displayList() const;
|
||||
|
||||
private:
|
||||
class ChapterPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ChapterPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
102
taglib/matroska/matroskachapteredition.cpp
Normal file
102
taglib/matroska/matroskachapteredition.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
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 "matroskachapter.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tlist.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::ChapterEdition::ChapterEditionPrivate
|
||||
{
|
||||
public:
|
||||
ChapterEditionPrivate() = default;
|
||||
~ChapterEditionPrivate() = default;
|
||||
List<Chapter> chapters;
|
||||
UID uid = 0;
|
||||
bool flagDefault = false;
|
||||
bool flagOrdered = false;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::ChapterEdition::ChapterEdition(const List<Chapter> &chapterList,
|
||||
bool isDefault, bool isOrdered, UID uid) :
|
||||
d(std::make_unique<ChapterEditionPrivate>())
|
||||
{
|
||||
d->chapters = chapterList;
|
||||
d->uid = uid;
|
||||
d->flagDefault = isDefault;
|
||||
d->flagOrdered = isOrdered;
|
||||
}
|
||||
|
||||
Matroska::ChapterEdition::ChapterEdition(const ChapterEdition &other) :
|
||||
d(std::make_unique<ChapterEditionPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::ChapterEdition::ChapterEdition(ChapterEdition &&other) noexcept = default;
|
||||
|
||||
Matroska::ChapterEdition::~ChapterEdition() = default;
|
||||
|
||||
Matroska::ChapterEdition &Matroska::ChapterEdition::operator=(
|
||||
ChapterEdition &&other) noexcept = default;
|
||||
|
||||
Matroska::ChapterEdition &Matroska::ChapterEdition::operator=(const ChapterEdition &other)
|
||||
{
|
||||
ChapterEdition(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Matroska::ChapterEdition::swap(ChapterEdition &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
Matroska::ChapterEdition::UID Matroska::ChapterEdition::uid() const
|
||||
{
|
||||
return d->uid;
|
||||
}
|
||||
|
||||
bool Matroska::ChapterEdition::isDefault() const
|
||||
{
|
||||
return d->flagDefault;
|
||||
}
|
||||
|
||||
bool Matroska::ChapterEdition::isOrdered() const
|
||||
{
|
||||
return d->flagOrdered;
|
||||
}
|
||||
|
||||
const List<Matroska::Chapter> &Matroska::ChapterEdition::chapterList() const
|
||||
{
|
||||
return d->chapters;
|
||||
}
|
||||
108
taglib/matroska/matroskachapteredition.h
Normal file
108
taglib/matroska/matroskachapteredition.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/***************************************************************************
|
||||
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_MATROSKACHAPTEREDITION_H
|
||||
#define TAGLIB_MATROSKACHAPTEREDITION_H
|
||||
|
||||
#include "matroskachapter.h"
|
||||
|
||||
namespace TagLib {
|
||||
class String;
|
||||
class ByteVector;
|
||||
|
||||
namespace Matroska {
|
||||
//! Edition of chapters.
|
||||
class TAGLIB_EXPORT ChapterEdition
|
||||
{
|
||||
public:
|
||||
//! Unique identifier.
|
||||
using UID = unsigned long long;
|
||||
|
||||
/*!
|
||||
* Construct an edition.
|
||||
*/
|
||||
ChapterEdition(const List<Chapter> &chapterList,
|
||||
bool isDefault, bool isOrdered = false, UID uid = 0);
|
||||
|
||||
/*!
|
||||
* Construct an edition as a copy of \a other.
|
||||
*/
|
||||
ChapterEdition(const ChapterEdition &other);
|
||||
|
||||
/*!
|
||||
* Construct an edition moving from \a other.
|
||||
*/
|
||||
ChapterEdition(ChapterEdition &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this edition.
|
||||
*/
|
||||
~ChapterEdition();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this object.
|
||||
*/
|
||||
ChapterEdition &operator=(const ChapterEdition &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this object.
|
||||
*/
|
||||
ChapterEdition &operator=(ChapterEdition &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the object with the content of \a other.
|
||||
*/
|
||||
void swap(ChapterEdition &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the UID of the edition.
|
||||
*/
|
||||
UID uid() const;
|
||||
|
||||
/*!
|
||||
* Check if this edition should be used as the default one.
|
||||
*/
|
||||
bool isDefault() const;
|
||||
|
||||
/*!
|
||||
* Check if the chapters can be defined multiple times and the order to
|
||||
* play them is enforced.
|
||||
*/
|
||||
bool isOrdered() const;
|
||||
|
||||
/*!
|
||||
* Get the list of all chapters.
|
||||
*/
|
||||
const List<Chapter> &chapterList() const;
|
||||
|
||||
private:
|
||||
class ChapterEditionPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ChapterEditionPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
151
taglib/matroska/matroskachapters.cpp
Normal file
151
taglib/matroska/matroskachapters.cpp
Normal file
@@ -0,0 +1,151 @@
|
||||
/***************************************************************************
|
||||
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 "matroskachapters.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Chapters::ChaptersPrivate
|
||||
{
|
||||
public:
|
||||
ChaptersPrivate() = default;
|
||||
~ChaptersPrivate() = default;
|
||||
ChaptersPrivate(const ChaptersPrivate &) = delete;
|
||||
ChaptersPrivate &operator=(const ChaptersPrivate &) = delete;
|
||||
ChapterEditionList editions;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Chapters::Chapters() :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkChapters)),
|
||||
d(std::make_unique<ChaptersPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Chapters::~Chapters() = default;
|
||||
|
||||
void Matroska::Chapters::addChapterEdition(const ChapterEdition &edition)
|
||||
{
|
||||
d->editions.append(edition);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::Chapters::removeChapterEdition(unsigned long long uid)
|
||||
{
|
||||
const auto it = std::find_if(d->editions.begin(), d->editions.end(),
|
||||
[uid](const ChapterEdition &file) {
|
||||
return file.uid() == uid;
|
||||
});
|
||||
if(it != d->editions.end()) {
|
||||
d->editions.erase(it);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Matroska::Chapters::clear()
|
||||
{
|
||||
d->editions.clear();
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
const Matroska::Chapters::ChapterEditionList &Matroska::Chapters::chapterEditionList() const
|
||||
{
|
||||
return d->editions;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
ByteVector Matroska::Chapters::renderInternal()
|
||||
{
|
||||
if(d->editions.isEmpty()) {
|
||||
// Avoid writing a Chapters element without ChapterEdition element.
|
||||
return {};
|
||||
}
|
||||
|
||||
EBML::MkChapters chapters;
|
||||
for(const auto &chapterEdition : std::as_const(d->editions)) {
|
||||
auto chapterEditionElement = EBML::make_unique_element<EBML::Element::Id::MkEditionEntry>();
|
||||
|
||||
if(const auto uid = chapterEdition.uid()) {
|
||||
auto uidElement = EBML::make_unique_element<EBML::Element::Id::MkEditionUID>();
|
||||
uidElement->setValue(uid);
|
||||
chapterEditionElement->appendElement(std::move(uidElement));
|
||||
}
|
||||
auto defaultElement = EBML::make_unique_element<EBML::Element::Id::MkEditionFlagDefault>();
|
||||
defaultElement->setValue(chapterEdition.isDefault());
|
||||
chapterEditionElement->appendElement(std::move(defaultElement));
|
||||
auto orderedElement = EBML::make_unique_element<EBML::Element::Id::MkEditionFlagOrdered>();
|
||||
orderedElement->setValue(chapterEdition.isOrdered());
|
||||
chapterEditionElement->appendElement(std::move(orderedElement));
|
||||
|
||||
for(const auto &chapter : chapterEdition.chapterList()) {
|
||||
auto chapterElement = EBML::make_unique_element<EBML::Element::Id::MkChapterAtom>();
|
||||
|
||||
auto cuidElement = EBML::make_unique_element<EBML::Element::Id::MkChapterUID>();
|
||||
const auto cuid = chapter.uid();
|
||||
cuidElement->setValue(cuid ? cuid : EBML::randomUID());
|
||||
chapterElement->appendElement(std::move(cuidElement));
|
||||
auto timeStartElement = EBML::make_unique_element<EBML::Element::Id::MkChapterTimeStart>();
|
||||
timeStartElement->setValue(chapter.timeStart());
|
||||
chapterElement->appendElement(std::move(timeStartElement));
|
||||
auto timeEndElement = EBML::make_unique_element<EBML::Element::Id::MkChapterTimeEnd>();
|
||||
timeEndElement->setValue(chapter.timeEnd());
|
||||
chapterElement->appendElement(std::move(timeEndElement));
|
||||
auto hiddenElement = EBML::make_unique_element<EBML::Element::Id::MkChapterFlagHidden>();
|
||||
hiddenElement->setValue(chapter.isHidden());
|
||||
chapterElement->appendElement(std::move(hiddenElement));
|
||||
|
||||
for(const auto &display : chapter.displayList()) {
|
||||
auto displayElement = EBML::make_unique_element<EBML::Element::Id::MkChapterDisplay>();
|
||||
|
||||
auto stringElement = EBML::make_unique_element<EBML::Element::Id::MkChapString>();
|
||||
stringElement->setValue(display.string());
|
||||
displayElement->appendElement(std::move(stringElement));
|
||||
auto languageElement = EBML::make_unique_element<EBML::Element::Id::MkChapLanguage>();
|
||||
languageElement->setValue(display.language());
|
||||
displayElement->appendElement(std::move(languageElement));
|
||||
|
||||
chapterElement->appendElement(std::move(displayElement));
|
||||
}
|
||||
|
||||
chapterEditionElement->appendElement(std::move(chapterElement));
|
||||
}
|
||||
|
||||
chapters.appendElement(std::move(chapterEditionElement));
|
||||
}
|
||||
return chapters.render();
|
||||
}
|
||||
87
taglib/matroska/matroskachapters.h
Normal file
87
taglib/matroska/matroskachapters.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/***************************************************************************
|
||||
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_MATROSKACHAPTERS_H
|
||||
#define TAGLIB_MATROSKACHAPTERS_H
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "tlist.h"
|
||||
#include "matroskaelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class MkChapters;
|
||||
}
|
||||
|
||||
namespace Matroska {
|
||||
class ChapterEdition;
|
||||
class File;
|
||||
|
||||
//! Collection of chapter editions.
|
||||
class TAGLIB_EXPORT Chapters
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
: private Element
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
//! List of chapter editions.
|
||||
using ChapterEditionList = List<ChapterEdition>;
|
||||
|
||||
//! Construct chapters.
|
||||
Chapters();
|
||||
|
||||
//! Destroy chapters.
|
||||
virtual ~Chapters();
|
||||
|
||||
//! Add a chapter edition.
|
||||
void addChapterEdition(const ChapterEdition &edition);
|
||||
|
||||
//! Remove a chapter edition.
|
||||
void removeChapterEdition(unsigned long long uid);
|
||||
|
||||
//! Remove all chapter editions.
|
||||
void clear();
|
||||
|
||||
//! Get list of all chapter editions.
|
||||
const ChapterEditionList &chapterEditionList() const;
|
||||
|
||||
private:
|
||||
friend class EBML::MkChapters;
|
||||
friend class File;
|
||||
class ChaptersPrivate;
|
||||
|
||||
// private Element implementation
|
||||
ByteVector renderInternal() override;
|
||||
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ChaptersPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
312
taglib/matroska/matroskacues.cpp
Normal file
312
taglib/matroska/matroskacues.cpp
Normal file
@@ -0,0 +1,312 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskacues.h"
|
||||
#include "ebmlelement.h"
|
||||
#include "ebmlmkcues.h"
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "tlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "tfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
Matroska::Cues::Cues(offset_t segmentDataOffset) :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkCues)),
|
||||
segmentDataOffset(segmentDataOffset)
|
||||
{
|
||||
setNeedsRender(false);
|
||||
}
|
||||
|
||||
Matroska::Cues::~Cues() = default;
|
||||
|
||||
ByteVector Matroska::Cues::renderInternal()
|
||||
{
|
||||
const auto beforeSize = sizeRenderedOrWritten();
|
||||
EBML::MkCues cues;
|
||||
cues.setMinRenderSize(beforeSize);
|
||||
for(const auto &cuePoint : cuePoints) {
|
||||
auto cuePointElement = EBML::make_unique_element<EBML::Element::Id::MkCuePoint>();
|
||||
auto timestamp = EBML::make_unique_element<EBML::Element::Id::MkCueTime>();
|
||||
timestamp->setValue(cuePoint->getTime());
|
||||
cuePointElement->appendElement(std::move(timestamp));
|
||||
|
||||
const auto &trackList = cuePoint->cueTrackList();
|
||||
for(const auto &cueTrack : trackList) {
|
||||
auto cueTrackElement = EBML::make_unique_element<EBML::Element::Id::MkCueTrackPositions>();
|
||||
|
||||
// Track number
|
||||
auto trackNumber = EBML::make_unique_element<EBML::Element::Id::MkCueTrack>();
|
||||
trackNumber->setValue(cueTrack->getTrackNumber());
|
||||
cueTrackElement->appendElement(std::move(trackNumber));
|
||||
|
||||
// Cluster position
|
||||
auto clusterPosition = EBML::make_unique_element<EBML::Element::Id::MkCueClusterPosition>();
|
||||
clusterPosition->setValue(cueTrack->getClusterPosition());
|
||||
cueTrackElement->appendElement(std::move(clusterPosition));
|
||||
|
||||
// Relative position, optional
|
||||
if(cueTrack->getRelativePosition().has_value()) {
|
||||
auto relativePosition = EBML::make_unique_element<EBML::Element::Id::MkCueRelativePosition>();
|
||||
// operator*() used instead of value() to support restricted compilers
|
||||
relativePosition->setValue(*cueTrack->getRelativePosition());
|
||||
cueTrackElement->appendElement(std::move(relativePosition));
|
||||
}
|
||||
|
||||
// Duration, optional
|
||||
if(cueTrack->getDuration().has_value()) {
|
||||
auto duration = EBML::make_unique_element<EBML::Element::Id::MkCueDuration>();
|
||||
// operator*() used instead of value() to support restricted compilers
|
||||
duration->setValue(*cueTrack->getDuration());
|
||||
cueTrackElement->appendElement(std::move(duration));
|
||||
}
|
||||
|
||||
// Block number, optional
|
||||
if(cueTrack->getBlockNumber().has_value()) {
|
||||
auto blockNumber = EBML::make_unique_element<EBML::Element::Id::MkCueBlockNumber>();
|
||||
// operator*() used instead of value() to support restricted compilers
|
||||
blockNumber->setValue(*cueTrack->getBlockNumber());
|
||||
cueTrackElement->appendElement(std::move(blockNumber));
|
||||
}
|
||||
|
||||
// Codec state, not in version 1
|
||||
if(cueTrack->getCodecState().has_value()) {
|
||||
auto codecState = EBML::make_unique_element<EBML::Element::Id::MkCueCodecState>();
|
||||
// operator*() used instead of value() to support restricted compilers
|
||||
codecState->setValue(*cueTrack->getCodecState());
|
||||
cueTrackElement->appendElement(std::move(codecState));
|
||||
}
|
||||
|
||||
// Reference times
|
||||
if(auto referenceTimes = cueTrack->referenceTimes(); !referenceTimes.isEmpty()) {
|
||||
auto cueReference = EBML::make_unique_element<EBML::Element::Id::MkCueReference>();
|
||||
for(const auto reference : referenceTimes) {
|
||||
auto refTime = EBML::make_unique_element<EBML::Element::Id::MkCueRefTime>();
|
||||
refTime->setValue(reference);
|
||||
cueReference->appendElement(std::move(refTime));
|
||||
}
|
||||
cueTrackElement->appendElement(std::move(cueReference));
|
||||
}
|
||||
cuePointElement->appendElement(std::move(cueTrackElement));
|
||||
}
|
||||
cues.appendElement(std::move(cuePointElement));
|
||||
}
|
||||
return cues.render();
|
||||
}
|
||||
|
||||
void Matroska::Cues::write(TagLib::File &file)
|
||||
{
|
||||
if(!data().isEmpty())
|
||||
Element::write(file);
|
||||
}
|
||||
|
||||
bool Matroska::Cues::sizeChanged(Element &caller, offset_t delta)
|
||||
{
|
||||
// Adjust own offset
|
||||
if(!Element::sizeChanged(caller, delta))
|
||||
return false;
|
||||
|
||||
const offset_t offset = caller.offset() - segmentDataOffset;
|
||||
for(const auto &cuePoint : cuePoints) {
|
||||
if(cuePoint->adjustOffset(offset, delta)) {
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Matroska::Cues::isValid(TagLib::File &file) const
|
||||
{
|
||||
for(const auto &cuePoint : cuePoints) {
|
||||
if(!cuePoint->isValid(file, segmentDataOffset))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Matroska::Cues::addCuePoint(std::unique_ptr<CuePoint> &&cuePoint)
|
||||
{
|
||||
cuePoints.push_back(std::move(cuePoint));
|
||||
}
|
||||
|
||||
const Matroska::Cues::CuePointList &Matroska::Cues::cuePointList()
|
||||
{
|
||||
return cuePoints;
|
||||
}
|
||||
|
||||
Matroska::CuePoint::CuePoint() = default;
|
||||
|
||||
Matroska::CuePoint::~CuePoint() = default;
|
||||
|
||||
bool Matroska::CuePoint::isValid(TagLib::File &file, offset_t segmentDataOffset) const
|
||||
{
|
||||
for(const auto &track : cueTracks) {
|
||||
if(!track->isValid(file, segmentDataOffset))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Matroska::CuePoint::addCueTrack(std::unique_ptr<CueTrack> &&cueTrack)
|
||||
{
|
||||
cueTracks.push_back(std::move(cueTrack));
|
||||
}
|
||||
|
||||
const Matroska::CuePoint::CueTrackList &Matroska::CuePoint::cueTrackList() const
|
||||
{
|
||||
return cueTracks;
|
||||
}
|
||||
|
||||
void Matroska::CuePoint::setTime(Time timestamp)
|
||||
{
|
||||
time = timestamp;
|
||||
}
|
||||
|
||||
Matroska::CuePoint::Time Matroska::CuePoint::getTime() const
|
||||
{
|
||||
return time;
|
||||
}
|
||||
|
||||
bool Matroska::CuePoint::adjustOffset(offset_t offset, offset_t delta)
|
||||
{
|
||||
bool ret = false;
|
||||
for(const auto &cueTrack : cueTracks)
|
||||
ret |= cueTrack->adjustOffset(offset, delta);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Matroska::CueTrack::CueTrack() = default;
|
||||
|
||||
Matroska::CueTrack::~CueTrack() = default;
|
||||
|
||||
bool Matroska::CueTrack::isValid(TagLib::File &file, offset_t segmentDataOffset) const
|
||||
{
|
||||
if(!trackNumber) {
|
||||
debug("Cue track number not set");
|
||||
return false;
|
||||
}
|
||||
if(!clusterPosition) {
|
||||
debug("Cue track cluster position not set");
|
||||
return false;
|
||||
}
|
||||
file.seek(segmentDataOffset + clusterPosition);
|
||||
if(EBML::Element::readId(file) != static_cast<unsigned int>(EBML::Element::Id::MkCluster)) {
|
||||
debug("No cluster found at position");
|
||||
return false;
|
||||
}
|
||||
if(codecState.has_value() && *codecState != 0) {
|
||||
// operator*() used instead of value() to support restricted compilers
|
||||
file.seek(segmentDataOffset + *codecState);
|
||||
if(EBML::Element::readId(file) != static_cast<unsigned int>(EBML::Element::Id::MkCodecState)) {
|
||||
debug("No codec state found at position");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::setTrackNumber(unsigned long long trackNr)
|
||||
{
|
||||
trackNumber = trackNr;
|
||||
}
|
||||
|
||||
unsigned long long Matroska::CueTrack::getTrackNumber() const
|
||||
{
|
||||
return trackNumber;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::setClusterPosition(offset_t clusterPos)
|
||||
{
|
||||
clusterPosition = clusterPos;
|
||||
}
|
||||
|
||||
offset_t Matroska::CueTrack::getClusterPosition() const
|
||||
{
|
||||
return clusterPosition;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::setRelativePosition(std::optional<offset_t> relativePos)
|
||||
{
|
||||
relativePosition = relativePos;
|
||||
}
|
||||
|
||||
std::optional<offset_t> Matroska::CueTrack::getRelativePosition() const
|
||||
{
|
||||
return relativePosition;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::setCodecState(std::optional<offset_t> codecStatePos)
|
||||
{
|
||||
codecState = codecStatePos;
|
||||
}
|
||||
|
||||
std::optional<offset_t> Matroska::CueTrack::getCodecState() const
|
||||
{
|
||||
return codecState;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::setBlockNumber(std::optional<unsigned long long> blockNr)
|
||||
{
|
||||
blockNumber = blockNr;
|
||||
}
|
||||
|
||||
std::optional<unsigned long long> Matroska::CueTrack::getBlockNumber() const
|
||||
{
|
||||
return blockNumber;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::setDuration(std::optional<unsigned long long> segmentTicks)
|
||||
{
|
||||
duration = segmentTicks;
|
||||
}
|
||||
|
||||
std::optional<unsigned long long> Matroska::CueTrack::getDuration() const
|
||||
{
|
||||
return duration;
|
||||
}
|
||||
|
||||
void Matroska::CueTrack::addReferenceTime(unsigned long long refTime)
|
||||
{
|
||||
refTimes.append(refTime);
|
||||
}
|
||||
|
||||
const Matroska::CueTrack::ReferenceTimeList &Matroska::CueTrack::referenceTimes() const
|
||||
{
|
||||
return refTimes;
|
||||
}
|
||||
|
||||
bool Matroska::CueTrack::adjustOffset(offset_t offset, offset_t delta)
|
||||
{
|
||||
bool ret = false;
|
||||
if(clusterPosition > offset) {
|
||||
clusterPosition += delta;
|
||||
ret = true;
|
||||
}
|
||||
// operator*() used instead of value() to support restricted compilers
|
||||
if(offset_t codecStateValue;
|
||||
codecState.has_value() && (codecStateValue = *codecState) != 0 &&
|
||||
codecStateValue > offset) {
|
||||
codecState = codecStateValue + delta;
|
||||
ret = true;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
116
taglib/matroska/matroskacues.h
Normal file
116
taglib/matroska/matroskacues.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKACUES_H
|
||||
#define TAGLIB_MATROSKACUES_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "tlist.h"
|
||||
#include "matroskaelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class MkCues;
|
||||
}
|
||||
|
||||
namespace Matroska {
|
||||
class CuePoint;
|
||||
class CueTrack;
|
||||
|
||||
class Cues : public Element
|
||||
{
|
||||
public:
|
||||
using CuePointList = std::list<std::unique_ptr<CuePoint>>;
|
||||
explicit Cues(offset_t segmentDataOffset);
|
||||
~Cues() override;
|
||||
bool isValid(TagLib::File &file) const;
|
||||
void addCuePoint(std::unique_ptr<CuePoint> &&cuePoint);
|
||||
const CuePointList &cuePointList();
|
||||
bool sizeChanged(Element &caller, offset_t delta) override;
|
||||
void write(TagLib::File &file) override;
|
||||
|
||||
private:
|
||||
friend class EBML::MkCues;
|
||||
ByteVector renderInternal() override;
|
||||
|
||||
CuePointList cuePoints;
|
||||
const offset_t segmentDataOffset;
|
||||
};
|
||||
|
||||
class CuePoint
|
||||
{
|
||||
public:
|
||||
using CueTrackList = std::list<std::unique_ptr<CueTrack>>;
|
||||
using Time = unsigned long long;
|
||||
CuePoint();
|
||||
~CuePoint();
|
||||
bool isValid(TagLib::File &file, offset_t segmentDataOffset) const;
|
||||
void addCueTrack(std::unique_ptr<CueTrack> &&cueTrack);
|
||||
const CueTrackList &cueTrackList() const;
|
||||
void setTime(Time timestamp);
|
||||
Time getTime() const;
|
||||
bool adjustOffset(offset_t offset, offset_t delta);
|
||||
|
||||
private:
|
||||
CueTrackList cueTracks;
|
||||
Time time = 0;
|
||||
};
|
||||
|
||||
class CueTrack
|
||||
{
|
||||
public:
|
||||
using ReferenceTimeList = List<unsigned long long>;
|
||||
CueTrack();
|
||||
~CueTrack();
|
||||
bool isValid(TagLib::File &file, offset_t segmentDataOffset) const;
|
||||
void setTrackNumber(unsigned long long trackNr);
|
||||
unsigned long long getTrackNumber() const;
|
||||
void setClusterPosition(offset_t clusterPos);
|
||||
offset_t getClusterPosition() const;
|
||||
void setRelativePosition(std::optional<offset_t> relativePos);
|
||||
std::optional<offset_t> getRelativePosition() const;
|
||||
void setCodecState(std::optional<offset_t> codecStatePos);
|
||||
std::optional<offset_t> getCodecState() const;
|
||||
void setBlockNumber(std::optional<unsigned long long> blockNr);
|
||||
std::optional<unsigned long long> getBlockNumber() const;
|
||||
void setDuration(std::optional<unsigned long long> segmentTicks);
|
||||
std::optional<unsigned long long> getDuration() const;
|
||||
void addReferenceTime(unsigned long long refTime);
|
||||
const ReferenceTimeList &referenceTimes() const;
|
||||
bool adjustOffset(offset_t offset, offset_t delta);
|
||||
|
||||
private:
|
||||
unsigned long long trackNumber = 0;
|
||||
offset_t clusterPosition = 0;
|
||||
std::optional<offset_t> relativePosition;
|
||||
std::optional<unsigned long long> blockNumber;
|
||||
std::optional<unsigned long long> duration;
|
||||
std::optional<offset_t> codecState;
|
||||
ReferenceTimeList refTimes;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
168
taglib/matroska/matroskaelement.cpp
Normal file
168
taglib/matroska/matroskaelement.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskaelement.h"
|
||||
#include <memory>
|
||||
#include "tlist.h"
|
||||
#include "tfile.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Element::ElementPrivate
|
||||
{
|
||||
public:
|
||||
ElementPrivate() = default;
|
||||
~ElementPrivate() = default;
|
||||
ElementPrivate(const ElementPrivate &) = delete;
|
||||
ElementPrivate &operator=(const ElementPrivate &) = delete;
|
||||
offset_t size = 0;
|
||||
offset_t offset = 0;
|
||||
ID id = 0;
|
||||
ByteVector data;
|
||||
List<Element *> sizeListeners;
|
||||
// The default write() implementation will delete an unrendered element,
|
||||
// therefore rendering is required by default and needs to be explicitly set
|
||||
// using setNeedsRender(false) together with overriding the write() method.
|
||||
bool needsRender = true;
|
||||
};
|
||||
|
||||
Matroska::Element::Element(ID id) :
|
||||
e(std::make_unique<ElementPrivate>())
|
||||
{
|
||||
e->id = id;
|
||||
}
|
||||
|
||||
Matroska::Element::~Element() = default;
|
||||
|
||||
offset_t Matroska::Element::size() const
|
||||
{
|
||||
return e->size;
|
||||
}
|
||||
|
||||
offset_t Matroska::Element::offset() const
|
||||
{
|
||||
return e->offset;
|
||||
}
|
||||
|
||||
void Matroska::Element::setData(const ByteVector &data)
|
||||
{
|
||||
e->data = data;
|
||||
}
|
||||
|
||||
const ByteVector &Matroska::Element::data() const
|
||||
{
|
||||
return e->data;
|
||||
}
|
||||
|
||||
void Matroska::Element::setOffset(offset_t offset)
|
||||
{
|
||||
e->offset = offset;
|
||||
}
|
||||
|
||||
void Matroska::Element::adjustOffset(offset_t delta)
|
||||
{
|
||||
e->offset += delta;
|
||||
}
|
||||
|
||||
void Matroska::Element::setSize(offset_t size)
|
||||
{
|
||||
e->size = size;
|
||||
}
|
||||
|
||||
Matroska::Element::ID Matroska::Element::id() const
|
||||
{
|
||||
return e->id;
|
||||
}
|
||||
|
||||
void Matroska::Element::addSizeListener(Element *element)
|
||||
{
|
||||
e->sizeListeners.append(element);
|
||||
}
|
||||
|
||||
void Matroska::Element::addSizeListeners(const List<Element *> &elements)
|
||||
{
|
||||
e->sizeListeners.append(elements);
|
||||
}
|
||||
|
||||
void Matroska::Element::setID(ID id)
|
||||
{
|
||||
e->id = id;
|
||||
}
|
||||
|
||||
bool Matroska::Element::render()
|
||||
{
|
||||
if(!needsRender())
|
||||
return true;
|
||||
|
||||
const auto beforeSize = sizeRenderedOrWritten();
|
||||
const auto data = renderInternal();
|
||||
setNeedsRender(false);
|
||||
if(const auto afterSize = data.size(); afterSize != beforeSize) {
|
||||
if(!emitSizeChanged(afterSize - beforeSize)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
setData(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Matroska::Element::setNeedsRender(bool needsRender)
|
||||
{
|
||||
e->needsRender = needsRender;
|
||||
}
|
||||
|
||||
bool Matroska::Element::needsRender() const
|
||||
{
|
||||
return e->needsRender;
|
||||
}
|
||||
|
||||
bool Matroska::Element::emitSizeChanged(offset_t delta)
|
||||
{
|
||||
for(const auto element : e->sizeListeners) {
|
||||
if(!element->sizeChanged(*this, delta))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Matroska::Element::sizeChanged(Element &caller, offset_t delta)
|
||||
{
|
||||
// The equal case is needed when multiple new elements are added
|
||||
// (e.g. Attachments and Tags), they will start with the same offset
|
||||
// are updated via size change handling.
|
||||
if(caller.offset() <= e->offset && caller.id() != e->id) {
|
||||
e->offset += delta;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
offset_t Matroska::Element::sizeRenderedOrWritten() const
|
||||
{
|
||||
const offset_t dataSize = e->data.size();
|
||||
return dataSize != 0 ? dataSize : e->size;
|
||||
}
|
||||
|
||||
void Matroska::Element::write(File &file)
|
||||
{
|
||||
file.insert(e->data, e->offset, e->size);
|
||||
e->size = e->data.size();
|
||||
}
|
||||
74
taglib/matroska/matroskaelement.h
Normal file
74
taglib/matroska/matroskaelement.h
Normal file
@@ -0,0 +1,74 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKAELEMENT_H
|
||||
#define TAGLIB_MATROSKAELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <memory>
|
||||
#include "taglib_export.h"
|
||||
#include "taglib.h"
|
||||
#include "tlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
class ByteVector;
|
||||
|
||||
namespace Matroska {
|
||||
class TAGLIB_EXPORT Element
|
||||
{
|
||||
public:
|
||||
using ID = unsigned int;
|
||||
explicit Element(ID id);
|
||||
virtual ~Element();
|
||||
|
||||
offset_t size() const;
|
||||
offset_t offset() const;
|
||||
ID id() const;
|
||||
void setOffset(offset_t offset);
|
||||
void adjustOffset(offset_t delta);
|
||||
void setSize(offset_t size);
|
||||
void setID(ID id);
|
||||
virtual bool render();
|
||||
void setNeedsRender(bool needsRender);
|
||||
bool needsRender() const;
|
||||
void setData(const ByteVector &data);
|
||||
const ByteVector &data() const;
|
||||
virtual void write(TagLib::File &file);
|
||||
void addSizeListener(Element *element);
|
||||
void addSizeListeners(const List<Element *> &elements);
|
||||
bool emitSizeChanged(offset_t delta);
|
||||
virtual bool sizeChanged(Element &caller, offset_t delta);
|
||||
|
||||
protected:
|
||||
offset_t sizeRenderedOrWritten() const;
|
||||
|
||||
private:
|
||||
virtual ByteVector renderInternal() = 0;
|
||||
|
||||
class ElementPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ElementPrivate> e;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
559
taglib/matroska/matroskafile.cpp
Normal file
559
taglib/matroska/matroskafile.cpp
Normal file
@@ -0,0 +1,559 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskafile.h"
|
||||
#include <memory>
|
||||
#include "matroskatag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskachapter.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskaseekhead.h"
|
||||
#include "matroskacues.h"
|
||||
#include "matroskasegment.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "ebmlelement.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlmksegment.h"
|
||||
#include "tlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "tagutils.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() = default;
|
||||
~FilePrivate() = default;
|
||||
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
std::unique_ptr<Tag> tag;
|
||||
std::unique_ptr<Attachments> attachments;
|
||||
std::unique_ptr<Chapters> chapters;
|
||||
std::unique_ptr<SeekHead> seekHead;
|
||||
std::unique_ptr<Cues> cues;
|
||||
std::unique_ptr<Segment> segment;
|
||||
std::unique_ptr<Properties> properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool Matroska::File::isSupported(IOStream *stream)
|
||||
{
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return id.startsWith("\x1A\x45\xDF\xA3");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle readStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(!isOpen()) {
|
||||
debug("Failed to open matroska file");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
read(readProperties, readStyle);
|
||||
}
|
||||
|
||||
Matroska::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle readStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(!isOpen()) {
|
||||
debug("Failed to open matroska file");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
read(readProperties, readStyle);
|
||||
}
|
||||
|
||||
Matroska::File::~File() = default;
|
||||
|
||||
Matroska::Properties *Matroska::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
Tag *Matroska::File::tag() const
|
||||
{
|
||||
return tag(true);
|
||||
}
|
||||
|
||||
Matroska::Tag *Matroska::File::tag(bool create) const
|
||||
{
|
||||
if(!d->tag && create) {
|
||||
d->tag = std::make_unique<Tag>();
|
||||
if(d->properties) {
|
||||
d->tag->setSegmentTitle(d->properties->title());
|
||||
}
|
||||
}
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap Matroska::File::properties() const
|
||||
{
|
||||
return d->tag ? d->tag->properties() : PropertyMap();
|
||||
}
|
||||
|
||||
void Matroska::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
if(d->tag) {
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap Matroska::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(!d->tag) {
|
||||
d->tag = std::make_unique<Tag>();
|
||||
}
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
String keyForAttachedFile(const Matroska::AttachedFile &attachedFile)
|
||||
{
|
||||
if(attachedFile.mediaType().startsWith("image/")) {
|
||||
return "PICTURE";
|
||||
}
|
||||
if(!attachedFile.fileName().isEmpty()) {
|
||||
return attachedFile.fileName();
|
||||
}
|
||||
if(!attachedFile.mediaType().isEmpty()) {
|
||||
return attachedFile.mediaType();
|
||||
}
|
||||
return String::fromULongLong(attachedFile.uid());
|
||||
}
|
||||
|
||||
bool keyMatchesAttachedFile(const String &key, const Matroska::AttachedFile &attachedFile)
|
||||
{
|
||||
return !key.isEmpty() && (
|
||||
(key == "PICTURE" && attachedFile.mediaType().startsWith("image/")) ||
|
||||
key == attachedFile.fileName() ||
|
||||
key == attachedFile.mediaType() ||
|
||||
key == String::fromULongLong(attachedFile.uid())
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
StringList Matroska::File::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys = TagLib::File::complexPropertyKeys();
|
||||
if(d->attachments) {
|
||||
const auto &attachedFiles = d->attachments->attachedFileList();
|
||||
for(const auto &attachedFile : attachedFiles) {
|
||||
if(String key = keyForAttachedFile(attachedFile);
|
||||
!key.isEmpty() && !keys.contains(key)) {
|
||||
keys.append(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(d->chapters && !d->chapters->chapterEditionList().isEmpty()) {
|
||||
keys.append("CHAPTERS");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> Matroska::File::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props = TagLib::File::complexProperties(key);
|
||||
if(key.upper() == "CHAPTERS") {
|
||||
if(d->chapters) {
|
||||
for(const auto &edition : d->chapters->chapterEditionList()) {
|
||||
VariantMap property;
|
||||
if(const auto uid = edition.uid()) {
|
||||
property.insert("uid", uid);
|
||||
}
|
||||
if(const auto isDefault = edition.isDefault()) {
|
||||
property.insert("isDefault", isDefault);
|
||||
}
|
||||
if(const auto isOrdered = edition.isOrdered()) {
|
||||
property.insert("isOrdered", isOrdered);
|
||||
}
|
||||
if(auto chapters = edition.chapterList(); !chapters.isEmpty()) {
|
||||
VariantList chaps;
|
||||
for(const auto &chapter : chapters) {
|
||||
VariantMap chap;
|
||||
if(const auto uid = chapter.uid()) {
|
||||
chap.insert("uid", uid);
|
||||
}
|
||||
if(const auto isHidden = chapter.isHidden()) {
|
||||
chap.insert("isHidden", isHidden);
|
||||
}
|
||||
chap.insert("timeStart", chapter.timeStart());
|
||||
if(const auto timeEnd = chapter.timeEnd()) {
|
||||
chap.insert("timeEnd", timeEnd);
|
||||
}
|
||||
if(auto displays = chapter.displayList(); !displays.isEmpty()) {
|
||||
VariantList disps;
|
||||
for(const auto &display : displays) {
|
||||
VariantMap disp;
|
||||
if(auto str = display.string(); !str.isEmpty()) {
|
||||
disp.insert("string", str);
|
||||
}
|
||||
if(auto language = display.language(); !language.isEmpty()) {
|
||||
disp.insert("language", language);
|
||||
}
|
||||
disps.append(disp);
|
||||
}
|
||||
chap.insert("displays", disps);
|
||||
}
|
||||
chaps.append(chap);
|
||||
}
|
||||
property.insert("chapters", chaps);
|
||||
}
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(d->attachments) {
|
||||
const auto &attachedFiles = d->attachments->attachedFileList();
|
||||
for(const auto &attachedFile : attachedFiles) {
|
||||
if(keyMatchesAttachedFile(key, attachedFile)) {
|
||||
VariantMap property;
|
||||
property.insert("data", attachedFile.data());
|
||||
property.insert("mimeType", attachedFile.mediaType());
|
||||
property.insert("description", attachedFile.description());
|
||||
property.insert("fileName", attachedFile.fileName());
|
||||
property.insert("uid", attachedFile.uid());
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool Matroska::File::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
if(TagLib::File::setComplexProperties(key, value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(key.upper() == "CHAPTERS") {
|
||||
chapters(true)->clear();
|
||||
for(const auto &ed : value) {
|
||||
List<Chapter> editionChapters;
|
||||
const auto chaps = ed.value("chapters").toList();
|
||||
for(const auto &chapVar : chaps) {
|
||||
auto chap = chapVar.toMap();
|
||||
const auto disps = chap.value("displays").toList();
|
||||
List<Chapter::Display> chapterDisplays;
|
||||
for(const auto &dispVar : disps) {
|
||||
auto disp = dispVar.toMap();
|
||||
chapterDisplays.append(Chapter::Display(
|
||||
disp.value("string").toString(),
|
||||
disp.value("language").toString()));
|
||||
}
|
||||
editionChapters.append(Chapter(
|
||||
chap.value("timeStart").toULongLong(),
|
||||
chap.value("timeEnd").toULongLong(),
|
||||
chapterDisplays,
|
||||
chap.value("uid", 0ULL).toULongLong(),
|
||||
chap.value("isHidden", false).toBool()));
|
||||
}
|
||||
d->chapters->addChapterEdition(ChapterEdition(
|
||||
editionChapters,
|
||||
ed.value("isDefault", false).toBool(),
|
||||
ed.value("isOrdered", false).toBool(),
|
||||
ed.value("uid", 0ULL).toULongLong()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
List<AttachedFile> &files = attachments(true)->attachedFiles();
|
||||
for(auto it = files.begin(); it != files.end();) {
|
||||
if(keyMatchesAttachedFile(key, *it)) {
|
||||
it = files.erase(it);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
for(const auto &property : value) {
|
||||
if(property.isEmpty())
|
||||
continue;
|
||||
auto mimeType = property.value("mimeType").value<String>();
|
||||
auto data = property.value("data").value<ByteVector>();
|
||||
auto fileName = property.value("fileName").value<String>();
|
||||
auto uid = property.value("uid").value<unsigned long long>();
|
||||
bool ok;
|
||||
if(key.upper() == "PICTURE" && !mimeType.startsWith("image/")) {
|
||||
mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
|
||||
? "image/png" : "image/jpeg";
|
||||
}
|
||||
else if(mimeType.isEmpty() && key.find("/") != -1) {
|
||||
mimeType = key;
|
||||
}
|
||||
else if(fileName.isEmpty() && key.find(".") != -1) {
|
||||
fileName = key;
|
||||
}
|
||||
else if(unsigned long long uidKey;
|
||||
!uid && ((uidKey = key.toULongLong(&ok))) && ok) {
|
||||
uid = uidKey;
|
||||
}
|
||||
if(fileName.isEmpty() && !mimeType.isEmpty()) {
|
||||
const int slashPos = mimeType.rfind('/');
|
||||
String ext = mimeType.substr(slashPos + 1);
|
||||
if(ext == "jpeg") {
|
||||
ext = "jpg";
|
||||
}
|
||||
fileName = "attachment." + ext;
|
||||
}
|
||||
if(!mimeType.isEmpty() && !fileName.isEmpty()) {
|
||||
d->attachments->addAttachedFile(AttachedFile(
|
||||
data, fileName, mimeType, uid,
|
||||
property.value("description").value<String>()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Matroska::Attachments *Matroska::File::attachments(bool create) const
|
||||
{
|
||||
if(!d->attachments && create)
|
||||
d->attachments = std::make_unique<Attachments>();
|
||||
return d->attachments.get();
|
||||
}
|
||||
|
||||
Matroska::Chapters *Matroska::File::chapters(bool create) const
|
||||
{
|
||||
if(!d->chapters && create)
|
||||
d->chapters = std::make_unique<Chapters>();
|
||||
return d->chapters.get();
|
||||
}
|
||||
|
||||
void Matroska::File::read(bool readProperties, Properties::ReadStyle readStyle)
|
||||
{
|
||||
const offset_t fileLength = length();
|
||||
|
||||
// Find the EBML Header
|
||||
const auto head = EBML::element_cast<EBML::Element::Id::EBMLHeader>(
|
||||
EBML::Element::factory(*this));
|
||||
if(!head || head->getId() != EBML::Element::Id::EBMLHeader) {
|
||||
debug("Failed to find EBML head");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(readProperties) {
|
||||
head->read(*this);
|
||||
}
|
||||
else {
|
||||
head->skipData(*this);
|
||||
}
|
||||
|
||||
// Find the Matroska segment in the file
|
||||
const std::unique_ptr<EBML::MkSegment> segment(
|
||||
EBML::element_cast<EBML::Element::Id::MkSegment>(
|
||||
EBML::findElement(*this, EBML::Element::Id::MkSegment, fileLength - tell())
|
||||
)
|
||||
);
|
||||
if(!segment) {
|
||||
debug("Failed to find Matroska segment");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read the segment into memory from file
|
||||
if(!segment->read(*this)) {
|
||||
debug("Failed to read segment");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the elements
|
||||
d->segment = segment->parseSegment();
|
||||
d->seekHead = segment->parseSeekHead();
|
||||
d->cues = segment->parseCues();
|
||||
d->tag = segment->parseTag();
|
||||
d->attachments = segment->parseAttachments();
|
||||
d->chapters = segment->parseChapters();
|
||||
|
||||
if(readProperties) {
|
||||
d->properties = std::make_unique<Properties>(this);
|
||||
|
||||
for(const auto &element : *head) {
|
||||
if(const auto id = element->getId(); id == EBML::Element::Id::DocType) {
|
||||
d->properties->setDocType(
|
||||
EBML::element_cast<EBML::Element::Id::DocType>(element)->getValue());
|
||||
}
|
||||
else if(id == EBML::Element::Id::DocTypeVersion) {
|
||||
d->properties->setDocTypeVersion(static_cast<int>(
|
||||
EBML::element_cast<EBML::Element::Id::DocTypeVersion>(element)->getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
segment->parseInfo(d->properties.get());
|
||||
segment->parseTracks(d->properties.get());
|
||||
if(d->tag) {
|
||||
d->tag->setSegmentTitle(d->properties->title());
|
||||
}
|
||||
}
|
||||
|
||||
if(readStyle == AudioProperties::Accurate &&
|
||||
((d->seekHead && !d->seekHead->isValid(*this)) ||
|
||||
(d->cues && !d->cues->isValid(*this)))) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
setValid(true);
|
||||
}
|
||||
|
||||
bool Matroska::File::save()
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("Matroska::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
if(!isValid()) {
|
||||
debug("Matroska::File::save() -- File is not valid.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not create new attachments, chapters or tags and corresponding
|
||||
// seek head entries if only empty objects were created.
|
||||
if(d->chapters && d->chapters->chapterEditionList().isEmpty() &&
|
||||
d->chapters->size() == 0 && d->chapters->offset() == 0 &&
|
||||
d->chapters->data().isEmpty()) {
|
||||
d->chapters.reset();
|
||||
}
|
||||
if(d->attachments && d->attachments->attachedFileList().isEmpty() &&
|
||||
d->attachments->size() == 0 && d->attachments->offset() == 0 &&
|
||||
d->attachments->data().isEmpty()) {
|
||||
d->attachments.reset();
|
||||
}
|
||||
if(d->tag && d->tag->isEmpty() &&
|
||||
d->tag->size() == 0 && d->tag->offset() == 0 &&
|
||||
d->tag->data().isEmpty()) {
|
||||
d->tag.reset();
|
||||
}
|
||||
|
||||
List<Element *> renderList;
|
||||
List<Element *> newElements;
|
||||
|
||||
// List of all possible elements we can write
|
||||
List<Element *> elements {
|
||||
d->chapters.get(),
|
||||
d->attachments.get(),
|
||||
d->tag.get()
|
||||
};
|
||||
|
||||
/* Build render list. New elements will be added
|
||||
* to the end of the file. For new elements,
|
||||
* the order is from least likely to change,
|
||||
* to most likely to change:
|
||||
* 1. Chapters
|
||||
* 2. Attachments
|
||||
* 3. Tags
|
||||
*/
|
||||
for(auto element : elements) {
|
||||
if(!element)
|
||||
continue;
|
||||
if(element->size())
|
||||
renderList.append(element);
|
||||
else {
|
||||
element->setOffset(length());
|
||||
newElements.append(element);
|
||||
}
|
||||
}
|
||||
if(renderList.isEmpty() && newElements.isEmpty())
|
||||
return true;
|
||||
|
||||
auto sortAscending = [](const auto a, const auto b) { return a->offset() < b->offset(); };
|
||||
renderList.sort(sortAscending);
|
||||
renderList.append(newElements);
|
||||
|
||||
// Add our new elements to the Seek Head (if the file has one)
|
||||
if(d->seekHead) {
|
||||
const auto segmentDataOffset = d->segment->dataOffset();
|
||||
for(const auto element : newElements)
|
||||
d->seekHead->addEntry(element->id(), element->offset() - segmentDataOffset);
|
||||
d->seekHead->sort();
|
||||
}
|
||||
|
||||
// Set up listeners, add seek head and segment length to the end
|
||||
for(auto it = renderList.begin(); it != renderList.end(); ++it) {
|
||||
for(auto it2 = std::next(it); it2 != renderList.end(); ++it2)
|
||||
(*it)->addSizeListener(*it2);
|
||||
if(d->cues)
|
||||
(*it)->addSizeListener(d->cues.get());
|
||||
if(d->seekHead)
|
||||
(*it)->addSizeListener(d->seekHead.get());
|
||||
(*it)->addSizeListener(d->segment.get());
|
||||
}
|
||||
if(d->cues) {
|
||||
renderList.append(d->cues.get());
|
||||
d->cues->addSizeListeners(renderList);
|
||||
if(d->seekHead) {
|
||||
d->cues->addSizeListener(d->seekHead.get());
|
||||
}
|
||||
d->cues->addSizeListener(d->segment.get());
|
||||
}
|
||||
if(d->seekHead) {
|
||||
renderList.append(d->seekHead.get());
|
||||
d->seekHead->addSizeListeners(renderList);
|
||||
d->seekHead->addSizeListener(d->segment.get());
|
||||
}
|
||||
d->segment->addSizeListeners(renderList);
|
||||
renderList.append(d->segment.get());
|
||||
|
||||
// Render the elements.
|
||||
// Because size changes of elements can cause segment offset updates and
|
||||
// size changes in other elements, we might need multiple rounds until no more
|
||||
// element needs rendering.
|
||||
int renderRound = 0;
|
||||
bool rendering = true;
|
||||
while(rendering && renderRound < 5) {
|
||||
rendering = false;
|
||||
for(const auto element : renderList) {
|
||||
if(element->needsRender()) {
|
||||
rendering = true;
|
||||
if(!element->render()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
++renderRound;
|
||||
}
|
||||
|
||||
// Write out to file
|
||||
renderList.sort(sortAscending);
|
||||
for(const auto element : renderList)
|
||||
element->write(*this);
|
||||
|
||||
return true;
|
||||
}
|
||||
186
taglib/matroska/matroskafile.h
Normal file
186
taglib/matroska/matroskafile.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKAFILE_H
|
||||
#define TAGLIB_MATROSKAFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
#include "matroskaproperties.h"
|
||||
|
||||
//! An implementation of Matroska metadata
|
||||
namespace TagLib::Matroska {
|
||||
class Tag;
|
||||
class Attachments;
|
||||
class Chapters;
|
||||
|
||||
//! An implementation of TagLib::File with Matroska specific methods
|
||||
|
||||
/*!
|
||||
* Implementation of TagLib::File for Matroska.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs a Matroska file from \a file. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If \a readStyle is \c Accurate all seek head and cues segment positions
|
||||
* are verified for the isValid() state of the file.
|
||||
*/
|
||||
explicit File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle readStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska file from \a stream. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If \a readStyle is \c Accurate all seek head and cues segment positions
|
||||
* are verified for the isValid() state of the file.
|
||||
*/
|
||||
explicit File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle readStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the tag of the file.
|
||||
*
|
||||
* It will create a tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note The tag <b>is still</b> owned by the Matroska::File and should not
|
||||
* be deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the Matroska tag of the file.
|
||||
*
|
||||
* If \a create is \c false this may return a null pointer if there is no tag.
|
||||
* If \a create is \c true it will create a tag if one does not exist and
|
||||
* returns a valid pointer.
|
||||
*
|
||||
* \note The tag <b>is still</b> owned by the Matroska::File and should not
|
||||
* be deleted by the user. It will be deleted when the file (object)
|
||||
* destroyed.
|
||||
*/
|
||||
Tag *tag(bool create) const;
|
||||
|
||||
/*!
|
||||
* Implements the reading part of the unified property interface.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the writing part of the unified tag dictionary interface.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the keys for attached files, "PICTURE" for images, the media
|
||||
* type, file name or UID for other attached files.
|
||||
* The names of the binary simple tags are included too.
|
||||
*/
|
||||
StringList complexPropertyKeys() const override;
|
||||
|
||||
/*!
|
||||
* Get the pictures stored in the attachments as complex properties
|
||||
* for \a key "PICTURE". Other attached files can be retrieved, by
|
||||
* media type, file name or UID.
|
||||
* The attached files are returned as maps with keys "data", "mimeType",
|
||||
* "description", "fileName, "uid".
|
||||
* Binary simple tags can be retrieved as maps with keys "data", "name",
|
||||
* "targetTypeValue", "language", "defaultLanguage".
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
|
||||
/*!
|
||||
* Set attached files as complex properties \a value, e.g. pictures for
|
||||
* \a key "PICTURE" with the maps in \a value having keys "data", "mimeType",
|
||||
* "description", "fileName, "uid". For other attached files, the mime type,
|
||||
* file name or UID can be used as the \a key.
|
||||
* Maps with keys "name" (with the same value as \a key) and "data" are
|
||||
* stored as binary simple tags with additional keys "targetTypeValue",
|
||||
* "language", "defaultLanguage".
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
/*!
|
||||
* Returns the Matroska::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns \c true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the attachments of the file.
|
||||
*
|
||||
* If \a create is \c false this may return a null pointer if there are no
|
||||
* attachments.
|
||||
* If \a create is \c true it will create attachments if none exist and
|
||||
* returns a valid pointer.
|
||||
*/
|
||||
Attachments *attachments(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the chapters of the file.
|
||||
*
|
||||
* If \a create is \c false this may return a null pointer if there are no
|
||||
* chapters.
|
||||
* If \a create is \c true it will create chapters if none exist and
|
||||
* returns a valid pointer.
|
||||
*/
|
||||
Chapters *chapters(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* 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;
|
||||
friend class Properties;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
155
taglib/matroska/matroskaproperties.cpp
Normal file
155
taglib/matroska/matroskaproperties.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/***************************************************************************
|
||||
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"
|
||||
|
||||
#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;
|
||||
String title;
|
||||
String docType;
|
||||
int docTypeVersion { 0 };
|
||||
int length { 0 };
|
||||
int bitrate { -1 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::Properties::Properties(File *file, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>(file))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Properties::~Properties() = default;
|
||||
|
||||
int Matroska::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int Matroska::Properties::bitrate() const
|
||||
{
|
||||
if(d->bitrate == -1) {
|
||||
d->bitrate = d->length != 0 ? static_cast<int>(d->file->length() * 8 / d->length) : 0;
|
||||
}
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int Matroska::Properties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int Matroska::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int Matroska::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
String Matroska::Properties::docType() const
|
||||
{
|
||||
return d->docType;
|
||||
}
|
||||
|
||||
int Matroska::Properties::docTypeVersion() const
|
||||
{
|
||||
return d->docTypeVersion;
|
||||
}
|
||||
|
||||
String Matroska::Properties::codecName() const
|
||||
{
|
||||
return d->codecName;
|
||||
}
|
||||
|
||||
String Matroska::Properties::title() const
|
||||
{
|
||||
return d->title;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Matroska::Properties::setLengthInMilliseconds(int length)
|
||||
{
|
||||
d->length = length;
|
||||
}
|
||||
|
||||
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::setDocType(const String &docType)
|
||||
{
|
||||
d->docType = docType;
|
||||
}
|
||||
|
||||
void Matroska::Properties::setDocTypeVersion(int docTypeVersion)
|
||||
{
|
||||
d->docTypeVersion = docTypeVersion;
|
||||
}
|
||||
|
||||
void Matroska::Properties::setCodecName(const String &codecName)
|
||||
{
|
||||
d->codecName = codecName;
|
||||
}
|
||||
|
||||
void Matroska::Properties::setTitle(const String &title)
|
||||
{
|
||||
d->title = title;
|
||||
}
|
||||
127
taglib/matroska/matroskaproperties.h
Normal file
127
taglib/matroska/matroskaproperties.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
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_MATROSKAPROPERTIES_H
|
||||
#define TAGLIB_MATROSKAPROPERTIES_H
|
||||
|
||||
#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;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns the EBML doc type, "matroska" or "webm".
|
||||
*/
|
||||
String docType() const;
|
||||
|
||||
/*!
|
||||
* Returns the EBML doc type version, typical values are 2 or 4.
|
||||
*/
|
||||
int docTypeVersion() const;
|
||||
|
||||
/*!
|
||||
* Returns the concrete codec name, for example "A_MPEG/L3"
|
||||
* used in the file if available, otherwise an empty string.
|
||||
*/
|
||||
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;
|
||||
friend class EBML::MkTracks;
|
||||
friend class File;
|
||||
|
||||
void setLengthInMilliseconds(int length);
|
||||
void setSampleRate(int sampleRate);
|
||||
void setChannels(int channels);
|
||||
void setBitsPerSample(int bitsPerSample);
|
||||
void setDocType(const String &docType);
|
||||
void setDocTypeVersion(int docTypeVersion);
|
||||
void setCodecName(const String &codecName);
|
||||
void setTitle(const String &title);
|
||||
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
133
taglib/matroska/matroskaseekhead.cpp
Normal file
133
taglib/matroska/matroskaseekhead.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskaseekhead.h"
|
||||
#include "ebmlmkseekhead.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "tfile.h"
|
||||
#include "tutils.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
Matroska::SeekHead::SeekHead(offset_t segmentDataOffset) :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkSeekHead)),
|
||||
segmentDataOffset(segmentDataOffset)
|
||||
{
|
||||
setNeedsRender(false);
|
||||
}
|
||||
|
||||
Matroska::SeekHead::~SeekHead() = default;
|
||||
|
||||
bool Matroska::SeekHead::isValid(TagLib::File &file) const
|
||||
{
|
||||
bool result = true;
|
||||
for(const auto &[id, offset] : entries) {
|
||||
file.seek(segmentDataOffset + offset);
|
||||
if(EBML::Element::readId(file) != id) {
|
||||
debug(Utils::formatString("No ID %x found at seek position", id));
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Matroska::SeekHead::addEntry(const Element &element)
|
||||
{
|
||||
entries.append({element.id(), element.offset()});
|
||||
debug("adding to seekhead");
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::SeekHead::addEntry(ID id, offset_t offset)
|
||||
{
|
||||
entries.append({id, offset});
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
ByteVector Matroska::SeekHead::renderInternal()
|
||||
{
|
||||
const auto beforeSize = sizeRenderedOrWritten();
|
||||
EBML::MkSeekHead seekHead;
|
||||
seekHead.setMinRenderSize(beforeSize);
|
||||
for(const auto &[id, position] : entries) {
|
||||
auto seekElement = EBML::make_unique_element<EBML::Element::Id::MkSeek>();
|
||||
auto idElement = EBML::make_unique_element<EBML::Element::Id::MkSeekID>();
|
||||
idElement->setValue(ByteVector::fromUInt(id, true));
|
||||
seekElement->appendElement(std::move(idElement));
|
||||
|
||||
auto positionElement = EBML::make_unique_element<EBML::Element::Id::MkSeekPosition>();
|
||||
positionElement->setValue(static_cast<unsigned long long>(position));
|
||||
seekElement->appendElement(std::move(positionElement));
|
||||
|
||||
seekHead.appendElement(std::move(seekElement));
|
||||
}
|
||||
return seekHead.render();
|
||||
}
|
||||
|
||||
void Matroska::SeekHead::write(File &file)
|
||||
{
|
||||
if(!data().isEmpty())
|
||||
Element::write(file);
|
||||
}
|
||||
|
||||
void Matroska::SeekHead::sort()
|
||||
{
|
||||
entries.sort([](const auto &a, const auto &b) { return a.second < b.second; });
|
||||
}
|
||||
|
||||
bool Matroska::SeekHead::sizeChanged(Element &caller, offset_t delta)
|
||||
{
|
||||
ID callerID = caller.id();
|
||||
if(callerID == static_cast<ID>(EBML::Element::Id::MkSegment)) {
|
||||
adjustOffset(delta);
|
||||
return true;
|
||||
}
|
||||
// The equal case is needed when multiple new elements are added
|
||||
// (e.g. Attachments and Tags), they will start with the same offset
|
||||
// and are updated via size change handling.
|
||||
offset_t offset = caller.offset() - segmentDataOffset;
|
||||
auto it = entries.begin();
|
||||
while(it != entries.end()) {
|
||||
it = std::find_if(it,
|
||||
entries.end(),
|
||||
[offset, callerID](const auto &a) {
|
||||
return a.second >= offset && a.first != callerID;
|
||||
}
|
||||
);
|
||||
if(it != entries.end()) {
|
||||
it->second += delta;
|
||||
setNeedsRender(true);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if(caller.data().isEmpty() && caller.size() + delta == 0) {
|
||||
// The caller element is removed, remove it from the seek head.
|
||||
it = std::find_if(entries.begin(), entries.end(),
|
||||
[callerID](const auto &a){ return a.first == callerID; });
|
||||
if(it != entries.end()) {
|
||||
entries.erase(it);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
55
taglib/matroska/matroskaseekhead.h
Normal file
55
taglib/matroska/matroskaseekhead.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKASEEKHEAD_H
|
||||
#define TAGLIB_MATROSKASEEKHEAD_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "matroskaelement.h"
|
||||
#include "tlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
class ByteVector;
|
||||
|
||||
namespace Matroska {
|
||||
class SeekHead : public Element
|
||||
{
|
||||
public:
|
||||
explicit SeekHead(offset_t segmentDataOffset);
|
||||
~SeekHead() override;
|
||||
|
||||
bool isValid(TagLib::File &file) const;
|
||||
void addEntry(const Element &element);
|
||||
void addEntry(ID id, offset_t offset);
|
||||
void write(TagLib::File &file) override;
|
||||
void sort();
|
||||
bool sizeChanged(Element &caller, offset_t delta) override;
|
||||
|
||||
private:
|
||||
ByteVector renderInternal() override;
|
||||
List<std::pair<unsigned int, offset_t>> entries;
|
||||
const offset_t segmentDataOffset;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
71
taglib/matroska/matroskasegment.cpp
Normal file
71
taglib/matroska/matroskasegment.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskasegment.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
Matroska::Segment::Segment(offset_t sizeLength, offset_t dataSize, offset_t lengthOffset) :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkSegment)),
|
||||
sizeLength(sizeLength), dataSize(dataSize)
|
||||
{
|
||||
setOffset(lengthOffset);
|
||||
setSize(sizeLength);
|
||||
}
|
||||
|
||||
Matroska::Segment::~Segment() = default;
|
||||
|
||||
ByteVector Matroska::Segment::renderInternal()
|
||||
{
|
||||
return EBML::renderVINT(dataSize, static_cast<int>(sizeLength));
|
||||
}
|
||||
|
||||
bool Matroska::Segment::render()
|
||||
{
|
||||
const auto beforeSize = sizeLength;
|
||||
auto data = renderInternal();
|
||||
setNeedsRender(false);
|
||||
if(auto afterSize = data.size(); afterSize != beforeSize) {
|
||||
sizeLength = 8;
|
||||
data = renderInternal();
|
||||
setNeedsRender(false);
|
||||
afterSize = data.size();
|
||||
if(!emitSizeChanged(afterSize - beforeSize)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
setData(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Matroska::Segment::sizeChanged(Element &, offset_t delta)
|
||||
{
|
||||
dataSize += delta;
|
||||
setNeedsRender(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
offset_t Matroska::Segment::dataOffset() const
|
||||
{
|
||||
return offset() + sizeLength;
|
||||
}
|
||||
46
taglib/matroska/matroskasegment.h
Normal file
46
taglib/matroska/matroskasegment.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKASEGMENT_H
|
||||
#define TAGLIB_MATROSKASEGMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "matroskaelement.h"
|
||||
|
||||
namespace TagLib::Matroska {
|
||||
class Segment : public Element
|
||||
{
|
||||
public:
|
||||
Segment(offset_t sizeLength, offset_t dataSize, offset_t lengthOffset);
|
||||
~Segment() override;
|
||||
bool render() override;
|
||||
bool sizeChanged(Element &caller, offset_t delta) override;
|
||||
offset_t dataOffset() const;
|
||||
|
||||
private:
|
||||
ByteVector renderInternal() override;
|
||||
|
||||
offset_t sizeLength;
|
||||
offset_t dataSize;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
188
taglib/matroska/matroskasimpletag.cpp
Normal file
188
taglib/matroska/matroskasimpletag.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskasimpletag.h"
|
||||
#include <variant>
|
||||
#include "matroskatag.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::SimpleTag::SimpleTagPrivate
|
||||
{
|
||||
public:
|
||||
SimpleTagPrivate(const String &name, const String &value,
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid, unsigned long long editionUid,
|
||||
unsigned long long chapterUid, unsigned long long attachmentUid) :
|
||||
value(value), name(name), language(language), trackUid(trackUid),
|
||||
editionUid(editionUid), chapterUid(chapterUid), attachmentUid(attachmentUid),
|
||||
targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {}
|
||||
SimpleTagPrivate(const String &name, const ByteVector &value,
|
||||
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid, unsigned long long editionUid,
|
||||
unsigned long long chapterUid, unsigned long long attachmentUid) :
|
||||
value(value), name(name), language(language), trackUid(trackUid),
|
||||
editionUid(editionUid), chapterUid(chapterUid), attachmentUid(attachmentUid),
|
||||
targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {}
|
||||
|
||||
const std::variant<String, ByteVector> value;
|
||||
const String name;
|
||||
const String language;
|
||||
const unsigned long long trackUid;
|
||||
const unsigned long long editionUid;
|
||||
const unsigned long long chapterUid;
|
||||
const unsigned long long attachmentUid;
|
||||
const TargetTypeValue targetTypeValue;
|
||||
const bool defaultLanguageFlag;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const String &name, const String &value,
|
||||
TargetTypeValue targetTypeValue,
|
||||
const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid) :
|
||||
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
|
||||
language, defaultLanguage, trackUid, 0, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const String &name, const String &value,
|
||||
TargetTypeValue targetTypeValue,
|
||||
const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid,
|
||||
unsigned long long editionUid,
|
||||
unsigned long long chapterUid,
|
||||
unsigned long long attachmentUid) :
|
||||
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
|
||||
language, defaultLanguage, trackUid, editionUid, chapterUid, attachmentUid))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const String &name, const ByteVector &value,
|
||||
TargetTypeValue targetTypeValue,
|
||||
const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid) :
|
||||
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
|
||||
language, defaultLanguage, trackUid, 0, 0, 0))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const String &name, const ByteVector &value,
|
||||
TargetTypeValue targetTypeValue,
|
||||
const String &language, bool defaultLanguage,
|
||||
unsigned long long trackUid,
|
||||
unsigned long long editionUid,
|
||||
unsigned long long chapterUid,
|
||||
unsigned long long attachmentUid) :
|
||||
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
|
||||
language, defaultLanguage, trackUid, editionUid, chapterUid, attachmentUid))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(const SimpleTag &other) :
|
||||
d(std::make_unique<SimpleTagPrivate>(*other.d))
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::SimpleTag(SimpleTag &&other) noexcept = default;
|
||||
|
||||
Matroska::SimpleTag::~SimpleTag() = default;
|
||||
|
||||
Matroska::SimpleTag &Matroska::SimpleTag::operator=(SimpleTag &&other) noexcept = default;
|
||||
|
||||
Matroska::SimpleTag &Matroska::SimpleTag::operator=(const SimpleTag &other)
|
||||
{
|
||||
SimpleTag(other).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Matroska::SimpleTag::swap(SimpleTag &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::TargetTypeValue Matroska::SimpleTag::targetTypeValue() const
|
||||
{
|
||||
return d->targetTypeValue;
|
||||
}
|
||||
|
||||
const String &Matroska::SimpleTag::name() const
|
||||
{
|
||||
return d->name;
|
||||
}
|
||||
|
||||
const String &Matroska::SimpleTag::language() const
|
||||
{
|
||||
return d->language;
|
||||
}
|
||||
|
||||
bool Matroska::SimpleTag::defaultLanguageFlag() const
|
||||
{
|
||||
return d->defaultLanguageFlag;
|
||||
}
|
||||
|
||||
unsigned long long Matroska::SimpleTag::trackUid() const
|
||||
{
|
||||
return d->trackUid;
|
||||
}
|
||||
|
||||
unsigned long long Matroska::SimpleTag::editionUid() const
|
||||
{
|
||||
return d->editionUid;
|
||||
}
|
||||
|
||||
unsigned long long Matroska::SimpleTag::chapterUid() const
|
||||
{
|
||||
return d->chapterUid;
|
||||
}
|
||||
|
||||
unsigned long long Matroska::SimpleTag::attachmentUid() const
|
||||
{
|
||||
return d->attachmentUid;
|
||||
}
|
||||
|
||||
Matroska::SimpleTag::ValueType Matroska::SimpleTag::type() const
|
||||
{
|
||||
return std::holds_alternative<ByteVector>(d->value) ? BinaryType : StringType;
|
||||
}
|
||||
|
||||
String Matroska::SimpleTag::toString() const
|
||||
{
|
||||
if(std::holds_alternative<String>(d->value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
return *std::get_if<String>(&d->value);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
ByteVector Matroska::SimpleTag::toByteVector() const
|
||||
{
|
||||
if(std::holds_alternative<ByteVector>(d->value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
return *std::get_if<ByteVector>(&d->value);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
185
taglib/matroska/matroskasimpletag.h
Normal file
185
taglib/matroska/matroskasimpletag.h
Normal file
@@ -0,0 +1,185 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKASIMPLETAG_H
|
||||
#define TAGLIB_MATROSKASIMPLETAG_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
class String;
|
||||
class ByteVector;
|
||||
|
||||
namespace Matroska {
|
||||
//! Attribute of Matroska metadata.
|
||||
class TAGLIB_EXPORT SimpleTag
|
||||
{
|
||||
public:
|
||||
//! Specifies the level of other elements the tag value applies to.
|
||||
enum TargetTypeValue {
|
||||
None = 0, //!< Empty or omitted, everything in the segment
|
||||
Shot = 10, //!< Shot
|
||||
Subtrack = 20, //!< Subtrack / movement / scene
|
||||
Track = 30, //!< Track / song / chapter
|
||||
Part = 40, //!< Part / session
|
||||
Album = 50, //!< Album / opera / concert / movie / episode
|
||||
Edition = 60, //!< Edition / issue / volume / opus / season / sequel
|
||||
Collection = 70 //!< Collection
|
||||
};
|
||||
|
||||
//! The types the value can have.
|
||||
enum ValueType {
|
||||
StringType = 0, //!< Item contains text information coded in UTF-8
|
||||
BinaryType = 1 //!< Item contains binary information
|
||||
};
|
||||
|
||||
/*!
|
||||
* Construct a string simple tag.
|
||||
*/
|
||||
SimpleTag(const String &name, const String &value,
|
||||
TargetTypeValue targetTypeValue = None,
|
||||
const String &language = String(), bool defaultLanguage = true,
|
||||
unsigned long long trackUid = 0);
|
||||
|
||||
/*!
|
||||
* Construct a string simple tag.
|
||||
*/
|
||||
// BIC: merge with constructor above
|
||||
SimpleTag(const String& name, const String& value, TargetTypeValue targetTypeValue, const String& language,
|
||||
bool defaultLanguage, unsigned long long trackUid, unsigned long long editionUid,
|
||||
unsigned long long chapterUid = 0, unsigned long long attachmentUid = 0);
|
||||
|
||||
/*!
|
||||
* Construct a binary simple tag.
|
||||
*/
|
||||
SimpleTag(const String &name, const ByteVector &value,
|
||||
TargetTypeValue targetTypeValue = None,
|
||||
const String &language = String(), bool defaultLanguage = true,
|
||||
unsigned long long trackUid = 0);
|
||||
|
||||
/*!
|
||||
* Construct a binary simple tag.
|
||||
*/
|
||||
// BIC: merge with constructor above
|
||||
SimpleTag(const String& name, const ByteVector& value, TargetTypeValue targetTypeValue, const String& language,
|
||||
bool defaultLanguage, unsigned long long trackUid, unsigned long long editionUid,
|
||||
unsigned long long chapterUid = 0, unsigned long long attachmentUid = 0);
|
||||
|
||||
/*!
|
||||
* Construct a simple tag as a copy of \a other.
|
||||
*/
|
||||
SimpleTag(const SimpleTag &other);
|
||||
|
||||
/*!
|
||||
* Construct a simple tag moving from \a other.
|
||||
*/
|
||||
SimpleTag(SimpleTag &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys this simple tag.
|
||||
*/
|
||||
~SimpleTag();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
*/
|
||||
SimpleTag &operator=(const SimpleTag &other);
|
||||
|
||||
/*!
|
||||
* Moves the contents of \a other into this item.
|
||||
*/
|
||||
SimpleTag &operator=(SimpleTag &&other) noexcept;
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the simple tag with the content of \a other.
|
||||
*/
|
||||
void swap(SimpleTag &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the name of the simple tag.
|
||||
*/
|
||||
const String &name() const;
|
||||
|
||||
/*!
|
||||
* Returns the logical level of the target.
|
||||
*/
|
||||
TargetTypeValue targetTypeValue() const;
|
||||
|
||||
/*!
|
||||
* Returns the language of the tag.
|
||||
*/
|
||||
const String &language() const;
|
||||
|
||||
/*!
|
||||
* Returns if this is the default/original language to use for the tag.
|
||||
*/
|
||||
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 UID that identifies the edition that the tags belong to,
|
||||
* zero if not defined, the tag applies to all editions
|
||||
*/
|
||||
unsigned long long editionUid() const;
|
||||
|
||||
/*!
|
||||
* Returns the UID that identifies the chapter that the tags belong to,
|
||||
* zero if not defined, the tag applies to all chapters
|
||||
*/
|
||||
unsigned long long chapterUid() const;
|
||||
|
||||
/*!
|
||||
* Returns the UID that identifies the attachment that the tags belong to,
|
||||
* zero if not defined, the tag applies to all attachments
|
||||
*/
|
||||
unsigned long long attachmentUid() const;
|
||||
|
||||
/*!
|
||||
* Returns the type of the value.
|
||||
*/
|
||||
ValueType type() const;
|
||||
|
||||
/*!
|
||||
* Returns the StringType value.
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
/*!
|
||||
* Returns the BinaryType value.
|
||||
*/
|
||||
ByteVector toByteVector() const;
|
||||
|
||||
private:
|
||||
class SimpleTagPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<SimpleTagPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
660
taglib/matroska/matroskatag.cpp
Normal file
660
taglib/matroska/matroskatag.cpp
Normal file
@@ -0,0 +1,660 @@
|
||||
/***************************************************************************
|
||||
* 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 "matroskatag.h"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmlmktags.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class Matroska::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate() = default;
|
||||
~TagPrivate() = default;
|
||||
|
||||
bool setTag(const String &key, const String &value);
|
||||
String getTag(const String &key) const;
|
||||
|
||||
template <typename T>
|
||||
int removeSimpleTags(T &&p)
|
||||
{
|
||||
auto &list = tags;
|
||||
int numRemoved = 0;
|
||||
for(auto it = list.begin(); it != list.end();) {
|
||||
it = std::find_if(it, list.end(), std::forward<T>(p));
|
||||
if(it != list.end()) {
|
||||
it = list.erase(it);
|
||||
numRemoved++;
|
||||
}
|
||||
}
|
||||
return numRemoved;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SimpleTagsList findSimpleTags(T &&p)
|
||||
{
|
||||
auto &list = tags;
|
||||
for(auto it = list.begin(); it != list.end();) {
|
||||
it = std::find_if(it, list.end(), std::forward<T>(p));
|
||||
if(it != list.end()) {
|
||||
list.append(*it);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
SimpleTagsList tags;
|
||||
ByteVector data;
|
||||
String segmentTitle;
|
||||
};
|
||||
|
||||
Matroska::Tag::Tag() :
|
||||
Element(static_cast<ID>(EBML::Element::Id::MkTags)),
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Matroska::Tag::~Tag() = default;
|
||||
|
||||
void Matroska::Tag::addSimpleTag(const SimpleTag &tag)
|
||||
{
|
||||
d->tags.append(tag);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::Tag::addSimpleTags(const SimpleTagsList& simpleTags)
|
||||
{
|
||||
d->tags.append(simpleTags);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::Tag::insertSimpleTag(unsigned int index, const SimpleTag &tag)
|
||||
{
|
||||
if(index < d->tags.size()) {
|
||||
auto it = d->tags.begin();
|
||||
std::advance(it, index);
|
||||
d->tags.insert(it, tag);
|
||||
}
|
||||
else {
|
||||
d->tags.append(tag);
|
||||
}
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
void Matroska::Tag::removeSimpleTag(unsigned int index)
|
||||
{
|
||||
if(index < d->tags.size()) {
|
||||
auto it = d->tags.begin();
|
||||
std::advance(it, index);
|
||||
d->tags.erase(it);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Matroska::Tag::removeSimpleTag(const String &name,
|
||||
SimpleTag::TargetTypeValue targetTypeValue,
|
||||
unsigned long long trackUid)
|
||||
{
|
||||
removeSimpleTag(name, targetTypeValue, trackUid, 0, 0, 0);
|
||||
}
|
||||
|
||||
void Matroska::Tag::removeSimpleTag(const String& name,
|
||||
SimpleTag::TargetTypeValue targetTypeValue,
|
||||
unsigned long long trackUid, unsigned long long editionUid,
|
||||
unsigned long long chapterUid, unsigned long long attachmentUid)
|
||||
{
|
||||
const auto it = std::find_if(d->tags.begin(), d->tags.end(),
|
||||
[&name, targetTypeValue, trackUid, editionUid, chapterUid, attachmentUid](
|
||||
const SimpleTag &t) {
|
||||
return t.name() == name && t.targetTypeValue() == targetTypeValue &&
|
||||
t.trackUid() == trackUid && t.editionUid() == editionUid &&
|
||||
t.chapterUid() == chapterUid && t.attachmentUid() == attachmentUid;
|
||||
}
|
||||
);
|
||||
if(it != d->tags.end()) {
|
||||
d->tags.erase(it);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Matroska::Tag::clearSimpleTags()
|
||||
{
|
||||
d->tags.clear();
|
||||
setNeedsRender(true);
|
||||
}
|
||||
|
||||
const Matroska::SimpleTagsList &Matroska::Tag::simpleTagsList() const
|
||||
{
|
||||
return d->tags;
|
||||
}
|
||||
|
||||
void Matroska::Tag::setSegmentTitle(const String &title)
|
||||
{
|
||||
d->segmentTitle = title;
|
||||
}
|
||||
|
||||
bool Matroska::Tag::setTag(const String &key, const String &value)
|
||||
{
|
||||
const bool found = d->setTag(key, value);
|
||||
if(found) {
|
||||
setNeedsRender(true);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void Matroska::Tag::setTitle(const String &s)
|
||||
{
|
||||
setTag("TITLE", s);
|
||||
}
|
||||
|
||||
void Matroska::Tag::setArtist(const String &s)
|
||||
{
|
||||
setTag("ARTIST", s);
|
||||
}
|
||||
|
||||
void Matroska::Tag::setAlbum(const String &s)
|
||||
{
|
||||
setTag("ALBUM", s);
|
||||
}
|
||||
|
||||
void Matroska::Tag::setComment(const String &s)
|
||||
{
|
||||
setTag("COMMENT", s);
|
||||
}
|
||||
|
||||
void Matroska::Tag::setGenre(const String &s)
|
||||
{
|
||||
setTag("GENRE", s);
|
||||
}
|
||||
|
||||
void Matroska::Tag::setYear(unsigned int i)
|
||||
{
|
||||
setTag("DATE", i != 0 ? String::number(i) : String());
|
||||
}
|
||||
|
||||
void Matroska::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
setTag("TRACKNUMBER", i != 0 ? String::number(i) : String());
|
||||
}
|
||||
|
||||
String Matroska::Tag::title() const
|
||||
{
|
||||
String s = d->getTag("TITLE");
|
||||
if(s.isEmpty()) {
|
||||
return d->segmentTitle;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
String Matroska::Tag::artist() const
|
||||
{
|
||||
return d->getTag("ARTIST");
|
||||
}
|
||||
|
||||
String Matroska::Tag::album() const
|
||||
{
|
||||
return d->getTag("ALBUM");
|
||||
}
|
||||
|
||||
String Matroska::Tag::comment() const
|
||||
{
|
||||
return d->getTag("COMMENT");
|
||||
}
|
||||
|
||||
String Matroska::Tag::genre() const
|
||||
{
|
||||
return d->getTag("GENRE");
|
||||
}
|
||||
|
||||
unsigned int Matroska::Tag::year() const
|
||||
{
|
||||
const auto value = d->getTag("DATE");
|
||||
if(value.isEmpty())
|
||||
return 0;
|
||||
auto list = value.split("-");
|
||||
return static_cast<unsigned int>(list.front().toInt());
|
||||
}
|
||||
|
||||
unsigned int Matroska::Tag::track() const
|
||||
{
|
||||
const auto value = d->getTag("TRACKNUMBER");
|
||||
if(value.isEmpty())
|
||||
return 0;
|
||||
auto list = value.split("-");
|
||||
return static_cast<unsigned int>(list.front().toInt());
|
||||
}
|
||||
|
||||
bool Matroska::Tag::isEmpty() const
|
||||
{
|
||||
return d->tags.isEmpty();
|
||||
}
|
||||
|
||||
ByteVector Matroska::Tag::renderInternal()
|
||||
{
|
||||
if(d->tags.isEmpty()) {
|
||||
// Avoid writing a Tags element without Tag element.
|
||||
return {};
|
||||
}
|
||||
|
||||
EBML::MkTags tags;
|
||||
List<SimpleTagsList> targetList;
|
||||
|
||||
// Build target-based list
|
||||
for(const auto &tag : std::as_const(d->tags)) {
|
||||
auto targetTypeValue = tag.targetTypeValue();
|
||||
auto trackUid = tag.trackUid();
|
||||
auto editionUid = tag.editionUid();
|
||||
auto chapterUid = tag.chapterUid();
|
||||
auto attachmentUid = tag.attachmentUid();
|
||||
auto it = std::find_if(targetList.begin(),
|
||||
targetList.end(),
|
||||
[&](const auto &list) {
|
||||
const auto &simpleTag = list.front();
|
||||
return simpleTag.targetTypeValue() == targetTypeValue &&
|
||||
simpleTag.trackUid() == trackUid &&
|
||||
simpleTag.editionUid() == editionUid &&
|
||||
simpleTag.chapterUid() == chapterUid &&
|
||||
simpleTag.attachmentUid() == attachmentUid;
|
||||
}
|
||||
);
|
||||
if(it == targetList.end()) {
|
||||
SimpleTagsList list;
|
||||
list.append(tag);
|
||||
targetList.append(list);
|
||||
}
|
||||
else
|
||||
it->append(tag);
|
||||
}
|
||||
for(const auto &list : targetList) {
|
||||
const auto &frontTag = list.front();
|
||||
const auto targetTypeValue = frontTag.targetTypeValue();
|
||||
const auto trackUid = frontTag.trackUid();
|
||||
const auto editionUid = frontTag.editionUid();
|
||||
const auto chapterUid = frontTag.chapterUid();
|
||||
const auto attachmentUid = frontTag.attachmentUid();
|
||||
auto tag = EBML::make_unique_element<EBML::Element::Id::MkTag>();
|
||||
|
||||
// Build <Tag Targets> element
|
||||
auto targets = EBML::make_unique_element<EBML::Element::Id::MkTagTargets>();
|
||||
if(targetTypeValue != SimpleTag::TargetTypeValue::None) {
|
||||
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagTargetTypeValue>();
|
||||
element->setValue(static_cast<unsigned int>(targetTypeValue));
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
if(trackUid != 0) {
|
||||
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagTrackUID>();
|
||||
element->setValue(trackUid);
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
if(editionUid != 0) {
|
||||
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagEditionUID>();
|
||||
element->setValue(editionUid);
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
if(chapterUid != 0) {
|
||||
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagChapterUID>();
|
||||
element->setValue(chapterUid);
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
if(attachmentUid != 0) {
|
||||
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagAttachmentUID>();
|
||||
element->setValue(attachmentUid);
|
||||
targets->appendElement(std::move(element));
|
||||
}
|
||||
tag->appendElement(std::move(targets));
|
||||
|
||||
// Build <Simple Tag> element
|
||||
for(const auto &simpleTag : list) {
|
||||
auto t = EBML::make_unique_element<EBML::Element::Id::MkSimpleTag>();
|
||||
auto tagName = EBML::make_unique_element<EBML::Element::Id::MkTagName>();
|
||||
tagName->setValue(simpleTag.name());
|
||||
t->appendElement(std::move(tagName));
|
||||
|
||||
// Tag Value
|
||||
if(simpleTag.type() == SimpleTag::StringType) {
|
||||
auto tagValue = EBML::make_unique_element<EBML::Element::Id::MkTagString>();
|
||||
tagValue->setValue(simpleTag.toString());
|
||||
t->appendElement(std::move(tagValue));
|
||||
}
|
||||
else if(simpleTag.type() == SimpleTag::BinaryType) {
|
||||
auto tagValue = EBML::make_unique_element<EBML::Element::Id::MkTagBinary>();
|
||||
tagValue->setValue(simpleTag.toByteVector());
|
||||
t->appendElement(std::move(tagValue));
|
||||
}
|
||||
|
||||
// Language
|
||||
auto language = EBML::make_unique_element<EBML::Element::Id::MkTagsTagLanguage>();
|
||||
const String &lang = simpleTag.language();
|
||||
language->setValue(!lang.isEmpty() ? lang : "und");
|
||||
t->appendElement(std::move(language));
|
||||
|
||||
// Default language flag
|
||||
auto dlf = EBML::make_unique_element<EBML::Element::Id::MkTagsLanguageDefault>();
|
||||
dlf->setValue(simpleTag.defaultLanguageFlag() ? 1 : 0);
|
||||
t->appendElement(std::move(dlf));
|
||||
|
||||
tag->appendElement(std::move(t));
|
||||
}
|
||||
tags.appendElement(std::move(tag));
|
||||
}
|
||||
return tags.render();
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// PropertyMap key, Tag name, Target type value, strict
|
||||
// If the key is the same as the name and the target type value is Track,
|
||||
// no translation is needed because this is the default mapping.
|
||||
// Therefore, keys like TITLE, ARTIST, GENRE, COMMENT, etc. are omitted
|
||||
// unless they shall have priority over higher level tags with the same name
|
||||
// when no target type value is given. The strict boolean marks
|
||||
// entries which shall not be mapped without correct target type value.
|
||||
// For offical tags, see https://www.matroska.org/technical/tagging.html
|
||||
constexpr std::array simpleTagsTranslation {
|
||||
std::tuple("TITLE", "TITLE", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("ALBUM", "TITLE", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("ARTIST", "ARTIST", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("ALBUMARTIST", "ARTIST", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("TRACKNUMBER", "PART_NUMBER", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("DISCNUMBER", "PART_NUMBER", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("TRACKTOTAL", "TOTAL_PARTS", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("DISCTOTAL", "TOTAL_PARTS", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("DATE", "DATE_RECORDED", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("TITLESORT", "TITLESORT", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("ALBUMSORT", "TITLESORT", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("ARTISTSORT", "ARTISTSORT", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("ALBUMARTISTSORT", "ARTISTSORT", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("MEDIA", "ORIGINAL_MEDIA_TYPE", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("LABEL", "LABEL_CODE", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("CATALOGNUMBER", "CATALOG_NUMBER", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("DJMIXER", "MIXED_BY", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("REMIXER", "REMIXED_BY", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("INITIALKEY", "INITIAL_KEY", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("RELEASEDATE", "DATE_RELEASED", Matroska::SimpleTag::TargetTypeValue::Album, false),
|
||||
std::tuple("ENCODINGTIME", "DATE_ENCODED", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("TAGGINGDATE", "DATE_TAGGED", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("ENCODEDBY", "ENCODER", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("ENCODING", "ENCODER_SETTINGS", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("OWNER", "PURCHASE_OWNER", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("REPLAYGAIN_TRACK_GAIN", "REPLAYGAIN_GAIN", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("REPLAYGAIN_ALBUM_GAIN", "REPLAYGAIN_GAIN", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("REPLAYGAIN_TRACK_PEAK", "REPLAYGAIN_PEAK", Matroska::SimpleTag::TargetTypeValue::Track, false),
|
||||
std::tuple("REPLAYGAIN_ALBUM_PEAK", "REPLAYGAIN_PEAK", Matroska::SimpleTag::TargetTypeValue::Album, true),
|
||||
std::tuple("MUSICBRAINZ_ALBUMARTISTID", "MUSICBRAINZ_ALBUMARTISTID", Matroska::SimpleTag::TargetTypeValue::Album, false),
|
||||
std::tuple("MUSICBRAINZ_ALBUMID", "MUSICBRAINZ_ALBUMID", Matroska::SimpleTag::TargetTypeValue::Album, false),
|
||||
std::tuple("MUSICBRAINZ_RELEASEGROUPID", "MUSICBRAINZ_RELEASEGROUPID", Matroska::SimpleTag::TargetTypeValue::Album, false),
|
||||
};
|
||||
|
||||
std::tuple<String, Matroska::SimpleTag::TargetTypeValue, bool> translateKey(const String &key)
|
||||
{
|
||||
auto it = std::find_if(simpleTagsTranslation.cbegin(),
|
||||
simpleTagsTranslation.cend(),
|
||||
[&key](const auto &t) { return key == std::get<0>(t); }
|
||||
);
|
||||
if(it != simpleTagsTranslation.end())
|
||||
return { std::get<1>(*it), std::get<2>(*it), std::get<3>(*it) };
|
||||
if(!key.isEmpty())
|
||||
return { key, Matroska::SimpleTag::TargetTypeValue::Track, false };
|
||||
return { String(), Matroska::SimpleTag::TargetTypeValue::None, false };
|
||||
}
|
||||
|
||||
String translateTag(const String &name, Matroska::SimpleTag::TargetTypeValue targetTypeValue)
|
||||
{
|
||||
const auto it = std::find_if(simpleTagsTranslation.cbegin(),
|
||||
simpleTagsTranslation.cend(),
|
||||
[&name, targetTypeValue](const auto &t) {
|
||||
return name == std::get<1>(t)
|
||||
&& (targetTypeValue == std::get<2>(t) ||
|
||||
(targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None
|
||||
&& !std::get<3>(t)));
|
||||
}
|
||||
);
|
||||
return it != simpleTagsTranslation.end()
|
||||
? String(std::get<0>(*it), String::UTF8)
|
||||
: targetTypeValue == Matroska::SimpleTag::TargetTypeValue::Track ||
|
||||
targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None
|
||||
? name
|
||||
: String();
|
||||
}
|
||||
}
|
||||
|
||||
bool Matroska::Tag::TagPrivate::setTag(const String &key, const String &value)
|
||||
{
|
||||
const auto tpl = translateKey(key);
|
||||
// Workaround Clang issue - no lambda capture of structured bindings
|
||||
const String &name = std::get<0>(tpl);
|
||||
auto targetTypeValue = std::get<1>(tpl);
|
||||
if(name.isEmpty())
|
||||
return false;
|
||||
removeSimpleTags(
|
||||
[&name, targetTypeValue] (const auto &t) {
|
||||
return t.name() == name
|
||||
&& t.targetTypeValue() == targetTypeValue;
|
||||
}
|
||||
);
|
||||
if(!value.isEmpty()) {
|
||||
tags.append(SimpleTag(name, value, targetTypeValue));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
String Matroska::Tag::TagPrivate::getTag(const String &key) const
|
||||
{
|
||||
const auto tpl = translateKey(key);
|
||||
// Workaround Clang issue - no lambda capture of structured bindings
|
||||
const String &name = std::get<0>(tpl);
|
||||
auto targetTypeValue = std::get<1>(tpl);
|
||||
bool strict = std::get<2>(tpl);
|
||||
if(name.isEmpty())
|
||||
return {};
|
||||
const auto it = std::find_if(tags.begin(), tags.end(),
|
||||
[&name, targetTypeValue, strict] (const SimpleTag &t) {
|
||||
return t.name() == name
|
||||
&& t.type() == SimpleTag::StringType
|
||||
&& (t.targetTypeValue() == targetTypeValue ||
|
||||
(t.targetTypeValue() == SimpleTag::TargetTypeValue::None && !strict))
|
||||
&& t.trackUid() == 0 && t.editionUid() == 0 && t.chapterUid() == 0
|
||||
&& t.attachmentUid() == 0;
|
||||
}
|
||||
);
|
||||
return it != tags.end() ? it->toString() : String();
|
||||
}
|
||||
|
||||
PropertyMap Matroska::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
for(const auto &simpleTag : std::as_const(d->tags)) {
|
||||
if(simpleTag.type() == SimpleTag::StringType && simpleTag.trackUid() == 0 &&
|
||||
simpleTag.editionUid() == 0 && simpleTag.chapterUid() == 0 &&
|
||||
simpleTag.attachmentUid() == 0) {
|
||||
if(String key = translateTag(simpleTag.name(), simpleTag.targetTypeValue());
|
||||
!key.isEmpty())
|
||||
properties[key].append(simpleTag.toString());
|
||||
else
|
||||
properties.addUnsupportedData(simpleTag.name());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap)
|
||||
{
|
||||
// Remove all simple tags which would be returned in properties()
|
||||
for(auto it = d->tags.begin(); it != d->tags.end();) {
|
||||
if(it->type() == SimpleTag::StringType &&
|
||||
it->trackUid() == 0 && it->editionUid() == 0 &&
|
||||
it->chapterUid() == 0 && it->attachmentUid() == 0 &&
|
||||
!translateTag(it->name(), it->targetTypeValue()).isEmpty()) {
|
||||
it = d->tags.erase(it);
|
||||
setNeedsRender(true);
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new properties
|
||||
PropertyMap unsupportedProperties;
|
||||
for(const auto &[key, values] : propertyMap) {
|
||||
for(const auto &value : values) {
|
||||
if(auto [name, targetTypeValue, _] = translateKey(key);
|
||||
!name.isEmpty()) {
|
||||
d->tags.append(SimpleTag(name, value, targetTypeValue));
|
||||
setNeedsRender(true);
|
||||
}
|
||||
else {
|
||||
unsupportedProperties[key] = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
return unsupportedProperties;
|
||||
}
|
||||
|
||||
void Matroska::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
if(d->removeSimpleTags(
|
||||
[&properties](const SimpleTag &t) {
|
||||
return properties.contains(t.name());
|
||||
}) > 0) {
|
||||
setNeedsRender(true);
|
||||
}
|
||||
}
|
||||
|
||||
StringList Matroska::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
for(const SimpleTag &t : std::as_const(d->tags)) {
|
||||
if((t.type() != SimpleTag::StringType ||
|
||||
t.trackUid() != 0 || t.editionUid() != 0 ||
|
||||
t.chapterUid() != 0 || t.attachmentUid() != 0 ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty()) &&
|
||||
!keys.contains(t.name())) {
|
||||
keys.append(t.name());
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> Matroska::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
if(key.upper() != "PICTURE") { // Pictures are handled at the file level
|
||||
for(const SimpleTag &t : std::as_const(d->tags)) {
|
||||
if(t.name() == key &&
|
||||
(t.type() != SimpleTag::StringType ||
|
||||
t.trackUid() != 0 || t.editionUid() != 0 ||
|
||||
t.chapterUid() != 0 || t.attachmentUid() != 0 ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty())) {
|
||||
VariantMap property;
|
||||
if(t.type() != SimpleTag::StringType) {
|
||||
property.insert("data", t.toByteVector());
|
||||
}
|
||||
else {
|
||||
property.insert("value", t.toString());
|
||||
}
|
||||
property.insert("name", t.name());
|
||||
if(t.targetTypeValue() != SimpleTag::TargetTypeValue::None) {
|
||||
property.insert("targetTypeValue", t.targetTypeValue());
|
||||
}
|
||||
if(t.trackUid()) {
|
||||
property.insert("trackUid", t.trackUid());
|
||||
}
|
||||
if(t.editionUid()) {
|
||||
property.insert("editionUid", t.editionUid());
|
||||
}
|
||||
if(t.chapterUid()) {
|
||||
property.insert("chapterUid", t.chapterUid());
|
||||
}
|
||||
if(t.attachmentUid()) {
|
||||
property.insert("attachmentUid", t.attachmentUid());
|
||||
}
|
||||
property.insert("language", t.language());
|
||||
property.insert("defaultLanguage", t.defaultLanguageFlag());
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool Matroska::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
if(key.upper() == "PICTURE") {
|
||||
// Pictures are handled at the file level
|
||||
return false;
|
||||
}
|
||||
if(d->removeSimpleTags(
|
||||
[&key](const SimpleTag &t) {
|
||||
return t.name() == key &&
|
||||
(t.type() != SimpleTag::StringType ||
|
||||
t.trackUid() != 0 || t.editionUid() != 0 ||
|
||||
t.chapterUid() != 0 || t.attachmentUid() != 0 ||
|
||||
translateTag(t.name(), t.targetTypeValue()).isEmpty());
|
||||
}) > 0) {
|
||||
setNeedsRender(true);
|
||||
}
|
||||
bool result = false;
|
||||
for(const auto &property : value) {
|
||||
if(property.value("name").value<String>() == key &&
|
||||
(property.contains("data") || property.contains("value") )) {
|
||||
SimpleTag::TargetTypeValue targetTypeValue;
|
||||
switch(Variant targetTypeValueVar = property.value("targetTypeValue", 0);
|
||||
targetTypeValueVar.type()) {
|
||||
case Variant::UInt:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<unsigned int>());
|
||||
break;
|
||||
case Variant::LongLong:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<long long>());
|
||||
break;
|
||||
case Variant::ULongLong:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<unsigned long long>());
|
||||
break;
|
||||
default:
|
||||
targetTypeValue = static_cast<SimpleTag::TargetTypeValue>(targetTypeValueVar.value<int>());
|
||||
}
|
||||
auto language = property.value("language").value<String>();
|
||||
const bool defaultLanguage = property.value("defaultLanguage", true).value<bool>();
|
||||
const auto trackUid = property.value("trackUid", 0ULL).value<unsigned long long>();
|
||||
const auto editionUid = property.value("editionUid", 0ULL).value<unsigned long long>();
|
||||
const auto chapterUid = property.value("chapterUid", 0ULL).value<unsigned long long>();
|
||||
const auto attachmentUid = property.value("attachmentUid", 0ULL).value<unsigned long long>();
|
||||
d->tags.append(property.contains("data")
|
||||
? SimpleTag(key, property.value("data").value<ByteVector>(),
|
||||
targetTypeValue, language, defaultLanguage, trackUid,
|
||||
editionUid, chapterUid, attachmentUid)
|
||||
: SimpleTag(key, property.value("value").value<String>(),
|
||||
targetTypeValue, language, defaultLanguage, trackUid,
|
||||
editionUid, chapterUid, attachmentUid));
|
||||
setNeedsRender(true);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
158
taglib/matroska/matroskatag.h
Normal file
158
taglib/matroska/matroskatag.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/***************************************************************************
|
||||
* 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_MATROSKATAG_H
|
||||
#define TAGLIB_MATROSKATAG_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "tag.h"
|
||||
#include "tlist.h"
|
||||
#include "matroskaelement.h"
|
||||
#include "matroskasimpletag.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class MkTags;
|
||||
}
|
||||
|
||||
namespace Matroska {
|
||||
//! List of tag attributes.
|
||||
using SimpleTagsList = List<SimpleTag>;
|
||||
|
||||
//! Matroska tag implementation.
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
, private Element
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs a Matroska tag.
|
||||
*/
|
||||
Tag();
|
||||
|
||||
~Tag() override;
|
||||
String title() const override;
|
||||
String artist() const override;
|
||||
String album() const override;
|
||||
String comment() const override;
|
||||
String genre() const override;
|
||||
unsigned int year() const override;
|
||||
unsigned int track() const override;
|
||||
void setTitle(const String &s) override;
|
||||
void setArtist(const String &s) override;
|
||||
void setAlbum(const String &s) override;
|
||||
void setComment(const String &s) override;
|
||||
void setGenre(const String &s) override;
|
||||
void setYear(unsigned int i) override;
|
||||
void setTrack(unsigned int i) override;
|
||||
bool isEmpty() const override;
|
||||
PropertyMap properties() const override;
|
||||
PropertyMap setProperties(const PropertyMap &propertyMap) override;
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Returns the names of the binary simple tags.
|
||||
*/
|
||||
StringList complexPropertyKeys() const override;
|
||||
|
||||
/*!
|
||||
* Get the binary simple tags as maps with keys "data", "name",
|
||||
* "targetTypeValue", "language", "defaultLanguage".
|
||||
* The attached files such as pictures with key "PICTURE" are available
|
||||
* with Matroska::File::complexProperties().
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
|
||||
/*!
|
||||
* Set the binary simple tags as maps with keys "data", "name",
|
||||
* "targetTypeValue", "language", "defaultLanguage".
|
||||
* The attached files such as pictures with key "PICTURE" can be set
|
||||
* with Matroska::File::setComplexProperties().
|
||||
*
|
||||
* Returns \c true if \c key can be stored as binary simple tags.
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
/*!
|
||||
* Add a tag attribute.
|
||||
*/
|
||||
void addSimpleTag(const SimpleTag &tag);
|
||||
|
||||
/*!
|
||||
* Add multiple tag attributes.
|
||||
*/
|
||||
void addSimpleTags(const SimpleTagsList &simpleTags);
|
||||
|
||||
/*!
|
||||
* Insert a tag attribute at position \a index.
|
||||
*/
|
||||
void insertSimpleTag(unsigned int index, const SimpleTag &tag);
|
||||
|
||||
/*!
|
||||
* Remove a tag attribute at position \a index.
|
||||
*/
|
||||
void removeSimpleTag(unsigned int index);
|
||||
|
||||
/*!
|
||||
* Remove a tag attribute.
|
||||
*/
|
||||
void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue,
|
||||
unsigned long long trackUid = 0);
|
||||
|
||||
/*!
|
||||
* Remove a tag attribute.
|
||||
*/
|
||||
// BIC: Merge with the method above
|
||||
void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue,
|
||||
unsigned long long trackUid, unsigned long long editionUid,
|
||||
unsigned long long chapterUid = 0, unsigned long long attachmentUid = 0);
|
||||
|
||||
/*!
|
||||
* Remove all tag attributes.
|
||||
*/
|
||||
void clearSimpleTags();
|
||||
|
||||
/*!
|
||||
* Get list of all tag attributes.
|
||||
*/
|
||||
const SimpleTagsList &simpleTagsList() const;
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
friend class EBML::MkTags;
|
||||
class TagPrivate;
|
||||
|
||||
bool setTag(const String &key, const String &value);
|
||||
void setSegmentTitle(const String &title);
|
||||
|
||||
// private Element implementation
|
||||
ByteVector renderInternal() override;
|
||||
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -37,7 +37,7 @@ namespace {
|
||||
constexpr std::array containers {
|
||||
"moov", "udta", "mdia", "meta", "ilst",
|
||||
"stbl", "minf", "moof", "traf", "trak",
|
||||
"stsd"
|
||||
"stsd", "stem"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -74,17 +74,7 @@ MP4::Atom::Atom(File *file)
|
||||
}
|
||||
else if(d->length == 1) {
|
||||
// The atom has a 64-bit length.
|
||||
if(const long long longLength = file->readBlock(8).toLongLong();
|
||||
longLength <= LONG_MAX) {
|
||||
// The actual length fits in long. That's always the case if long is 64-bit.
|
||||
d->length = static_cast<long>(longLength);
|
||||
}
|
||||
else {
|
||||
debug("MP4: 64-bit atoms are not supported");
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
d->length = file->readBlock(8).toLongLong();
|
||||
}
|
||||
|
||||
if(d->length < 8 || d->length > file->length() - d->offset) {
|
||||
@@ -95,12 +85,10 @@ MP4::Atom::Atom(File *file)
|
||||
}
|
||||
|
||||
d->name = header.mid(4, 4);
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
if(const char ch = d->name.at(i); (ch < ' ' || ch > '~') && ch != '\251') {
|
||||
debug("MP4: Invalid atom type");
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
}
|
||||
|
||||
if(d->name == "stem") {
|
||||
file->seek(d->length - 8, File::Current);
|
||||
return;
|
||||
}
|
||||
|
||||
for(auto c : containers) {
|
||||
|
||||
@@ -60,8 +60,8 @@ namespace TagLib {
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
struct AtomData {
|
||||
AtomData(AtomDataType type, const ByteVector &data) :
|
||||
type(type), data(data) { }
|
||||
AtomData(AtomDataType ptype, const ByteVector &pdata) :
|
||||
type(ptype), data(pdata) { }
|
||||
AtomDataType type;
|
||||
int locale { 0 };
|
||||
ByteVector data;
|
||||
|
||||
@@ -69,3 +69,13 @@ MP4::CoverArt::data() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
bool MP4::CoverArt::operator==(const CoverArt &other) const
|
||||
{
|
||||
return format() == other.format() && data() == other.data();
|
||||
}
|
||||
|
||||
bool MP4::CoverArt::operator!=(const CoverArt &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,17 @@ namespace TagLib {
|
||||
//! The image data
|
||||
ByteVector data() const;
|
||||
|
||||
/*!
|
||||
* Returns \c true if the CoverArt and \a other are of the same format and
|
||||
* contain the same data.
|
||||
*/
|
||||
bool operator==(const CoverArt &other) const;
|
||||
|
||||
/*!
|
||||
* Returns \c true if the CoverArt and \a other differ in format or data.
|
||||
*/
|
||||
bool operator!=(const CoverArt &other) const;
|
||||
|
||||
private:
|
||||
class CoverArtPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
|
||||
@@ -30,6 +30,7 @@ using namespace TagLib;
|
||||
class MP4::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
Type type;
|
||||
bool valid { true };
|
||||
AtomDataType atomDataType { TypeUndefined };
|
||||
union {
|
||||
@@ -43,11 +44,13 @@ public:
|
||||
StringList m_stringList;
|
||||
ByteVectorList m_byteVectorList;
|
||||
MP4::CoverArtList m_coverArtList;
|
||||
MP4::Stem m_stem;
|
||||
};
|
||||
|
||||
MP4::Item::Item() :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::Void;
|
||||
d->valid = false;
|
||||
}
|
||||
|
||||
@@ -67,36 +70,42 @@ MP4::Item::~Item() = default;
|
||||
MP4::Item::Item(bool value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::Bool;
|
||||
d->m_bool = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::Int;
|
||||
d->m_int = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(unsigned char value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::Byte;
|
||||
d->m_byte = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(unsigned int value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::UInt;
|
||||
d->m_uint = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(long long value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::LongLong;
|
||||
d->m_longlong = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value1, int value2) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::IntPair;
|
||||
d->m_intPair.first = value1;
|
||||
d->m_intPair.second = value2;
|
||||
}
|
||||
@@ -104,21 +113,31 @@ MP4::Item::Item(int value1, int value2) :
|
||||
MP4::Item::Item(const ByteVectorList &value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::ByteVectorList;
|
||||
d->m_byteVectorList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const StringList &value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::StringList;
|
||||
d->m_stringList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const MP4::CoverArtList &value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::CoverArtList;
|
||||
d->m_coverArtList = value;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const MP4::Stem &value) :
|
||||
d(std::make_shared<ItemPrivate>())
|
||||
{
|
||||
d->type = Type::Stem;
|
||||
d->m_stem = value;
|
||||
}
|
||||
|
||||
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
|
||||
{
|
||||
d->atomDataType = type;
|
||||
@@ -183,8 +202,60 @@ MP4::Item::toCoverArtList() const
|
||||
return d->m_coverArtList;
|
||||
}
|
||||
|
||||
MP4::Stem
|
||||
MP4::Item::toStem() const
|
||||
{
|
||||
return d->m_stem;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::Item::isValid() const
|
||||
{
|
||||
return d->valid;
|
||||
}
|
||||
|
||||
MP4::Item::Type MP4::Item::type() const
|
||||
{
|
||||
return d->type;
|
||||
}
|
||||
|
||||
bool MP4::Item::operator==(const Item &other) const
|
||||
{
|
||||
if(isValid() && other.isValid() &&
|
||||
type() == other.type() &&
|
||||
atomDataType() == other.atomDataType()) {
|
||||
switch(type()) {
|
||||
case Type::Void:
|
||||
return true;
|
||||
case Type::Bool:
|
||||
return toBool() == other.toBool();
|
||||
case Type::Int:
|
||||
return toInt() == other.toInt();
|
||||
case Type::IntPair: {
|
||||
const auto lhs = toIntPair();
|
||||
const auto rhs = other.toIntPair();
|
||||
return lhs.first == rhs.first && lhs.second == rhs.second;
|
||||
}
|
||||
case Type::Byte:
|
||||
return toByte() == other.toByte();
|
||||
case Type::UInt:
|
||||
return toUInt() == other.toUInt();
|
||||
case Type::LongLong:
|
||||
return toLongLong() == other.toLongLong();
|
||||
case Type::StringList:
|
||||
return toStringList() == other.toStringList();
|
||||
case Type::ByteVectorList:
|
||||
return toByteVectorList() == other.toByteVectorList();
|
||||
case Type::CoverArtList:
|
||||
return toCoverArtList() == other.toCoverArtList();
|
||||
case Type::Stem:
|
||||
return toStem() == other.toStem();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MP4::Item::operator!=(const Item &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "tstringlist.h"
|
||||
#include "taglib_export.h"
|
||||
#include "mp4coverart.h"
|
||||
#include "mp4stem.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace MP4 {
|
||||
@@ -36,6 +37,23 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT Item
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* The data type stored in the item.
|
||||
*/
|
||||
enum class Type : unsigned char {
|
||||
Void,
|
||||
Bool,
|
||||
Int,
|
||||
IntPair,
|
||||
Byte,
|
||||
UInt,
|
||||
LongLong,
|
||||
StringList,
|
||||
ByteVectorList,
|
||||
CoverArtList,
|
||||
Stem,
|
||||
};
|
||||
|
||||
struct IntPair {
|
||||
int first, second;
|
||||
};
|
||||
@@ -64,6 +82,7 @@ namespace TagLib {
|
||||
Item(const StringList &value);
|
||||
Item(const ByteVectorList &value);
|
||||
Item(const CoverArtList &value);
|
||||
Item(const Stem &value);
|
||||
|
||||
void setAtomDataType(AtomDataType type);
|
||||
AtomDataType atomDataType() const;
|
||||
@@ -77,9 +96,23 @@ namespace TagLib {
|
||||
StringList toStringList() const;
|
||||
ByteVectorList toByteVectorList() const;
|
||||
CoverArtList toCoverArtList() const;
|
||||
Stem toStem() const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
Type type() const;
|
||||
|
||||
/*!
|
||||
* Returns \c true if the Item and \a other are of the same type and
|
||||
* contain the same value.
|
||||
*/
|
||||
bool operator==(const Item &other) const;
|
||||
|
||||
/*!
|
||||
* Returns \c true if the Item and \a other differ in type or value.
|
||||
*/
|
||||
bool operator!=(const Item &other) const;
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
|
||||
@@ -35,6 +35,12 @@
|
||||
using namespace TagLib;
|
||||
using namespace MP4;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char freeFormPrefix[] = "----:com.apple.iTunes:";
|
||||
|
||||
} // namespace
|
||||
|
||||
class ItemFactory::ItemFactoryPrivate
|
||||
{
|
||||
public:
|
||||
@@ -81,6 +87,8 @@ std::pair<String, Item> ItemFactory::parseItem(
|
||||
return parseGnre(atom, data);
|
||||
case ItemHandlerType::Covr:
|
||||
return parseCovr(atom, data);
|
||||
case ItemHandlerType::Stem:
|
||||
return parseStem(atom, data);
|
||||
case ItemHandlerType::TextImplicit:
|
||||
return parseText(atom, data, -1);
|
||||
case ItemHandlerType::Text:
|
||||
@@ -122,6 +130,8 @@ ByteVector ItemFactory::renderItem(
|
||||
return renderInt(name, item);
|
||||
case ItemHandlerType::Covr:
|
||||
return renderCovr(name, item);
|
||||
case ItemHandlerType::Stem:
|
||||
return renderStem(name, item);
|
||||
case ItemHandlerType::TextImplicit:
|
||||
return renderText(name, item, TypeImplicit);
|
||||
case ItemHandlerType::Text:
|
||||
@@ -169,8 +179,8 @@ std::pair<ByteVector, Item> ItemFactory::itemFromProperty(
|
||||
case ItemHandlerType::TextImplicit:
|
||||
case ItemHandlerType::Text:
|
||||
return {name, values};
|
||||
|
||||
case ItemHandlerType::Covr:
|
||||
case ItemHandlerType::Stem:
|
||||
debug("MP4: Invalid item \"" + name + "\" for property");
|
||||
break;
|
||||
case ItemHandlerType::Unknown:
|
||||
@@ -216,6 +226,7 @@ std::pair<String, StringList> ItemFactory::itemToProperty(
|
||||
return {key, item.toStringList()};
|
||||
|
||||
case ItemHandlerType::Covr:
|
||||
case ItemHandlerType::Stem:
|
||||
debug("MP4: Invalid item \"" + itemName + "\" for property");
|
||||
break;
|
||||
case ItemHandlerType::Unknown:
|
||||
@@ -231,7 +242,11 @@ String ItemFactory::propertyKeyForName(const ByteVector &name) const
|
||||
if(d->propertyKeyForName.isEmpty()) {
|
||||
d->propertyKeyForName = namePropertyMap();
|
||||
}
|
||||
return d->propertyKeyForName.value(name);
|
||||
String key = d->propertyKeyForName.value(name);
|
||||
if(key.isEmpty() && name.startsWith(freeFormPrefix)) {
|
||||
key = name.mid(std::size(freeFormPrefix) - 1);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::nameForPropertyKey(const String &key) const
|
||||
@@ -244,7 +259,14 @@ ByteVector ItemFactory::nameForPropertyKey(const String &key) const
|
||||
d->nameForPropertyKey[t] = k;
|
||||
}
|
||||
}
|
||||
return d->nameForPropertyKey.value(key);
|
||||
ByteVector name = d->nameForPropertyKey.value(key);
|
||||
if(name.isEmpty() && !key.isEmpty()) {
|
||||
const auto &firstChar = key[0];
|
||||
if(firstChar >= 'A' && firstChar <= 'Z') {
|
||||
name = (freeFormPrefix + key).data(String::UTF8);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -286,6 +308,7 @@ ItemFactory::NameHandlerMap ItemFactory::nameHandlerMap() const
|
||||
{"akID", ItemHandlerType::Byte},
|
||||
{"gnre", ItemHandlerType::Gnre},
|
||||
{"covr", ItemHandlerType::Covr},
|
||||
{"stem", ItemHandlerType::Stem},
|
||||
{"purl", ItemHandlerType::TextImplicit},
|
||||
{"egid", ItemHandlerType::TextImplicit},
|
||||
};
|
||||
@@ -383,7 +406,7 @@ Map<ByteVector, String> ItemFactory::namePropertyMap() const
|
||||
}
|
||||
|
||||
MP4::AtomDataList ItemFactory::parseData2(
|
||||
const MP4::Atom *atom, const ByteVector &data, int expectedFlags,
|
||||
const MP4::Atom *, const ByteVector &data, int expectedFlags,
|
||||
bool freeForm)
|
||||
{
|
||||
AtomDataList result;
|
||||
@@ -616,6 +639,12 @@ std::pair<String, Item> ItemFactory::parseCovr(
|
||||
};
|
||||
}
|
||||
|
||||
std::pair<String, Item> ItemFactory::parseStem(
|
||||
const MP4::Atom *atom, const ByteVector &data)
|
||||
{
|
||||
return {atom->name(), Item(Stem(data))};
|
||||
}
|
||||
|
||||
|
||||
ByteVector ItemFactory::renderAtom(
|
||||
const ByteVector &name, const ByteVector &data)
|
||||
@@ -646,7 +675,7 @@ ByteVector ItemFactory::renderInt(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromShort(item.toInt()));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(item.toInt())));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
@@ -686,8 +715,8 @@ ByteVector ItemFactory::renderIntPair(
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(2, '\0') +
|
||||
ByteVector::fromShort(item.toIntPair().first) +
|
||||
ByteVector::fromShort(item.toIntPair().second) +
|
||||
ByteVector::fromShort(static_cast<short>(item.toIntPair().first)) +
|
||||
ByteVector::fromShort(static_cast<short>(item.toIntPair().second)) +
|
||||
ByteVector(2, '\0'));
|
||||
return renderData(name, TypeImplicit, data);
|
||||
}
|
||||
@@ -697,8 +726,8 @@ ByteVector ItemFactory::renderIntPairNoTrailing(
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(2, '\0') +
|
||||
ByteVector::fromShort(item.toIntPair().first) +
|
||||
ByteVector::fromShort(item.toIntPair().second));
|
||||
ByteVector::fromShort(static_cast<short>(item.toIntPair().first)) +
|
||||
ByteVector::fromShort(static_cast<short>(item.toIntPair().second)));
|
||||
return renderData(name, TypeImplicit, data);
|
||||
}
|
||||
|
||||
@@ -725,6 +754,13 @@ ByteVector ItemFactory::renderCovr(
|
||||
return renderAtom(name, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderStem(
|
||||
const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
auto data = item.toStem().data();
|
||||
return renderAtom(name, data);
|
||||
}
|
||||
|
||||
ByteVector ItemFactory::renderFreeForm(
|
||||
const String &name, const MP4::Item &item)
|
||||
{
|
||||
|
||||
@@ -142,6 +142,7 @@ namespace TagLib {
|
||||
Byte,
|
||||
Gnre,
|
||||
Covr,
|
||||
Stem,
|
||||
TextImplicit,
|
||||
Text
|
||||
};
|
||||
@@ -214,6 +215,8 @@ namespace TagLib {
|
||||
const MP4::Atom *atom, const ByteVector &bytes);
|
||||
static std::pair<String, Item> parseCovr(
|
||||
const MP4::Atom *atom, const ByteVector &data);
|
||||
static std::pair<String, Item> parseStem(
|
||||
const MP4::Atom *atom, const ByteVector &data);
|
||||
|
||||
// Functions used by renderItem() to render atom data for items.
|
||||
static ByteVector renderAtom(
|
||||
@@ -242,6 +245,8 @@ namespace TagLib {
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderCovr(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
static ByteVector renderStem(
|
||||
const ByteVector &name, const MP4::Item &item);
|
||||
|
||||
private:
|
||||
static ItemFactory factory;
|
||||
|
||||
@@ -196,7 +196,7 @@ MP4::Properties::read(File *file, const Atoms *atoms)
|
||||
}
|
||||
}
|
||||
if(unit > 0 && length > 0)
|
||||
d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
|
||||
d->length = static_cast<int>(static_cast<double>(length) * 1000.0 / static_cast<double>(unit) + 0.5);
|
||||
|
||||
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
|
||||
if(!atom) {
|
||||
|
||||
64
taglib/mp4/mp4stem.cpp
Normal file
64
taglib/mp4/mp4stem.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/**************************************************************************
|
||||
copyright : (C) 2026 by Antoine Colombier
|
||||
email : antoine@mixxx.org
|
||||
**************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* 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 "mp4stem.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
MP4::Stem::Stem(const ByteVector &data) :
|
||||
d(std::make_shared<StemPrivate>())
|
||||
{
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
MP4::Stem::Stem() = default;
|
||||
MP4::Stem::Stem(const Stem &) = default;
|
||||
MP4::Stem &MP4::Stem::operator=(const Stem &) = default;
|
||||
|
||||
void
|
||||
MP4::Stem::swap(Stem &item) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, item.d);
|
||||
}
|
||||
|
||||
MP4::Stem::~Stem() = default;
|
||||
|
||||
ByteVector
|
||||
MP4::Stem::data() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
bool MP4::Stem::operator==(const Stem &other) const
|
||||
{
|
||||
return data() == other.data();
|
||||
}
|
||||
|
||||
bool MP4::Stem::operator!=(const Stem &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user