Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 35 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,30 +108,52 @@ jobs:
with:
toolchain: "1.92.0"
- uses: Swatinem/rust-cache@v2
# `rawshift-video` is parked and unpublished for v1, and the facade no
# longer has a `video` feature, so this builds the crate directly.
- run: cargo build -p rawshift-video --all-features
# Assert the video build pulls in zero image crates — this is the contract
# the image/video workspace split exists to guarantee, and it must keep
# holding while the crate is parked.
- run: cargo build -p rawshift-video --features full
# The contract the image/video workspace split exists to guarantee:
# video shares only rawshift-core and rawshift-hwdec with image, and
# pulls in none of the image codec stack.
- name: Assert no image crates in the video dependency tree
run: |
tree=$(cargo tree -p rawshift-video --all-features --prefix none --no-dedupe)
tree=$(cargo tree -p rawshift-video --features full --prefix none --no-dedupe)
echo "$tree"
if echo "$tree" | grep -qiE 'rawshift-image|zune-|libheif|libwebp|img-parts|little_exif|resvg|ravif|jxl-oxide|^image '; then
echo "::error::video build pulled in image crates"
exit 1
fi
echo "OK: video build is free of image crates"
# Assert the parked crate stays out of the facade entirely.
- name: Assert the facade does not depend on rawshift-video
# Opacity: no backend type may appear in a public signature. The crates
# wrap every backend rather than re-exporting it, so swapping one is not
# a breaking change for callers — this keeps that true.
- name: Assert backend types stay out of the public API
run: |
tree=$(cargo tree -p rawshift --all-features --prefix none --no-dedupe)
if echo "$tree" | grep -q 'rawshift-video'; then
echo "::error::rawshift facade depends on the parked rawshift-video crate"
set -o pipefail
leaks=$(grep -rn --include='*.rs' \
-E '^\s*pub (fn|struct|enum|trait|type|const)[^;{]*\b(symphonia_[a-z_]*|mp4_atom|rawshift_hwdec)::' \
crates/rawshift-video/src crates/rawshift-video-core/src \
crates/rawshift-video-isobmff/src crates/rawshift-video-matroska/src \
crates/rawshift-video-hwdec/src || true)
if [ -n "$leaks" ]; then
echo "$leaks"
echo "::error::a backend type appears in a public signature"
exit 1
fi
echo "OK: facade is free of rawshift-video"
echo "OK: no backend type in a public signature"
# The facade must expose video, and must still build without it.
- name: Assert the facade wires video behind its own feature
run: |
cargo tree -p rawshift --features video --prefix none --no-dedupe \
| grep -q 'rawshift-video' \
|| { echo "::error::the facade's video feature does not pull in rawshift-video"; exit 1; }
if cargo tree -p rawshift --no-default-features --features image --prefix none --no-dedupe \
| grep -q 'rawshift-video'; then
echo "::error::an image-only facade build pulled in rawshift-video"
exit 1
fi
echo "OK: video is behind its own facade feature"
# Container parsing and metadata must work with no decoder at all, which
# is what every backend-less target gets.
- name: Build the containers with no codec features
run: cargo build -p rawshift-video --no-default-features --features mp4,mkv

# ── Compile boundaries (issue #34) ──────────────────────────────────────────
# The `hw-*` backend pins are *verified* feature flags: rawshift-hwdec holds
Expand Down
37 changes: 37 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,43 @@ dependencies are not permitted because they prevent publishing rawshift.
that only updates the version requirements and lockfile, a full test +
benchmark run, and a CHANGELOG.md note for any behavioral change.

### Video is outside gamut's charter

The policy above governs **image** work. It does not apply to video, because
gamut has declared video permanently out of scope. From gamut's README:

> **gamut is image-first.** Even where a format's codec (AV1, AV2, VVC, HEVC)
> is fundamentally a video codec, gamut implements only the intra-frame,
> still-image subset those formats use — no inter-frame prediction, no motion
> compensation, no video sequences. […] gamut will not grow video primitives.

`gamut-isobmff` says the same at the container level: *"Image sequences/tracks
and item protection are out of scope."* It parses the HEIF still-image item
model (`meta`/`iloc`/`iinf`/`iprp`), not the movie model (`moov`/`trak`/`stbl`),
so it cannot back MP4/MOV video however much the two formats share a box
grammar.

