Repository navigation
Preserve source metadata in extracted image manifests - #3861
Conversation
MrNeRF
left a comment
There was a problem hiding this comment.
Built 668bed1 and ran it. All 27 Media ctest contracts pass. I also ran the CLI by hand on a small mkv with a comment and a location tag:
- Flag off: the manifest and PNGs are identical to dev apart from timings, and the tag shows up nowhere.
- Flag on: the tag appears only in
source_metadata, and the PNGs don't change. - All frames rejected: writes
frames: []only when preserving. - Resume: flipping the flag on a resumed run fails with "Checkpoint identity or frame changed" and leaves the images alone.
So the behaviour is right. What I'd like changed is the duplication.
-
The result envelope
{"schema_version", 1}, {"success", true}, {"metadata", ...}, {"summary", ...}is now built in four places.cli.cpp:103and:113already had it, and the PR addssource_metadata_jsoninpy_media.cppandvideo_frame_extractor.cpp:1524. The point of the Python API is "same JSON as the CLI", so could you add onejson_detailhelper that takes the catalog and summary, and use it in all four places? The same goes for"preserve_static_container_v1": it's written separately into the checkpoint identity (resumable_file_sink.cpp:175) and the manifest (video_frame_extractor.cpp:2585). One constant keeps the two from drifting apart, and drift there would break resume. -
test_media_bindings.pygrows by 368 lines, and several cases retest things that are covered elsewhere:test_named_xmp_and_partial_rejection_keep_original_packetsrepeats the native XMP contracts inmetadata_observation_contracts.cpp.test_default_budget_reports_partial_collectioncould be one more entry in the CLI parity loop.test_errors_and_recovery_use_the_existing_contractmostly checks nanobind's path conversion.- The 4097-byte "spaced" rotation case duplicates
probe_unit_contracts.cpp. The 9 MiB case through the full pipeline is the useful one.
Could you trim those?
Smaller things:
test_source_metadata_keeps_other_streams_without_decoding_themonly checks that the audio tag is kept. It doesn't check anything about decoding, so a name like..._keeps_other_stream_tagswould fit better.- The
# -*- coding: utf-8 -*-lines added totest_media_bindings.pyandtest_ingest_cli.pydo nothing on Python 3.
|
Addressed in b66f820.
Validation: 54 Python binding cases and 64 metadata CLI cases passed on Windows, Linux and macOS arm64, with the declared skips. Native contracts passed on all three. Linux ASan/UBSan with leak detection passed the native/CLI checks and the two real 9 MiB extractions. CMake regenerated the media stub with no API changes; clang-format 18, locale, I/O, backend-neutrality and error-debt checks passed. The PR description now reflects the current test coverage. The earlier macOS Actions failure was the unused cuda_runtime.h include in tests/test_collision_jobs.cpp, already present in dev. Its removal is already included in #3844; this followup keeps the media review changes separate from that fix. |
MrNeRF
left a comment
There was a problem hiding this comment.
Thanks, all four points are done. sourceMetadataResult now serves the CLI, Python and the manifest, the policy string is one constant, the four duplicate binding tests are gone, and the oversized-rotation case runs only through the full extraction.
I rebuilt b66f820 and reran my checks. All 27 Media ctest contracts pass (CLI and Python binding contracts included). With the flag off, the manifest and PNGs are identical to dev, and the tag shows up nowhere. With it on, the tag appears only in source_metadata. All frames rejected still writes frames: [] only when preserving. Flipping the flag on resume still fails with rc 4 and leaves the images alone.
The macOS failure is dev's #include <cuda_runtime.h> in tests/test_collision_jobs.cpp:18, not this PR.
Image extraction can now keep the input video's static metadata alongside the exported frames. Enabling source preservation writes one catalog and summary to extraction_metadata.json, while each frame keeps its existing source timestamp and extraction information. Python callers can also read an image's source metadata and interpreted properties through the same catalog and JSON representation used by the CLI.
APIs and file output
CLI example:
Python examples:
The retained observation can include GPS, identifiers and private information in tags or opaque payloads. The opt-in applies to the entire observation. Turning it off affects newly generated manifests; existing artifacts remain on disk. Atomic publication applies to the manifest itself, while image files and the manifest keep the existing recovery semantics.
Shared probe hardening and test execution
The stream descriptor previously copied the entire rotate tag independently of the raw metadata collector's byte budget. Limit that presentation copy to 4096 bytes and expose rotate_tag_limited when larger text is omitted. The summary propagates the limited state. Numeric interpretation still borrows the demuxer value, preserving valid angles, including values with extensive whitespace, and the existing tag/display-matrix precedence. The raw collector retains its separate budget and exact collected bytes. Ordinary orientation JSON is unchanged; the omission marker is emitted when needed.
The media Python tests now start with PYTHONUTF8=1, matching the Windows workflow. The CLI test reads the UTF-8 manifest explicitly and verifies its Unicode source locator. Python stubs were regenerated through the CMake targets.
Validation
Local validation of the current media revision:
The binding cases cover CLI parity, partial collection, owned results, concurrent calls, native paths and supported image carriers. Parser-specific XMP contracts and the 4095/4096/4097-byte rotation boundaries remain in their existing native tests. The full extraction cases cover PNG/JPEG/EXR/TIFF16/JXL/AVIF output, opt-in/default behavior, unchanged PNG output bytes, additional stream tags, invalid UTF-8 tag bytes, output/source geometry separation, resume policy changes, empty selections, previous manifest replacement, cancellation and manifest path collisions. The 9 MiB rotation case remains an end-to-end Python extraction test.
The declared Python skips concern the existing reserved-publication qualification and native non-UTF-8 filename support. A fresh full application rebuild, full hardware backend matrix, TSan and process-wide allocation-failure qualification were not completed for the serializer/test refactoring. The retained metadata scope is static container and stream observations.