Skip to content

feat(apv): add APV (Advanced Professional Video) reader and writer - #5367

Draft
zachlewis wants to merge 3 commits into
AcademySoftwareFoundation:mainfrom
zachlewis:zl-apv_imageio
Draft

feat(apv): add APV (Advanced Professional Video) reader and writer#5367
zachlewis wants to merge 3 commits into
AcademySoftwareFoundation:mainfrom
zachlewis:zl-apv_imageio

Conversation

@zachlewis

Copy link
Copy Markdown
Collaborator

Motivation

APV is a royalty-free, all-intra professional video codec (via the BSD-3-Clause OpenAPV library). Because every frame is coded independently, a raw .apv bitstream is naturally addressable as a sequence of individual images — a good fit for OIIO's subimage model, in both directions.

What this does

  • New apv.imageio plugin: the reader exposes one subimage per access unit (decoding its primary frame); the writer emits one access unit per subimage, so appending subimages produces a valid multi-frame bitstream.
  • 10/12-bit YCbCr 4:2:2 / 4:4:4 / 4:4:4:4 (alpha) / 4:0:0 presented as full-range uint16 RGB(A)/grayscale, converted with the matrix coefficients signaled in the bitstream (709 default, 601/2020 honored, limited/full range respected). Writing converts with BT.709.
  • CICP round trip: bitstream color descriptions surface as the CICP attribute plus an oiio:ColorSpace color-interop ID when known (same pattern as the JPEG XL reader); a CICP attribute on output is signaled into the bitstream.
  • Output controls: apv:profile (defaults 422-10 / 4444-10 / 400-10 by channel count), apv:qp, apv:bitrate, apv:preset, FramesPerSecond.
  • FindAPV.cmake (OpenAPV installs libraries into lib/oapv and static consumers must define OAPV_STATIC_DEFINE), a pinned build_APV.cmake local-build recipe, testsuite, and builtinplugins.rst docs.

What it deliberately does not do

Auxiliary AU frames (preview, depth, alpha-as-separate-PBU, non-primary) and metadata payloads (MDCV/CLL) are parsed past but not exposed yet; chroma resampling is simple co-sited linear. All noted in the code as future work.

Notes

  • OpenAPV's lightweight oapvd_info() header probe misparses the color-description fields (reproducible with OpenAPV's own reference encoder; its full decoder parses them correctly). The reader therefore takes color metadata from the decode's frame info. Will report upstream.
  • The local-build recipe benefits from build(cmake): guard libtiff's missing Deflate target #5313 for incremental reconfigures, but the plugin itself is independent of that PR.

Testing

New testsuite/apv: write/read round trips over 422/444/4444/400 profiles at 10 and 12 bits with oiiotool --diff thresholds, multi-subimage append, CICP carry-through (verified 9/16 → pq_rec2020_display interop ID). Also verified against a bitstream produced by OpenAPV's own reference encoder.

Assisted-by: Claude Code (Fable 5)

Add an apv.imageio plugin using the OpenAPV library
(https://github.com/openapv/openapv, BSD-3-Clause). APV is an
all-intra professional video codec, so a raw .apv bitstream is
naturally addressable as a sequence of individual images: the reader
exposes one subimage per access unit (decoding its primary frame),
and the writer emits one access unit per subimage, so appending
subimages produces a valid multi-frame bitstream.

- 10/12-bit YCbCr 4:2:2 / 4:4:4 / 4:4:4:4 (alpha) / 4:0:0 presented
  as full-range uint16 RGB(A)/grayscale, converted with the matrix
  coefficients signaled in the bitstream (709/601/2020).
- CICP round trip: bitstream color descriptions surface as the CICP
  attribute (and oiio:ColorSpace interop ID when known); a CICP
  attribute on output is signaled in the bitstream.
- Output controls: apv:profile (default 422-10 / 4444-10 / 400-10 by
  channel count), apv:qp, apv:bitrate, apv:preset, FramesPerSecond.
- FindAPV.cmake module (OpenAPV installs into lib/oapv and needs
  OAPV_STATIC_DEFINE for static libs), build_APV.cmake local-build
  recipe with pinned tag+commit, testsuite/apv round-trip tests, and
  builtinplugins.rst documentation.

Auxiliary frames within an access unit (preview, depth, alpha,
non-primary) are not yet exposed; chroma resampling is simple
co-sited linear.

Assisted-by: Claude Code (Fable 5)
Signed-off-by: Zach Lewis <zachcanbereached@gmail.com>
Assisted-by: Claude Code (Fable 5)
Signed-off-by: Zach Lewis <zachcanbereached@gmail.com>
Two Color Interop Forum alignment improvements to the writer:

- If no explicit CICP attribute is present but oiio:ColorSpace names a
  color space the default ColorConfig can map to CICP code points
  (e.g. a color interop ID), derive the bitstream color description
  from it via ColorConfig::get_cicp(). Untagged input still writes no
  color description rather than guessing.
- Choose the encode matrix coefficients to match the primaries
  (BT.2020 with the 2020 NCL matrix, BT.601 families with theirs,
  everything else including P3 with BT.709) instead of always BT.709,
  and signal the matrix actually used.

Testsuite gains an interop-ID-only round trip.

Assisted-by: Claude Code (Fable 5)
Signed-off-by: Zach Lewis <zachcanbereached@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant