isobmff: improve streaming metadata parsing and callback flow - #64
Conversation
|
Welcome back @evanoberholster !! Very much excited to see an update from you. 😃 I hope all is well. |
|
Thanks Matt @mholt. Glad to be back and hope to update this library. Any suggestions on things that need urgent attention? |
|
Nothing urgent that I know :) One of the top things on my wishlist is still a way to enumerate all the metadata discovered (XMP, EXIF, ...) but that's not urgent! |
…on tests - make box/iinf/infe/iloc/pitm parsing bounds-safe and panic-free - improve progressive ReadMetadata traversal and EOF behavior - tolerate non-TIFF/unsupported mdat Exif candidates instead of aborting - downgrade unknown brand/box logging to debug for forward compatibility
- add xpacket header probing and pass XPacketHeader into XMP callbacks - wrap Exif/XMP/preview callbacks with io.LimitedReader - treat callback errors as non-fatal except io.EOF; set metadata goal flags only on callback success - stop scanning once requested metadata goals are satisfied - refactor top-level/inner box header parsing for clearer validation paths - improve image type fallback using compatible ftyp brands - fix iprp to propagate child parse/close errors - simplify iloc by removing dead optionSpeed branch - expand CR3 parsing cleanup and preview extraction paths (CMT/THMB/PRVW) - add callback and reader tests, xpacket tests, and benchmark sample selection/label cleanup BREAKING CHANGE: - NewReader now returns *Reader - XMPReader signature is now func(io.Reader, XPacketHeader) error
- merge core BMFF types/errors/ftyp parsing into unified package layout - split box/type and metadata parsing into dedicated files (boxtype, item/meta boxes, metadata) - replace callback state booleans with compact metadata goal/have flags - improve reader/box internals and error handling for bounded reads/discards - add coverage for callbacks, brand detection, iloc/idat offset resolution, and JXL/FTYP handling
…itions - streamline box/meta parsing flow for progressive reads - reduce allocations with fixed-size string buffers and stricter MIME limits - narrow HEIF/JXL processing to Exif/XMP-required paths - change CR3 preview completion to PRVW-only and stop after Exif+XMP+PRVW
- migrate callers to callback-based `isobmff.NewReader(r, exif, xmp, preview)` - centralize metadata scanning with EOF-tolerant read-until-done helpers - extend `meta.PreviewHeader` with `ImageType` and `Source` (THMB/PRVW) - include JXL/HEIC handling in decode paths that use ISOBMFF/TIFF readers - tighten TIFF type fallback to preserve known input type unless detection is confident
- move isobmff, jpeg, png, tiff, and xmp packages into `meta/*` - update all internal imports and logger wiring to new package paths - keep benchmark/test sample assets under `meta/isobmff/samples` - remove unused ISOBMFF helpers flagged by lint - simplify xpacket BOM handling (`bytes.TrimPrefix`)
…ace lookups - replace legacy reader implementation with a gosax-based streaming decoder (`decode.go`) and keep parsing allocation-aware - add container-aware `Parse` support for sidecar, JPEG APP1, and ISOBMFF payloads (CR3/HEIC/HEIF/AVIF/JXL) - move `xmpns` types into `meta/xmp` and centralize `Name/Namespace/Property` lookup logic; remove duplicated fallback lookup path - expand ExifTool compatibility: - root/toolkit aliases (`x:XMPToolkit`) - xmpMM history aliases (`HistoryAction`, `HistoryWhen`, etc.) - CRS alias handling (`RedHue`/`BlueSaturation`, etc.) - `ColorTemperature` support - refactor CRS to materialize only core fields; treat HSL/tone-curve tags as compatibility no-ops to avoid unnecessary memory use - harden parser helpers (`parseUint`, rational parsing, permissive error flow) and improve debug visibility for non-`ErrPropertyNotSet` parse warnings - add/refresh tests and benchmarks for lookup behavior, CRS handling, parser edge cases, and embedded metadata parsing BREAKING CHANGE: removed `meta/xmp/xmpns`; use `meta/xmp` for Name/Namespace/ Property types and identify helpers.
- Refactor `meta/xmp` decoding to a faster streaming parser path and centralize name/namespace identification. - Expand XMP tag coverage to align closer with ExifTool, including missing `XMP::exifEX` and `XMP::tiff` fields. - Add normalized GPS model in `meta.GPS` and route EXIF GPS parsing through it. - Add `meta.ShutterSpeed` (`float32`) and use it for `Exif.ShutterSpeedValue`. - Improve EXIF date/subsecond handling and keep subseconds merged into base timestamps. - Add MWG Regions parsing support (`RegionInfo`, region list + extensions). - Add/extend tests for parser, identify, EXIF, TIFF, DC, and regions. - Reorganize XMP fixtures under `meta/xmp/test`, add descriptive CR3 sidecar samples, and update fixture docs (`README.md`). - Update XMP benchmarks to use current fixture paths.
- Add FromBytes([]byte) with fast matching for common MIME types, extensions, and short names - Add in-place ASCII lowercase normalization helper for byte-based matching - Fall back to FromString for compatibility with less common inputs - Add TestFromBytes coverage for MIME, extension, whitespace, and unknown cases chore(gitignore): ignore local Go temp/cache artifacts - Ignore .go* and .cache* files/directories generated in local workflows
Add `meta.Rational32` and `parseRational32` to parse/store signed rational fields from XMP without losing numerator/denominator precision. Switch EXIF parsing to use `Rational32` for `ExposureTime` and `ShutterSpeedValue`, and update tests to assert via `Float64()`. Also parse `FocalPlaneResolutionUnit` as `uint8` to match XMP value range.
- Replace optional namespace pointers in XMP with value fields and add namespace presence tracking via nsFlag + IsParsed(ns Namespace) - Mark parsed namespace presence during parse and update CRS/MM/Photoshop/ DynamicMedia/Lightroom/Regions tests for the new semantics - Align XMP EXIF model with ExifTool/CIPA expectations by removing non-spec EXIF-only additions (DateTime, Subsec*, SamplesPerPixel, PhotometricInterpretation) and simplifying date handling - Add README examples for parsing sidecar and embedded XMP metadata - Add shared language constants for x-default and common locales
- move date parsing internals out of parser.go into new date.go - consolidate fast-path and fallback date parsing behind parseDate - add cached fixed-offset timezone locations to reduce allocations - remove dateShape indirection and simplify fast-layout detection - update dc:date parsing to use unified parser and enforce CIPA formats - add/adjust date-focused tests (fast path, fallback hook, timezone cache, non-CIPA rejections) - clean up xmp cmd sample constants and refresh benchmark comment labels
…oss meta/xmp/imagetype - add `golangci.yml` and modernize `.github/workflows/golangci-lint.yml` - update CI behavior for draft PRs, only-new-issues, and `govulncheck` security scanning - tidy and refresh module metadata (`go.mod`/`go.sum`), including dependency updates and cleanup - fix `imagetype` test lint failures (`errcheck`, `errorlint`, `govet`, `misspell`) - fix `meta/*` lint classes requested: `govet`, `misspell`, `gofmt`, `wastedassign`, and `errorlint` - refactor `meta/xmp` parsing helpers (`parseInt8`, safer pooled readers, `errors.Is` usage) and add targeted `gosec` suppressions for bounded casts
…ders/tests** - convert ISOBMFF size/remain/length handling from mixed `int64`/`uint*` to `int` across box parsing, metadata traversal, CRX, PRVW, and XPacket paths - update `XPacketHeader.Length` and related call sites/logging/tests to use `int` - add explicit bounded conversions (`uint64ToInt`) and keep overflow checks centralized - refresh ISOBMFF tests for new max-int behavior and remove redundant loop-var copies - add targeted `nolint:gosec` annotations where casts are spec-bounded **chore(lint): fix root benchmark/reader lint findings** - add checked pooled-reader helper in `imagemeta.go` to satisfy `errcheck` type-assertion rules - replace direct error equality checks with `errors.Is` in benchmarks (`errorlint`) - remove benchmark shadowed `err` variables (`govet`) and clean pooled-reader error paths
- add a checked pooled-reader accessor in `imagemeta.go` to avoid unchecked type assertions - switch benchmark error comparisons to `errors.Is` (`errorlint`) - remove benchmark variable shadowing (`govet`) and improve pooled-reader cleanup paths
…de path - add a new high-performance `meta/exif` package with: - typed EXIF model (`IFD0`, `IFD1`, `IFD2`, `ExifIFD`, `GPS`, DNG/TIFF-EP groups) - streaming reader/parser state machine for TIFF/JPEG/ISOBMFF metadata - tag/ifd subpackages with explicit tag/type definitions and validation - maker-note framework with Apple, Nikon, and Canon support - add Canon maker-note model and parser support in `meta/canon` and `meta/exif/makernote` - switch `imagemeta.Decode*` functions from `exif2` to `meta/exif` - update CR3/HEIF/AVIF/JXL handling to use ISOBMFF append decode flow - wire package logging to the new exif implementation - add extensive unit tests, benchmarks, and parser/tag coverage tests - update ISOBMFF metadata tests/behavior to align with new EXIF decode flow
…ta/exif/canon.go`) and make decoders progressive/short-read tolerant - add structured Canon models and enums for camera settings, AF info, face detect, processing, lighting, HDR/multi-exp, and related tags - improve Canon maker-note directory handling for raw IFD, `Canon\0\0\0`-prefixed payloads, and embedded TIFF headers (CR3 path) - add parser options and parsed-tag tracking improvements across maker-note flow - add/expand unit tests and benchmarks for Canon decode paths and reader hot paths - generate/update Canon stringer/msgpack artifacts and lens model string mappings - add `imagetype.FileType.IsISOBMFF` - update lint workflow execution and reporting behavior - `go test ./meta/exif ./meta/canon ./imagetype ./meta/isobmff`
- configure logging via `meta/exif` and `meta/isobmff` packages - eliminate incompatible `exif2.Exif` assignment from `imagemeta.Decode` - simplify command flow to decode a single local `1.cr3` file This updates the sample command to compile against the current `imagemeta.Decode` return type and parser wiring.
… hot paths - move BufferedReader interface to meta/utils and update exif reader usage - simplify tag validation and IFD0 parsing flow in parser - simplify Canon maker-note parsing helpers and warning placement - replace embedded-value lookup table with switch-based IsEmbedded - export TIFF endian signature helpers in utils - add Nikon camera model string table - refresh benchmark comment snapshots and remove tag decode microbenchmark file
Add broader Canon maker-note coverage, including shot info, file info, AF config, focus-distance helpers, and generated lens model mappings. Refactor EXIF and ISOBMFF readers to reuse pooled bufio readers via shared utilities, and fix small parsing issues such as aperture division and dimension orientation detection.
- move Canon, Nikon, Sony, and Panasonic maker-note models under meta/exif/makernote - add Nikon, Sony, and Panasonic maker-note parsing paths and tests - merge EXIF IFD types into meta/exif/tag and update parser/reader code - move TIFF header scanning under meta/exif and update callers - remove remaining exif2-based header, logger, and benchmark dependencies - consolidate EXIF benchmarks and helper utilities around the current parser - update EXIF value/model handling for lens info, offset time, user comment, and APEX-derived values
- refactor IFD0/ImageIFD structures around metadata-focused fields - add typed ResolutionUnit and SubfileType values and ExifTool enum mappings - expose image offset/length from strip/thumbnail references - parse DNGAdobeData and rebase embedded Adobe maker notes - normalize make parsing and simplify maker-note parsed-tag tracking - move GPSInfo into its own file and tighten related parser/tests - update XMP EXIF/TIFF mirrors and add coverage for new value mappings
Restructured makernote packages, renaming make.go to cameramake.go Refined Canon, Nikon, Panasonic, Sony makernote parsing and tag handling Updated core EXIF parser, reader, model, and value handling for improved compatibility Added/adjusted tests and benchmarks for EXIF makernote workflows
ScanMetadata ScanMetadataWithReaderAt The existing streaming Exif/XMP flow remains intact, while ReaderAt-backed scans can independently read segment payloads without moving the forward marker scanner. Split/expanded the JPEG package into focused files: metadata.go: JPEG metadata model and SOF dimensions jfif.go: APP0 JFIF ciff.go: APP0 Canon CIFF / HEAPJPGM mpf.go: APP2 MPF / MPImage entries icc.go: APP2 ICC chunk assembly and selected profile/header tags photoshop.go: APP13 Photoshop resources and IPTC IIM adobe.go: APP14 Adobe DCT marker scanner.go: forward scanner and ReaderAt segment reads app.go: APP marker dispatch marker.go: marker constants and signatures
- add DecodeCRW support in the main imagemeta dispatcher - expose CIFF parsing for Canon CRW and APP0 CIFF metadata - map parsed CIFF fields into the EXIF model - move shared metadata callback types into meta - move BMFF metadata loop and JPEG scan adapter into format packages - refactor Canon maker-note parsing helpers to reduce repeated bounds checks - improve JPEG metadata test coverage, including CIFF/CRW paths - update EXIF benchmarks to use aligned JPEG benchmark samples
- add shared meta/logging helpers and structured component tagging - unify exif/isobmff/jpeg log style and header field naming - move xmp debug warnings from stdout to structured logs - add logging coverage for decoded metadata items and header marshaling - keep hot-path benchmark behavior in line after logging changes
- restructure EXIF model and time-tag handling for IFD0/ExifIFD - add/expand Canon, Nikon, Panasonic, and DNG maker-note support - normalize tag parsing and exiftool-style value formatting - add allocation-free ExifVersion parsing and parser coverage tests - update comparator-related helpers and metadata flattening
…s, and export reader pool - Expand Sony maker-note parsing with CameraInfo, FocusInfo, AFInfo, and structured tag decoding - Restructure Apple maker-note into dedicated package with run-time parsing and fallback scanner - Change ExifVersion from string to typed [4]byte with String() method - Export AcquirePooledReader/ReleasePooledReader for external use - Refactor reader to drain queued tags inline and propagate errors - Fix model tests: signed GPS fields, missing WithRef methods, nil IFD1 pointer, VersionID expectation
remove tidwall/pretty and make cmd/main.go non-panicking switch SetLogger to shared meta/logging logger add cmd/corpus JSONL batch decode runner harden JPEG marker handling (invalid-size resync + safe panic recovery + EXIF short-circuit) treat ExifLength==0 as unbounded for TIFF/RAW decode add short-buffer imagetype fallback for partial reads
- Updated logging interfaces and implementations across the meta/exif, meta/isobmff, meta/jpeg, meta/xmp, and preview packages to utilize the new slog logging framework. - Modified logger initialization and event creation methods to align with slog's structure. - Enhanced structured logging capabilities by implementing MarshalLogObject methods for various types. - Introduced new logging levels and adjusted existing logging calls to ensure consistent logging behavior. - Updated tests to reflect changes in the logging framework, ensuring compatibility and correctness.
- Updated logging methods to use a centralized logger retrieval mechanism. - Replaced direct logger assignments with GetLogger and SetLogger for consistency. - Improved string formatting in various components by using strings.Builder for better performance. - Removed the RationalU type from the tag package as it was deemed unnecessary. - Added new utility functions for handling IFD types and normalized type resolution. - Enhanced test coverage for logging and utility functions to ensure reliability.
- Updated `parser_ifd0_image_test.go` to use `parseIFD0Tag` instead of `parseIFD0ImageTag` for consistency. - Simplified maker-note accessors in `parser_makernote.go` by removing redundant `makerNoteInfo` function calls. - Streamlined maker-note make resolution in `reader_makernote.go` to directly access `r.Exif.MakerNote`. - Enhanced handling of camera setting values in `utils.go` for clarity and efficiency. - Removed unused GPS parsing functions in `values.go` to clean up the codebase and improve maintainability.
- Removed the ModelIDFromModel function from Sony maker-note as it is no longer needed. - Added new fields to the Sony struct for additional metadata such as Brightness, LongExposureNoiseReduction, HDR, and others. - Updated the parseSonyTag function to handle the new fields in the Sony maker-note. - Refactored Nikon parsing functions to utilize new decoding functions for VRInfo, WorldTime, ISOInfo, and others, improving code clarity and maintainability. - Simplified the Nikon parsing logic by removing redundant code and utilizing dedicated decoding functions. - Updated tests for Sony maker-note to reflect changes in expected values for LensType and SonyModelID. - Adjusted the handling of MakerNote pointers in the parser to ensure proper initialization.
- Introduced `canon_string_test.go` to validate the behavior of `canonString` function with various test cases. - Refactored Canon makernote decoding functions to use method receivers on `Seq16` and `Seq32` types for better readability and consistency. - Removed obsolete `makernote_tags_test.go` file. - Added comprehensive tests for Nikon decoding functions in `decode_test.go`, ensuring correct parsing of version strings, bitset indices, and lens formatting. - Updated Sony makernote decoding comments for clarity and consistency. - Removed unused color fields from `IFD0Tag` and adjusted related parsing logic in `parser.go` and tests to reflect these changes. - Enhanced `normalizeValueString` function to handle underscores and whitespace more effectively, improving string normalization.
- Updated test cases in `meta/exif/values_helpers_test.go` to ensure proper variable scoping. - Improved parsing functions in `meta/exifTypes.go` to safely cast integers and handle potential errors. - Modified error handling in `meta/isobmff/metadata.go` to return errors instead of nil on failure. - Adjusted JPEG reading logic in `meta/jpeg/app.go` and `meta/jpeg/extended_xmp.go` to safely cast integers and handle errors. - Refactored binary reading functions in `meta/jpeg/binary.go` to remove unnecessary parameters and improve clarity. - Simplified CIFF parsing in `meta/jpeg/ciff.go` by removing redundant error returns. - Enhanced ICC profile handling in `meta/jpeg/icc.go` to use new TRCLength type and ensure safe casting. - Updated tests in `meta/jpeg/jpeg_test.go` to handle read errors properly. - Cleaned up logging in `meta/jpeg/log.go` to remove unnecessary conversions. - Improved buffer handling in `meta/jpeg/scanner.go` to ensure safe casting and error handling. - Enhanced PNG header scanning in `meta/png/png.go` to safely handle offsets. - Introduced new utility functions in `meta/utils.go` for safe integer casting. - Updated tests in `meta/utils/limited_buffered_reader_test.go` to use `errors.Is` for EOF checks. - Refined UUID handling in `meta/uuid.go` to ensure proper error handling during marshaling and unmarshaling.
417e447 to
dc8240b
Compare
Highlights
What Changed1) Parser ConsolidationLegacy paths were retired in favor of the new package structure:
This removes split ownership between old/new parser code and makes format responsibilities clearer. 2) EXIF parsing refactored
Canon-specific parsing was moved into dedicated EXIF logic and maker-note subpackages. 3) MakerNote Coverage Increase
4) JPEG Metadata Scanner Upgrade
Scanner APIs were cleaned up for better stream + 5) ISO-BMFF / CR3 Flow Improvements
6) XMP Refactor
7) Image Type API Improvements
Public API ImpactFor normal consumers using top-level decode APIs, this is largely additive/improving:
Compatibility noteIf downstream code imported internal/legacy parser packages directly (old Quality and Safety ImprovementsThis branch trends strongly toward:
TODO items completed
SummaryThis is an extensive rewrite that improves on exif parsing with stronger correctness, better structure, and improved extensibility for future parser work. The goal remains aligning as much as possible with exiftool which remains a work in progress. |
BREAKING CHANGES: