Commit Graph

2652 Commits

Author SHA1 Message Date
Urs Fleisch
0df52e3993 Apply stco/co64 bounds fix from PR #1333 to MP4 chapter code
The updateChunkOffsets() function in mp4qtchapterlist.cpp and
mp4chapterlist.cpp is duplicated code from mp4tag.cpp and needs
the patch from mp4tag.cpp too.
2026-04-23 11:03:23 -07:00
Ryan Francesconi
ba2441b378 corrected nanosecond unit change -> milliseconds
taglib/mp4/mp4chapterlist.h
• start​Time doc comment: 100​-nanosecond units → milliseconds

taglib/mp4/mp4chapterlist.cpp
• render​Chpl​Data: from​Long​Long(ch​.start​Time) → from​Long​Long(ch​.start​Time * 10000​LL)
• parse​Chpl​Data: ch​.start​Time = start​Time → ch​.start​Time = start​Time100ns / 10000​LL

taglib/mp4/mp4qtchapterlist.cpp
• read: current​Time * 10000000​.0 / timescale → current​Time * 1000​.0 / timescale
• build​Stts lambda: time100ns * timescale / 10000000​.0 → time​Ms * timescale / 1000​.0

tests/test_mp4.cpp
• All start​Time assignments and assertions divided by 10,000 (e.g. 300000000​LL → 30000​LL)
2026-04-23 11:03:23 -07:00
Ryan Francesconi
c5ea13bb34 overloads for read, write, remove
Changes made

mp4chapterlist.h
• Added (​MP4::​File*) overloads for read, write, remove
• Replaced broken class ​File; forward declaration with #include "mp4file​.h" (fixed a subtle C++ name-resolution linker bug where Atoms(​File*) resolved to MP4::​File* instead of Tag​Lib::​File*)

mp4chapterlist.cpp
• Refactored: path-based overloads are now thin wrappers that delegate to file-based overloads
• File-based overloads construct Atoms locally — no Atoms* in the public API
• Removed chpl​Header​Size = 9 constant; replaced the minimum-size guard in parse​Chpl​Data with a correct 5-byte check (the old constant was version-1 specific and would reject valid version-0 atoms)

mp4qtchapterlist.h
• Added (​MP4::​File*) overloads for read, write, remove
• Removed Atoms* parameters entirely from the public API

mp4qtchapterlist.cpp
• Same refactor: path-based overloads delegate; file-based overloads construct Atoms locally
• Added empty-chapter guard: write(​MP4::​File*, {}) delegates to remove(file) instead of writing a 0-sample chapter track

tests/test_mp4.cpp
• Added test​Chapter​List​File​API and test​QTChapter​List​File​API — exercise the full write/read/remove cycle via the file-based API
• Updated test bodies to use the simplified (​MP4::​File*) API (no MP4::​Atoms construction in test code)
2026-04-23 11:03:23 -07:00
Ryan Francesconi
4a73d73b20 MP4: Add QuickTime-style chapter track support
QuickTime-style chapter tracks are the native chapter format for
Apple's ecosystem. They use a disabled text track (hdlr type "text")
referenced by a chap track-reference in the audio track's tref box.
This format is recognized by QuickTime, iTunes/Music, Final Cut Pro,
Logic Pro, DaVinci Resolve, VLC, and most other MP4/M4A players. It
is also the format that AVFoundation reads natively via
AVAssetChapterMetadataGroup.

The implementation produces output that matches ffmpeg's chapter track
structure byte-for-byte: per-sample stts entries (required by
AVFoundation), encd atoms for UTF-8 text encoding, edts/elst edit
lists, gmhd with gmin+text media information, and disabled tkhd flags
(track_in_movie only).

Key behaviors:
- write() inserts tref + chapter trak as a single contiguous block,
  then appends text samples in an mdat atom at EOF
- Handles non-zero first chapter times by prepending a dummy chapter
  at time 0 (stripped on read)
- Overwrite support: removes existing chapter track before writing
- Preserves existing metadata tags and audio data integrity
- Uses timescale=1000 (milliseconds) for chapter track timing

7 new tests covering write/read round-trip, remove, overwrite, tag
preservation, empty file read, timestamp precision, and non-zero
first chapter handling.
2026-04-23 11:03:23 -07:00
Ryan Francesconi
9c56f191e5 MP4: Add Nero-style chapter marker support
Implement read/write/remove of Nero-style chapter markers (chpl atom)
in MP4 files. The chpl atom lives at moov/udta/chpl, storing up to 255
chapter entries with 100-nanosecond timestamps and UTF-8 titles.

