Repository navigation
Releases: aagedal/SwiftMediaMetadata
Release list
SwiftMediaMetadata 3.0.3
Fixed
- MXF parsing now jumps over essence using the Random Index Pack (or the
footer's PreviousPartition chain) instead of visiting every frame's KLVs.
Frame-wrapped XAVC/XDCAM clips previously cost one small read per KLV across
the whole file — seconds on SSD and far longer on camera cards; a 16 GB
XAVC clip now parses in ~20 ms. Essence KLVs are also no longer peeked, and
the NRT/ARRI fallback scan is limited to the header metadata. Files without
a valid partition index keep the previous full walk. - MXF picture dimensions now use the descriptor's display rectangle when it is
smaller than the stored (coding-block padded) one, matching ffprobe. XAVC
1080p was reported as 1920×1088 with a 136:135 pixel aspect ratio.
SwiftMediaMetadata 3.0.2
Fixed
- File-based MXF metadata parsing now uses bounded positional reads instead of
materializing the complete media file. Essence and unneeded KLV payloads are
skipped while metadata, MCA labels and file-relative offsets are preserved. - MXF reads reject invalid bounds, truncated payloads, and file extent changes;
interrupted reads retry. Same-size concurrent writes are not detected. - Added parity, bounded-read, sparse-file, malformed-input, and extent-change
regression coverage. Caller-owned Data continues to use the existing parser.
SwiftMediaMetadata 3.0.1
Fixed
- Sony RTMD track discovery now skips top-level
mdatpayloads instead of
copying the complete audio/video payload into memory. Structural boxes and
file-absolute RTMD sample offsets retain their existing behavior. - The real-world bare-JPEG fixture lookup now supports SwiftPM's current copied
resource-directory layout, restoring a clean test run on newer toolchains. - Release packaging now discovers SwiftPM's active binary output directory
instead of copying a stale executable from a toolchain-specific build path.
SwiftMediaMetadata 3.0.0
Added
- Breaking:
XMPValue.languageAlternativeandXMPLanguageAlternative
retain every
orderedrdf:Altitem, its exactxml:langtag, duplicate languages, and
empty values. The reader, writer, exporter, sidecar display, and embedded XMP
paths round-trip the lossless representation; the existing scalar
langAlternativecase remains the carrier for onex-defaultvalue. Clients
with an exhaustive switch overXMPValuemust handle the new case. IPTCXMPSynchronizationOptionsand synchronization reports provide explicit
merge, replace, clear, title, and date-precision policies for standards-aware
IPTC-IIM ↔ XMP synchronization. The existing legacy synchronization methods
remain available unchanged.PhotoMetadataprovides an XMP-preferred, IIM/Exif-fallback projection that
retains every carrier candidate and reports disagreements. Canonical edits
can be applied withPhotoMetadataMutationwithout discarding the raw XMP
graph.XMPStructurePatchand structured-array patching let callers update selected
XMP members while retaining unknown sibling fields. The typed IPTC Extension
and PLUS setters now use the same lossless merge behavior.ImageMetadata.WriteOptions.FileCreationDatePolicypreserves a destination's
creation date by default and can explicitly update or assign it independently
of the modification-date policy.MetadataWriteResult<Output>provides one source-compatible result contract
for image, video, audio, XMP sidecar, and single-file batch writes. The result
carries either serializedDataor the committed destinationURLtogether
with non-fatal warnings.serialized()andwriteResult(to:options:)separate in-memory metadata
serialization from filesystem commits. ExistingwriteToData()and
write(to:)entry points remain available as compatibility wrappers.XMPSidecarRevision,XMPSidecarSnapshot, andXMPSidecar.updateprovide
content-revisioned sidecar mutations with stale-write detection, bounded
merge retries, serialized-packet validation, and an installed-state result.
Transactional and ordinary sidecar writes share a cross-process lock and the
existing filesystem write-options contract.- Per-format metadata capabilities describe read, direct-write, sidecar-only,
opaque-preservation, stripping, and unsupported behavior independently for
Exif, IPTC-IIM, XMP, Camera Raw, ICC, and C2PA. semanticMetadataandpreservationReport(comparedTo:)compare normalized
metadata meaning rather than XML prefixes/order, TIFF byte order/offsets, or
equivalent date and GPS spellings. Reports separate added, removed, changed,
unrepresentable, and opaque-preserved values.- Typed XMP GPS APIs parse decimal, directional decimal, DMS, and
degrees/decimal-minutes coordinates; retain original lexical values; validate
ranges and hemisphere conflicts; and emit stable XMP coordinate/rational
spellings.PhotoMetadatanow retains typed XMP and Exif GPS candidates via
PhotoMetadataValue.number; exhaustive switches over that enum must handle
the new case.
Changed
- The image API implementation is split into focused reading, writing, editing,
extraction, sidecar, location, and format extensions. The public API remains
source compatible. plus:ImageSupplierstructured arrays serialize as the standard-required
rdf:Seqrather thanrdf:Bag.- Ambiguous TIFF byte signatures remain TIFF regardless of camera Make. A
proprietary TIFF-based RAW is classified from a known RAW filename extension
when reading from a URL, preventing rendered Sony/Olympus/Pentax TIFFs from
being incorrectly blocked by the RAW write guard.
Fixed
- Release preflight notes are selected from the requested version's non-empty
changelog section instead of always reusing the 2.0.0 notes or requiring a
breaking-change entry in every future release. - Atomic image, video, audio, and sidecar writes explicitly restore the
destination's prior filesystem visibility. This prevents iCloud Drive and
similar providers from carrying a dot-prefixed staging file's hidden flag
onto the installed file. - Atomic and direct writes restore the selected file creation date after the
filesystem transaction.
SwiftMediaMetadata 2.0.0
Added
- Deterministic parser property tests now exercise TIFF/Exif, ISOBMFF, XMP,
C2PA/JUMBF, and video bitstreams with truncations, malformed lengths, seeded
byte soup, and write-read-write stability checks. A reusable script supports
longer reproducible local runs. - Release tooling now provides a single macOS preflight, a tag-driven GitHub
release workflow, SHA-256 checksums, archive-content validation, and a
step-by-step 2.0 release/Homebrew checklist. ImageMetadata.WriteOptions.FileModificationDatePolicycontrols filesystem
modification dates independently of timestamps embedded in media metadata.
Use.preserveExistingfor in-place edits or.set(Date)when a new output
should inherit a source file's date. Image, video, audio, XMP sidecar, and
batch writes share the policy.- Mutating CLI commands accept
-P/--preserve-file-modification-date.
Changed
- Breaking: the Swift package, library product, importable module, source
target, and test targets are renamed fromSwiftExifto
SwiftMediaMetadata. Clients must use the renamed GitHub repository, depend
on theSwiftMediaMetadataproduct, and replaceimport SwiftExifwith
import SwiftMediaMetadata. The installedswift-exifCLI command is
unchanged. - Image, video, audio, and XMP sidecar writes now use one internal filesystem
transaction for atomic replacement, backups, temporary-file cleanup, and
file attributes. - The 33,536-city offline geolocation database is now a versioned bundled
resource instead of 54,000 lines of generated Swift literals. Reverse
geocoding behavior is unchanged, while a measured clean library build is 79%
faster and the geolocation debug object shrinks from 12.0 MB to 102 KB.
1.6.0 — Cinema RAW: ARRIRAW, RED, X-OCN, CRM/CRL + per-frame metadata
Added
- ARRIRAW (MXF) — full slate / lens / camera / medium / frameline / LUT JSON harvest. ARRI cameras (ALEXA 35, ALEXA Mini LF, AMIRA, …) carry production-side metadata as a set of JSON documents wrapped in MXF KLV metadata sets (tag
0x807AMIME,0x807BJSON payload,0x807Cschema URL). NewARRIJSONParser.swiftdecodes the six well-known schemas —slate_info,camera_device,lens_device,recording_medium,frameline,custom_lut3d_design— and surfaces them viaCameraMetadata.userMeta*.MXFReaderroutes ALEXA-codec MXF clips through this path;FormatDetectorreportsarrirawso consumers can distinguish ARRI MXFs from generic broadcast MXF. - Sony X-OCN (MXF + NRT AcquisitionRecord) — VENICE / BURANO / F55 / F65 cinema RAW. Format detection routes
.mxfclips with the X-OCN essence descriptor through a deep harvest of the embeddedNonRealTimeMetaXML (camera attributes, ISO, shutter angle, gamma, gamut, monitoring transform, ASC CDL, roll/tilt sensor metadata). Surfaces alongside ARRIRAW underFileFormat = XOCN. Tested against VENICE 8K samples (8664×5784, ISO 3200, ShutterAngle 173°, full ASC CDL). - Canon Cinema RAW Light (.CRM / .CRL) — clip + per-frame CTMD metadata. New
CRMReader.swiftrecognises the Canon-flavored ISOBMFF (brandcrx+ Canon UUID), exposesFileFormat = CRM, and decodes the CTMD timed-metadata sample groups for ISO, shutter time, focal length, aperture, white balance, and per-frame lens metadata across both the CRAW master format and the proxy CRL. - RED RAW (.R3D) — REDCODE clip metadata. New
R3DReader.swiftparses the RED2 atom and decodes OBKM / RDLG metadata (lens model, slate, sensor, codec). Verified against KOMODO Chroma Key and V-RAPTOR LUT samples. - Nikon RAW Video (.R3D, codec
NR3D) — N-RAW detection. Nikon Z8 / Z9 N-RAW recordings happen to use the.R3Dextension but are an ISOBMFF derivative (ftyp niko, codecNR3D), not REDCODE. Format dispatch sniffs the magic bytes to pick the right reader; misnamed files in "RED RAW" folders no longer get misclassified. - Sony RTMD per-frame attributes + IMU export.
RTMDReaderalready surfaced clip-level summaries; the newrtmd-framesCLI subcommand walks everyrtmdsample and emits one of three CSV streams —attributes(frame-level ISO, exposure, F-number, focal length, white balance, GPS, date-time, serial),gyroscope(timestamp + pitch/roll/yaw), oraccelerometer(timestamp + xyz). Verified against Sony A1 / FX3 / FX30 footage at 2 kHz IMU sampling.
Changed
MP4Parser.swiftdecomposed (no behavior change). The 3,277-line monolith is now split along its existingMARK:boundaries into five files held together byextension MP4Parser:MP4Parser.swift(477 lines) — top-level dispatch,mvhd, format detectionMP4UserData.swift(775) —udta/meta/ilst, GPS©xyz, XMP/NRT uuid, BRAW per-framebmdfheaderMP4VisualSampleEntry.swift(557) —stsdvisual entry, codec configs (avcC/hvcC/av1C/vvcC/dvcC/dvvC), HDR side-data (mdcv/clli), ProRes/APV FourCC defaultsMP4AudioSampleEntry.swift(290) —stsdaudio entry,chan,esdsMP4TrackParser.swift(881) —trakwalking,tkhd/mdhd/edts, timecode-track decodingMP4Chapters.swift(364) — Nerochpl, QuickTimetref→chap, sample-table helpers shared withRTMDReader/BRAWFrameReader
6.9× reduction in the main file. Verified by diffing the JSON read sweep over 332 mixed video / image files in the local corpus before and after — output is byte-identical.
Fixed
- Linux cross-compile broken by the ARRIRAW commit.
CFNumberIsFloatTypeis CoreFoundation, Apple-only — swift-corelibs-foundation doesn't ship it, so the static-musl Linux builds failed at the ARRI JSON slate stringifier added in af31434. Replaced with a Swift cast-order check (as? Int64first; otherwise format as%g), preserving observable output on both platforms. - Phase26
--maptest asserted lowercaseXMP-dc:title/:descriptionwhile ExifTool-conventional capitalization (XMP-dc:Title/:Description) had already landed in 3374496. Test now asserts the canonical form.
Tests
1478 tests, 0 failures (26 skipped). Verified end-to-end across the local corpus:
- 47 images (jpg / arw / cr3 / heic / jxl) read sweep — 0 errors
- 64 videos (8 X-OCN, 12 MXF, 2 ARRIRAW, 2 RED, 1 Nikon RAW, 3 BRAW, 2 CRM, 18 MP4, 11 MOV, …) — 0 errors
- 3/3 BRAW clips × 821 per-frame rows
- 7/7 RTMD-bearing clips × ~27k attribute rows + ~1.1M IMU samples each
- MCA labels match the bmx
labels.txtground truth byte-for-byte - macOS arm64 + Linux aarch64 + Linux x86_64 binaries all return identical metadata for image / X-OCN / BRAW spot checks
Binaries
This release ships three CLI binaries:
swift-exif-macos-arm64(5.7 MB, Mach-Oarm64,-O+WMO)swift-exif-linux-aarch64(24 MB, static ELF muslaarch64,-Onone)swift-exif-linux-x86_64(26 MB, static ELF muslx86_64,-Onone)
Linux binaries tested on alpine:3.20 for both arches.
1.5.0 — BRAW container metadata, ExifTool/ffprobe parity
Added
- Blackmagic RAW (
.braw) container metadata — new format support routed throughMP4Parser. BRAW is an ISOBMFF derivative with the legacy QuickTime layout (wide+mdathead,moovtail-placed, noftyp); the parser tolerates the missingftypand themdtadecoder grew handlers for BRAW's typed payloads including the BMD-specific type 71 (float32-BE pair) used for rectangle fields. Surfaces toCameraMetadata: camera make/model + firmware, color science generation, viewing gamma + gamut, compression ratio, shutter type; off-speedcaptureFps(distinct from the mvhd/stts-derivedframeRate); production slate (clip number / scene / take / reel / camera / environment / day-night); sensor area, crop / safe-area rectangles, LUT used, post-3DLUT mode, embedded LUT name, frameguide aspect ratio, gamut compression. Per-frame interpretation (white point, tint, absolute ISO, shutter angle) lives in proprietarybfdn/ctrnboxes inside the codec sample entry and remains unparsed. Verified against Pyxis 12K / 6K and Cinema 6K samples. - MPEG-TS bitstream decoding —
MPEGReadernow decodes inline H.264 / HEVC SPS, AAC ADTS frame headers, and PCR timestamps directly from Transport Stream packets, surfacing pixel format, profile/level, framerate, sample rate, channel layout, and timing on.ts/.m2tsfiles without ffprobe. NewMPEGBitstream.swifthouses the bit-readers (Exp-Golomb, NAL emulation-prevention unescaping, ADTS frame parsing). - SMPTE ST 377-4 MCA audio labels for MXF — multichannel audio labeling per SMPTE ST 377-4 is now decoded out of MXF audio descriptors. New
MCAAudioLabeling,MCALabelsRenderer,MXFMCAReader, surfaced throughVideoStream/VideoMetadataand the JSON exporter. New CLI subcommandswift-exif mxf-labelsemits a bmx-compatiblelabels.txtround-trip for production audio workflows. - Apple ecosystem support (Phase 18) —
AppleMakerNote.swift(iPhone MakerNote: lens model, ContentIdentifier, image stabilization, HDR mode, …),AAESidecar.swift(Apple Photos.aaeadjustment XML),HEIFAuxiliaryImages.swift(depth maps, alpha mattes, HDR gain maps via shared ISOBMFF / iloc), and Live PhotoContentIdentifiersurfaced fromMP4Parser. - Pentax, Leica, and Sigma MakerNote parsers — three additional vendor MakerNote implementations with matching writer support and round-trip test coverage.
- ExifTool / ffprobe parity initiative —
PARITY_PLAN.mdplus the first phases:- HEIC parity (Phase 1) —
infe-drivencontent_type, explicitmeta → iprp → ipcowalk, top-levelcolrfallback. - JPEG XL (Phase 2) — decode
SizeHeaderfrom the JXL codestream soFile:ImageWidth/Heightare populated. - MP4 streams (Phase 3.1 / 3.2 / 3.5) — preserve track declaration order in
streams, surface data tracks, hoist chapter tracks out ofstreamsinto a siblingchaptersarray. - MP3 / M4A streams (Phase 3.3) — emit a single synthetic audio stream from
--streamsto match ffprobe. - MKV track order (Phase 3.4) — preserve Matroska track declaration order.
- GPS / subtitle codec cosmetics (Phase 4) — render GPS coordinates with degree signs, alias
tx3gtomov_text.
- HEIC parity (Phase 1) —
Changed
- Format-level
durationnow matches ffprobe. Previouslyformat.durationwasmvhdverbatim; mvhd is the longest mdhd of any track, so files likeChapterMarkerDualSubtitle.mp4reported 98.691 s while audio/video both ended at ~71.16 s. The MP4 reader now caps mvhd atmax(audio, video)when subtitle/data tracks inflate it, and keeps the smaller mvhd value untouched when edit-list trimming legitimately makes it shorter than the longest essence stream. - Audio file
format.bit_ratenow matches ffprobe. For MP3 / M4A / FLAC, the containerbit_rateis now derived asfile_size × 8 / duration(whole-file rate including container overhead and AAC priming/postroll padding) instead of the audio stream's declared rate. - MXF and MPEG-TS per-stream
Duration. MXF essence descriptors and MPEG-TS streams don't carry per-track durations the way MP4 does. The format-level duration is now propagated to streams that don't already have one (matching ffprobe behaviour); legitimate per-stream durations are never overwritten. - GPMF SCAL divisor lookup is now hoisted out of the per-sample GPS5 loop in
GPMFReader— pure refactor, behavior preserved.
Fixed
- GIF parser sub-block handling — regression tests added covering the earlier sub-block-overrun hardening (truncated extension blocks, malformed image descriptors, unterminated sub-block chains).
Tests
1420 tests, 0 failures (26 skipped), covering BRAW slate extraction across Pyxis 12K / 6K / Cinema 6K, the new MakerNote families, MXF MCA round-trip via bmx, MPEG-TS bitstream parity vs ffprobe, and HEIF auxiliary image extraction.
1.4.0 — Recursive XMPValue, more RAW formats, C2PA thumbnails
Added
- NRW, SRW, and generic
.rawextension support —RawFormatnow listsnrw(Nikon Coolpix),srw(Samsung), andrawalongside the existing TIFF-based RAWs. Read and write parity with NEF / ARW / ORF / PEF via the sharedTIFFFileParser. ImageMetadata.extractC2PAThumbnails()— convenience accessor returning everyc2pa.thumbnail.claim.*/c2pa.thumbnail.ingredient.*assertion as aC2PAThumbnail(label, raw bytes, format suffix). Mirrors the existingextractThumbnail()pattern for EXIF IFD1 thumbnails.- Recursive
XMPValuefor nested structured schemas — Adobe Camera Raw'sMaskGroupBasedCorrections(rdf:Bag of corrections, each holding its own rdf:Bag of mask sub-structs), face regions inmwg-rs, and other recursive XMP shapes now ride the generic API instead of being hand-rolled. Both writer and reader recurse.
Changed
-
Breaking —
XMPValue.structurepayload is now[String: XMPValue](was[String: String]), andXMPValue.structuredArrayis[[String: XMPValue]](was[[String: String]]). Direct enum-pattern consumers must wrap field values as.simple(...)when constructing them, and unwrap when reading.Two new convenience accessors smooth the migration:
flatStructureValueandflatStructuredArrayValuereturn the legacy[String: String]shape by dropping any non-.simpleentries — useful for IPTC / xmpDM timecode / stRef schemas.XMPData.flattenandXMPData.wrapSimpleround-trip between the two forms. -
Build script drops macOS Intel —
Scripts/build-release.shnow ships macOS arm64 + Linux x86_64-musl + Linux aarch64-musl only.
Tests
1087 tests, 0 failures (14 new tests covering recursive XMP structures, C2PA thumbnail edge cases, and NRW write round-trip).
1.3.1 — Scan Type resolution + parser hardening
Added
VideoScanType+scanType/scanOrderhelpers onVideoMetadata
andVideoStream— derive MediaInfo-style "Scan Type" (Progressive /
Interlaced / Unknown) and "Scan Order" (TFF / BFF) UI columns without
enumerating everyVideoFieldOrdercase.fieldOrderstill encodes
both values in one enum and remains the ground truth.- MXF
FieldDominanceparsing (0x3212) — SMPTE 377-1 §G.2.51 tag now
resolves TFF vs BFF for interlaced essence descriptors that carry it.
Fixed
fieldOrdernow resolves for every supported container. Previously,
MP4 / MOV without afielatom, Matroska / WebM withoutFlagInterlaced,
and MXF withFrameLayout=1(separated fields) all returnednilor
.unknown, leaving downstream "Scan Type" UIs blank for the majority
of real-world files. New behaviour:- MP4 / MOV: absence of
fieldefaults to.progressive(matches
ffmpeg'smovdemuxer and iPhone / camera convention). - Matroska / WebM: absence of
FlagInterlaced/FieldOrder
defaults to.progressivefor video essence (cover-art MJPEG tracks
keepnil). VP8 / VP9 / AV1 have no interlaced coding mode; HEVC /
H.264 writers only emit these elements for genuinely interlaced
source. - MXF:
FrameLayout=1(separated fields) andFrameLayout=3
(mixed) now resolve to.topFieldFirst(the broadcast convention and
what MediaInfo / ffprobe report for untagged interlaced essence).
FieldDominance=2overrides to.bottomFieldFirst. Previously these
returned.unknown.
- MP4 / MOV: absence of
Security
- Int-overflow hardening across binary parsers — malformed input can
no longer trap the process via arithmetic overflow on 32-bit lengths,
offsets, or sub-block sizes. Affected paths: ICC profile, IPTC IIM,
MPF, XMP, PNG, PSD, and the sharedDataslicing helpers. - MP4 chapter allocation cap —
chpl(Nero) andudta > chap
readers refuse chapter counts that would allocate more than a sane
ceiling, stopping a malformedcountfield from triggering
gigabyte-scaleArray.reserveCapacityon import. - GIF sub-block overrun fix — application-extension sub-block reader
no longer walks past the declared data length when a truncated stream
omits its terminator. - Video-read memory cap — the MKV / WebM front-end limits aggregate
bytes buffered for a single import, keeping multi-file scans (batch
imports, folder-watch) well under 1 GB resident even when fed
unusually large Matroska clusters. - CBOR decoder overflow guard — string and byte-string readers
reject lengths that would overflowInt, preventing a crafted C2PA
manifest from crashing the CBOR front-end. - zlib deflate-bomb cap — PNG / ICC / XMP inflate paths stop
producing output once a generous decompressed-size ceiling is
reached, so a tiny deflate stream can no longer expand to gigabytes
and exhaust memory.
Validation
Audited against a 22-file video fixture set (H.264, HEVC, VVC, AV1,
VP8/VP9, ProRes, ProRes RAW, APV, AVC-Intra, MXF broadcast):
- All 20 modern MP4 / MOV / MKV / WebM files now resolve Scan Type to
Progressive (previouslynilfor 6 of them). - Both broadcast MXF files (AVC-Intra 50) now resolve to Scan Type =
Interlaced, Scan Order = TFF — matches MediaInfo output.
1.3.0 — Chapter markers + multi-source timecodes
Added
- Chapter markers across three container families:
- MP4 / MOV / M4V: QuickTime text-track chapters (
tref > chappointing
at atext/subttrack whose stts-timed UTF-8 samples are the titles —
written by DaVinci Resolve, Apple Compressor, iTunes, ffmpeg
-map_chapters), with Neroudta > chplas the fallback (x264, ffmpeg,
MP4Box). - Matroska / WebM: top-level
Chaptersmaster element — every
EditionEntry+ChapterAtom, honouringEditionFlagHiddenand
ChapterFlagHidden; supports bothChapLanguageand the newer
ChapLanguageBCP47. - New
VideoChapterstruct (index, optionalid,startTime,
endTime,title,language, computedduration). Exposed via
VideoMetadata.chapters.
- MP4 / MOV / M4V: QuickTime text-track chapters (
- Provenance-tagged
timecodes: [Timecode]onVideoMetadata—
every independent clip-level source (tmcdTrack,quicktimeUdta,xmpDM,
xmpDMAlt,mxfMaterialPackage,mxfFilePackage,sonyNRT) recorded
separately with optional frame-rate companion. The scalartimecodestays
in sync with the first recorded value for backward compatibility.
recordTimecode(value, source:)helper dedupes entries and appends a
timecode mismatch: …warning when two sources disagree. - XMP timecode parsing —
XMPData.startTimecode/.altTimecode
decodexmpDM:startTimeCode/xmpDM:altTimecode, mappingtimeFormat
(24Timecode,29.97Timecode,2997DropTimecode,50Timecode, …) to a
numeric fps. - MXF TimecodeComponent labelling — the first-encountered component is
tagged.mxfMaterialPackage, subsequent ones.mxfFilePackage(header
metadata always puts MaterialPackage before SourcePackage). - Sony NRT LtcChangeTable —
LtcChange@frameCount="0"now feeds a
.sonyNRTentry intimecodes, accepting both the hex-encoded SMPTE 12M
LTC word (XDCAM professional bodies) and the already-formatted
HH:MM:SS:FFstring (Alpha consumer bodies). - CLI
--streams --format jsonnow emits:format.Timecodes— array of{ value, source, frameRate }entries.format.ChapterCount/ChapterStartTimes/ChapterEndTimes/
ChapterDurations/ChapterTitles/ChapterLanguages.- Per-chapter rows in the
streamsarray withStreamType="chapter",
carryingIndex,ChapterUID,StartTime,EndTime,Duration,
Title,Language— mirrors ffprobe's-show_chapterslayout.
Changed
- QuickTime text chapter tracks are no longer reported as subtitles.
Any track referenced viatref > chap(on any parent track — DaVinci
writes it on every trak, ffmpeg's mov muxer writes it only on non-video
traks) is now excluded fromVideoMetadata.subtitleStreams, matching
ffprobe's-select_streams soutput exactly. Real user-facing
subtitles (tx3g, stpp, wvtt, …) are unaffected. - Per-stream timecode is only set when the video track explicitly
cross-references atmcdtrack viatref > tmcd— matches ffprobe's
behaviour of leaving per-stream tags empty for tmcd tracks that are
only advertised at the clip level (e.g. Atomos Ninja ProRes RAW).
Fixed
- DaVinci Resolve and ffmpeg-muxed MP4/MOV files no longer inflate the
subtitle count by including the hidden chapter-text track.
Validation
Verified end-to-end against:
- DaVinci Resolve MP4 + MOV exports (with 0, 1, and 2 real subtitle
tracks alongside the chapter text track). - ffmpeg-remuxed MP4 with a second SRT-sourced subtitle track and
chapters preserved via-map_chapters. - 90 GB Harry Potter Matroska with 37 chapters (English).