Consequently, for `rawshift-video` and the `rawshift-video-*` crates:

- Do **not** open gamut issues for video containers, video codecs, or
multi-frame primitives. They will be declined as out of charter, and
`blocked-upstream` is the wrong label for them.
- Third-party dependencies are permitted where a mature library exists —
judged by the `PRINCIPLES.md` rule ("reinvent the wheel only when
necessary"), not by the upstream-first rule. Current choices:
`symphonia-format-isomp4` and `symphonia-format-mkv` for demuxing (MPL-2.0,
matching rawshift's own license; pure Rust with no build script).
- Video **decode** stays in `rawshift-hwdec`. `docs/SUPPORT.md` fixes the
reasons: software HEVC is ruled out by the patent posture, and FFmpeg/libav
is ruled out on license and portability grounds (see "Excluded" there).
- Everything else in this file still applies to video: no git dependencies,
the fixed target list, the MSRV rule, and a CHANGELOG note for behavioral
change.

Image work is unaffected — gamut remains the upstream home for image
primitives, color, metadata, still-image container parsing, and codecs, and the
upstream-first sequence above is still mandatory there.

## Testing Methodology

Try to unit test the bulk majority of the code but functions that take in external inputs such as image/video file(s) should use test fixtures derived from external sources (which may require human sourcing as prerequisite). Also extend example binaries in `examples/` as necessary to show that each feature actually works.
Expand Down
42 changes: 42 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,52 @@ from the published [gamut](https://github.com/visualcommons/gamut) crates.
dependency is a registry package. 0.x source compatibility is a non-goal (see
`docs/V1_DESIGN.md`).

### Added

#### Video (`rawshift-video`)

`rawshift-video` is no longer a parked placeholder: it ships containers,
tracks, timelines, metadata, and hardware-backed frame decode, and is wired
into the `rawshift` facade behind a new `video` feature. Six crates make it up
— `rawshift-video-core` (the shared vocabulary), `-isobmff`, `-matroska`,
`-symphonia` (an internal bridge), `-hwdec`, and the `rawshift-video` facade.

- **Containers** — MP4, M4V and QuickTime on `mp4-atom` with a rawshift-owned
sample index; Matroska and WebM on `symphonia-format-mkv`. Both read tracks,
timelines, rotation, colour signalling and metadata. AVI and MXF are named
in the API and return a matchable `UnsupportedContainer`.
- **Decode** — HEVC random access points through `rawshift-hwdec`, which
covers keyframe extraction, thumbnails, scrubbing, and All-Intra camera
modes end to end. H.264's seam is final and its backend path is a follow-up.
- **Everything but decode works with no hardware decoder**, on every target
in `docs/SUPPORT.md` including `wasm32` and `musl`. A missing backend
surfaces as `VideoError::HwDecoderUnavailable`, and
`hw_decode_available()` reports capability up front.

No software H.264 or HEVC decoder ships, and none will: `docs/SUPPORT.md`
records why. FFmpeg/libav is excluded as a dependency in any form, on license
and portability grounds.

Video is outside gamut's charter — gamut states it "will not grow video
primitives" — so `AGENTS.md` now carves video out of the Upstream-First
Policy: video containers and codecs take third-party dependencies judged by
the `PRINCIPLES.md` maturity rule instead.

### Changed

All entries below are **breaking**, grouped by area.

#### Video and hardware decode

- `HwCodec` gains `H264`. The enum is documented as deliberately exhaustive,
so this is that decision taken explicitly; matches on it need a new arm.
H.264 is reachable only through the new sequence seam —
`decoder(HwCodec::H264)` returns `None` and `available_codecs()` never lists
it — because no rawshift still format uses H.264.
- `MetadataNamespace` is now `#[non_exhaustive]` and gains `Quicktime` and
`Matroska`. Matches on it need a `_` arm. Both changes land together while
the workspace is pre-1.0, after which new namespaces are additive forever.

#### Package boundaries

- Image formats now live in 17 independently publishable
Expand Down
Loading
Loading