Metadata-Version: 2.4
Name: spiral-h264
Version: 0.1.2
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Rust
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
License-File: LICENSE
Summary: GPL H.264 encoder worker for Spiral production video transcoding
Author-email: SpiralDB <hello@spiraldb.com>
License: GPL-2.0-or-later
Requires-Python: >=3.11
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# spiral-h264

`spiral-h264` is the GPL encoder worker for Spiral video transcoding. It uses
the patched x264 fork as a git submodule and exposes a narrow CLI that accepts
raw Y4M frames plus a JSON encode schedule, then emits an Annex B H.264 stream
and JSONL sample metadata.

The user-facing APIs, frame graph selection, ffmpeg orchestration, MP4
muxing/packing, indexing, and Spiral expressions live in `pyspiral` / SpiralDB.
This package should stay limited to the x264 encode boundary.

It deliberately does not demux source containers, run `ffmpeg`, mux MP4, pack
prefix-readable artifacts, or build Spiral indexes. Those steps stay in the
permissive SpiralDB/pyspiral codebase.

## CLI

```bash
spiral-h264 encode-y4m \
  --config encode.json \
  --annex-b out.h264 \
  --metadata samples.jsonl \
  < input.y4m
```

`--input-y4m path` may be used instead of stdin.

The config JSON is intentionally small and mirrors x264 encoder controls plus
the scheduled frame IR:

```json
{
  "frame_ir": "schedule.json",
  "keyint": 120,
  "keyint_min": 120,
  "bframes": 15,
  "crf": 25,
  "preset": "medium",
  "scenecut": 0,
  "b_adapt": 0,
  "b_pyramid": "normal",
  "open_gop": false,
  "rc_lookahead": 40,
  "threads": 4,
  "x264_extra_params": "weightp=0:mbtree=0",
  "x264_layer_qps": "0=22,1=23,2=24,3=30,4=34"
}
```

`samples.jsonl` starts with one stream record, followed by one record per sample:

```jsonl
{"type":"stream","width":1280,"height":720,"fps_num":30,"fps_den":1}
{"type":"sample","sample_index":0,"byte_offset":693,"byte_len":1312,"pts":0,"dts":0,"duration":1,"is_keyframe":true,"picture_type":1,"display_frame":0,"decode_order":0,"temporal_layer":0,"max_ref_layer":0,"frame_id":0}
```

## Development

```bash
git submodule update --init --recursive
cargo test
cargo run -- encode-y4m --help
maturin build --features python
python scripts/check_cargo_licenses.py
```

`ffmpeg` is not bundled. SpiralDB/pyspiral is responsible for invoking system
`ffmpeg` to produce Y4M input.

## Publishing

`Cargo.toml` is the single source of truth for the package version. The Python
package version is dynamic in `pyproject.toml`, and maturin derives it from the
Cargo package metadata.

PyPI publishing uses GitHub Actions Trusted Publishing / OIDC. No PyPI API token
is stored in GitHub. Publishing runs from `.github/workflows/publish.yml` when a
GitHub release is published, or manually through `workflow_dispatch`.

Configure the PyPI project trusted publisher with:

- PyPI project: `spiral-h264`
- Owner: `spiraldb`
- Repository: `spiral-h264`
- Workflow: `publish.yml`
- Environment: `pypi`

Release process:

1. Bump `version` in `Cargo.toml`.
2. Merge the release change after CI is green.
3. Create and publish a GitHub release tagged `vX.Y.Z`, matching the
   `Cargo.toml` version exactly.
4. The publish workflow validates the tag/version match, builds macOS and
   manylinux wheels, builds an sdist that includes the x264 submodule source,
   then publishes with `pypa/gh-action-pypi-publish` using job-level
   `id-token: write`.

Manual `workflow_dispatch` is intended only for dry-run validation of the build
graph; normal PyPI releases should go through a GitHub release tag.

## License

This repository is GPL-2.0-or-later because it statically links the patched x264
source under `third_party/x264`.