Includes CppUnit tests covering round-trip read/write, remove, tag
preservation, and reading from files with no chapters.
2026-04-23 11:03:23 -07:00
Urs Fleisch
77f6b9add5 Drop zero size ID3v2 frames but accept tag (#437) 2026-04-20 15:11:33 +02:00
Urs Fleisch
a64e7543f8 Fix DSD/DSF signed integer issues (#1332) 2026-04-20 15:08:37 +02:00
Felipe
d466b72eea docs: Some improvements to the documentation (#1337)
Make MP4 AtomDataType descriptions visible in the generated documentation.
Convert the ID3v2 text frame listing into a table.
Convert the shorten `fileType()` documentation into a table.
Fix some typos.
Add link to specification in `EventType` for consistency with other headers.
2026-04-13 20:05:53 +02:00
Urs Fleisch
c3a0e1d0a2 Matroska: Use seek head for faster element lookup (#1321)
Limit scan for Matroska seek head to 512 KB in ReadStyle::Fast

---------

Co-authored-by: tolriq <git@leetzone.org>
2026-04-13 19:58:52 +02:00
Ryan Francesconi
13751f5a6b Fix/shorten rice golomb k bounds (#1335)
* Shorten: Reject out-of-range k in getRiceGolombCode

k values outside [0, 31] cause undefined behavior: a left shift by 32
on int32_t (UB in C++) when bitsAvailable reaches 32 after a buffer
refill. Guard against this at the top of getRiceGolombCode and return
false (invalid file) for any k outside the valid range.

* Shorten: Reject out-of-range k in getRiceGolombCode

k values outside [0, 31] cause undefined behavior: a left shift by 32
on int32_t (UB in C++) when bitsAvailable reaches 32 after a buffer
refill. Guard against this at the top of getRiceGolombCode and return
false (invalid file) for any k outside the valid range.
2026-04-09 14:03:36 -06:00
Urs Fleisch
4da5ac2de4 Fix writing too many offsets when updating MP4 stco/co64 atoms (#1332)
This will fix a DoS with a crafted MP4 file causing too many offsets
to be written when updating the stco or co64 tables in MP4 files.

Credits for the discovery of this bug go to Yuen Ying Ng (Ruth)
(Cyber Security Researcher at PwC Hong Kong).
2026-04-08 20:53:59 +02:00
Urs Fleisch
193091fe2e Fix unbounded recursion in EBML/Matroska MasterElement and MP4 atoms (#1326)
Credits for fix and reporting go to https://github.com/ericliu-12.
2026-04-08 20:52:58 +02:00
Ryan Francesconi
5d63187a8b MP4: Fix data race in ItemFactory lazy map initialization (#1331)
Concurrent calls to propertyKeyForName() and handlerTypeForName() (e.g.
via batchMap during import) could race on the isEmpty() guard used for
first-call lazy initialization.

Replace isEmpty() guards with std::call_once / std::once_flag so that
each map is initialized exactly once in a thread-safe manner. Using
call_once (rather than eager construction in the base class constructor)
preserves virtual dispatch, allowing ItemFactory subclasses to override
nameHandlerMap() and namePropertyMap() correctly.

Both property maps are initialized together in a single once_flag since
nameForPropertyKey is derived from namePropertyMap.
2026-04-04 17:52:54 +02:00
Ryan Francesconi
f32b503f56 Fix bitrate calculation unit errors in ADTS and MP4 ESDS parsers (#1330)
mpegheader.cpp: ADTS bitrate divided by 1024 (binary kilo) instead of
1000 (decimal kilo), causing ~2.4% underreporting for all AAC streams.

mp4properties.cpp: ESDS averageBitrate double-rounded via both +500 and
+0.5 before int cast, causing standard bitrates (128000, 192000, etc.)
to read 1 kbps too high.
2026-04-04 16:34:37 +02:00
Ryan Francesconi
d6a2134cf3 Clamp oversized RIFF chunk to available bytes instead of rejecting it (#1329)
Some encoders write a valid data chunk but with a slightly too-large
declared chunkSize, or place the data chunk beyond the declared RIFF
boundary. The previous behaviour called break, abandoning all remaining
chunks and making the file appear empty to taglib.

Lenient parsers (ffmpeg, QuickTime) handle this case by clamping the
chunk size to the bytes that actually remain in the file. Adopt the
same strategy: when chunkSize would exceed the file length, clamp it
and continue parsing rather than stopping early.
2026-04-04 12:47:49 +02:00
Ryan Francesconi
abadbb6768 Add BEXT and iXML chunk support to WAV files (#1323)
Read, write, and remove Broadcast Audio Extension (BEXT, EBU Tech 3285)
and iXML metadata chunks in WAV files. BEXT is widely used in broadcast
and professional audio for originator, description, time reference, and
loudness metadata. iXML is used by field recorders and DAWs for scene,
take, and track metadata.
2026-04-04 12:14:34 +02:00
Daniel
49510e7d5a Move MPEG check to end of content-based detection (#1319)
MPEG::File::isSupported() scans for frame sync bytes that can appear
in other files, causing them to be misidentified as MP3.

This also includes a test with such a file.
2026-04-04 08:01:41 +02:00
Daniel
7f2f2ddcaf Add tests for FileRef content-based detection via ByteVectorStream (#1318)
This covers all 18 formats supported by the content-based detection.
2026-04-04 07:43:56 +02:00
Urs Fleisch
0368c0239a Pin submodule utfcpp to tag v4.0.9 (#1315)
git submodule init
git submodule update --remote
(cd 3rdparty/utfcpp && git checkout v4.0.9)
git add 3rdparty/utfcpp
git commit -m 'Pin submodule utfcpp to tag v4.0.9'
2026-03-31 20:04:11 +02:00
Felipe
9411bb161f Opus: Read output gain (#1320) 2026-03-31 11:14:44 -05:00
Urs Fleisch
78298769de Version 2.2.1 v2.2.1 2026-03-07 06:41:13 +01:00
Urs Fleisch
c43d2b3fc1 Avoid duplicates in StringList Matroska::Tag::complexPropertyKeys()
When using for example

examples/tagwriter -C GENRE \
"name=GENRE,targetTypeValue=50,value=Soft Rock;name=GENRE,targetTypeValue=50,value=Classic Rock" \
path/to/file.mka

the GENRE key was included twice and tagreader displayed the two genre
tags twice.
2026-02-28 07:51:04 +01:00
Urs Fleisch
3db0d48f4b Support edition, chapter and attachment UIDs in Matroska simple tags (#1311) 2026-02-24 06:53:23 +01:00
Urs Fleisch
de6e2b15c8 Version 2.2 v2.2 2026-02-18 05:15:14 +01:00
Urs Fleisch
b7f0225f0d Do not allow unknown frames embedded in CTOC and CHAP (#1306)
This prevents the parsing of frames with specially constructed recursive
frame hierarchies from taking an extremely long time.
2026-02-15 08:38:14 +01:00
tsdgeos
f4117f873c wavpack: Fix infinite loop when reading broken files (#1304)
A crafted file can have blockSamples set to 0 and a blockSize so big
that when adding 8 it overflows and offset is 0 so it goes back to the
same position and loops forever
2026-02-04 17:31:59 +01:00
Damien Plisson
cf86f20b36 Add DSD information to WavPack properties (#1303) 2026-02-04 17:31:03 +01:00
Urs Fleisch
74d93db166 Update changelog for v2.2beta v2.2beta 2026-02-01 12:08:59 +01:00
Stephen Booth
397b6c1de3 Add check for ID3v2 frame data length (#1300)
Also fix some wrong frame sizes in the unit tests.

---------

Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2026-01-31 08:24:54 +01:00
Stephen Booth
51f431c96a Verify the ID3v2 version and revision are not 0xFF (#1301) 2026-01-31 08:21:17 +01:00
Antoine Colombier
11e3eb05bd MP4: Add support for NI STEM (#1299) 2026-01-25 13:33:54 +01:00
Urs Fleisch
2c01b63433 Merge pull request #1149 from complexlogic/matroska
Another Matroska Attempt
2026-01-24 15:27:22 +01:00
Urs Fleisch
d83d751443 Matroska: Fix build with older macOS and 32-bit Android compilers
When building for macOS < 10.14, the API for std::optional and
std::variant is restricted

    error: 'value' is unavailable: introduced in macOS 10.14
    error: 'get<..>' is unavailable: introduced in macOS 10.14

There was also an issue with Android armeabi-v7a where long is not
64 bit and a static assertion failed.
2026-01-24 14:59:10 +01:00
Urs Fleisch
f4e7a742c3 Improve Matroska API
Make AttachedFile immutable. This is consistent with SimpleTag and
Chapter and avoids using attached files which do not have all required
attributes.
Provide methods to insert and remove a single simple tag, so that
they can be modified without setting all of them while still not
exposing internal lists to the API.
Use DATE_RECORDED instead of DATE_RELEASED for year() and the "DATE"
property. This is more consistent with other tag formats, e.g. for ID3v2
"TDRC" is used, which is the recording time.
2026-01-24 14:59:10 +01:00
Urs Fleisch
68a514f4a0 Matroska: Avoid inlined header methods, only install public headers 2026-01-24 14:59:10 +01:00
Urs Fleisch
607cd5bdf4 Matroska: Fix issues reported by static analysis, formatting, docs 2026-01-24 14:59:10 +01:00
Urs Fleisch
b625be5690 Support for Matroska chapters 2026-01-24 14:59:10 +01:00
Urs Fleisch
d2bf7e72d2 Unit tests for Matroska 2026-01-24 14:59:10 +01:00
Urs Fleisch
280d6306d2 Update property mapping documentation for Matroska 2026-01-24 14:59:10 +01:00
Urs Fleisch
aef78068b3 Render when tags are modified 2026-01-24 14:59:10 +01:00
Urs Fleisch
81815e1091 Include doc type and version in properties 2026-01-24 14:59:10 +01:00
Urs Fleisch
b9122afaca Preserve track UID in simple tags
Some encoders write track specific DURATION tags, which should not be
removed.
2026-01-24 14:59:10 +01:00
Urs Fleisch
1d3a375765 Use segment title as fallback for returned tag title
Some applications like handbrake store the title only in the segment
info element.
2026-01-24 14:59:10 +01:00
Urs Fleisch
241b3b2921 Remove offset listeners, they are not used 2026-01-24 14:59:10 +01:00
Urs Fleisch
48104959b2 Integrate cues, fix element rendering and resizing 2026-01-24 14:59:10 +01:00
Urs Fleisch
c817cc0a47 tagwriter: Support setting of complex properties
A complex property can be set with

-C <complex-property-key> <key1=val1,key2=val2,...>

The second parameter can be set to "" to delete complex properties with the
given key. The set complex property values, a simple shorthand syntax can be
used. Multiple maps are separated by ';', values within a map are assigned
with key=value and separated by a ','. Types are automatically detected,
double quotes can be used to force a string. A ByteVector can be constructed
from the contents of a file with the path is given after "file://". There is
no escape, but hex codes are supported, e.g. "\x2C" to include a ',' and \x3B
to include a ';'.

Examples:

Set a GEOB frame in an ID3v2 tag:
examples/tagwriter -C GENERALOBJECT \
  'data=file://file.bin,description=My description,fileName=file.bin,mimeType=application/octet-stream' \
  file.mp3

Set an APIC frame in an ID3v2 tag (same as -p file.jpg 'My description'):
examples/tagwriter -C PICTURE \
  'data=file://file.jpg,description=My description,pictureType=Front Cover,mimeType=image/jpeg' \
  file.mp3

Set an attached file in a Matroska file:
examples/tagwriter -C file.bin \
  'fileName=file.bin,data=file://file.bin,mimeType=application/octet-stream' \
  file.mka

Set simple tag with target type in a Matroska file:
examples/tagwriter -C PART_NUMBER \
  name=PART_NUMBER,targetTypeValue=20,value=2 file.mka

Set simple tag with binary value in a Matroska file:
examples/tagwriter -C BINARY \
  name=BINARY,data=file://file.bin,targetTypeValue=60 file.mka
2026-01-24 14:59:10 +01:00
Urs Fleisch
7a5a10102e Set Matroska tags which are not in the property map using complex properties
Also all attached files can be accessed and modified using complex properties.
2026-01-24 14:59:10 +01:00
Urs Fleisch
d47d28f0f8 Fix and simplify Matroska simple tag
Avoid use of raw pointers, fix property interface.
2026-01-24 14:59:10 +01:00
Urs Fleisch
3566b00596 Clean up attachments
Avoid use of raw pointers.
2026-01-24 14:59:10 +01:00
Urs Fleisch
98bc68d16e Implement complex properties for Matroska 2026-01-24 14:59:10 +01:00