Summary
The decoder offers a PBU-based API set (API set 1: oapvd_info_pbu, oapvd_decode_frame, ...) that walks a bitstream PBU by PBU, but the encoder only offers whole-AU encoding through oapve_encode(). This adds the encoder-side counterpart: the application assembles an access unit itself — raw AU framing first, then frames appended as frame PBUs, then metadata appended as metadata PBUs.
Branch: https://github.com/AcademySoftwareFoundation/openapv/tree/add_enc_pbu_api
Design
Mirroring the decoder API set 1 conventions:
- The application owns the AU framing: it writes the 4-byte au size and the
aPv1 signature itself, exactly as the set-1 decoding application reads them.
- Two new APIs (kept minimal on purpose; the
oapve_pbu_ prefix is provisional and can be renamed before release):
int oapve_pbu_encode_frame(oapve_t eid, oapv_frm_t *ifrm, int frm_idx, oapvm_t mid,
oapv_bitb_t *bitb, oapve_stat_t *stat, oapv_frm_t *rfrm);
int oapve_pbu_encode_metadata(oapve_t eid, oapvm_t mid, int group_id,
oapv_bitb_t *bitb, oapve_stat_t *stat);
oapve_pbu_encode_frame() writes one frame PBU (pbu_size + pbu_header() + frame()) into the caller's bitstream buffer. frm_idx selects the per-frame parameter and rate control state slot, matching the OAPV_CFG_FRM() addressing of oapve_config(). When the frame hash is enabled, the hash payload is collected into mid, so the caller can then emit it with the metadata call — which is what makes the "metadata as separate PBUs" flow natural.
oapve_pbu_encode_metadata() writes the metadata of one group as one metadata PBU and returns OAPV_ERR_NOT_FOUND when the group has no metadata, so the caller can simply try each group.
- No
au_info writer was added since oapve_encode() does not emit access_unit_info PBUs either; it can be added later if a use case appears.
Internally the frame-PBU and metadata-PBU blocks of oapve_encode() were factored into shared helpers (enc_pbu_frame / enc_pbu_metadata), so both API sets run the identical code path.
Application
oapv_app_enc gains --api-set 0|1 like the decoding application. Set 1 assembles each AU with the new APIs (encode_au_apiset1()): framing, frame PBUs per input frame, then one metadata PBU per distinct frame group. The oapve_stat_t it reports is filled compatibly, so the rest of the app (PSNR, reconstruction output, statistics) is unchanged.
Verification
- The refactor is bitstream-neutral: set-0 outputs are byte-identical to current main across CQP, ABR, fastest preset, and hash configurations.
- API set 1 output is byte-identical to API set 0 across 7 configurations x 4 AUs each: plain CQP,
--hash with reconstruction, ABR (--bitrate, exercising rate-control state continuity across AUs), --preset fastest at QP 0, --disable-tile-size-in-fh, a non-default tile geometry, and HDR metadata (--master-display/--max-cll plus color description).
- Profile sweep (422-12, 444-10, 4444-10, 400-10, 422-10-UNCONST): set 0 == set 1, byte-identical.
- A set-1-encoded stream decodes with
hash:match on every frame, with both decoding API sets.
- All 18 ctest cases pass; AddressSanitizer reports no findings on the set-1 path.
Notes for review
- API naming (
oapve_pbu_encode_*) is intentionally provisional.
- Multi-frame AUs are supported by the API design (per-call
frm_idx), while the reference app currently encodes one frame per AU as before.
- The programmers guide gained a "PBU-based encoding (API set 1)" section with the assembly pseudo code.
Summary
The decoder offers a PBU-based API set (API set 1:
oapvd_info_pbu,oapvd_decode_frame, ...) that walks a bitstream PBU by PBU, but the encoder only offers whole-AU encoding throughoapve_encode(). This adds the encoder-side counterpart: the application assembles an access unit itself — raw AU framing first, then frames appended as frame PBUs, then metadata appended as metadata PBUs.Branch: https://github.com/AcademySoftwareFoundation/openapv/tree/add_enc_pbu_api
Design
Mirroring the decoder API set 1 conventions:
aPv1signature itself, exactly as the set-1 decoding application reads them.oapve_pbu_prefix is provisional and can be renamed before release):oapve_pbu_encode_frame()writes one frame PBU (pbu_size+pbu_header()+frame()) into the caller's bitstream buffer.frm_idxselects the per-frame parameter and rate control state slot, matching theOAPV_CFG_FRM()addressing ofoapve_config(). When the frame hash is enabled, the hash payload is collected intomid, so the caller can then emit it with the metadata call — which is what makes the "metadata as separate PBUs" flow natural.oapve_pbu_encode_metadata()writes the metadata of one group as one metadata PBU and returnsOAPV_ERR_NOT_FOUNDwhen the group has no metadata, so the caller can simply try each group.au_infowriter was added sinceoapve_encode()does not emitaccess_unit_infoPBUs either; it can be added later if a use case appears.Internally the frame-PBU and metadata-PBU blocks of
oapve_encode()were factored into shared helpers (enc_pbu_frame/enc_pbu_metadata), so both API sets run the identical code path.Application
oapv_app_encgains--api-set 0|1like the decoding application. Set 1 assembles each AU with the new APIs (encode_au_apiset1()): framing, frame PBUs per input frame, then one metadata PBU per distinct frame group. Theoapve_stat_tit reports is filled compatibly, so the rest of the app (PSNR, reconstruction output, statistics) is unchanged.Verification
--hashwith reconstruction, ABR (--bitrate, exercising rate-control state continuity across AUs),--preset fastestat QP 0,--disable-tile-size-in-fh, a non-default tile geometry, and HDR metadata (--master-display/--max-cllplus color description).hash:matchon every frame, with both decoding API sets.Notes for review
oapve_pbu_encode_*) is intentionally provisional.frm_idx), while the reference app currently encodes one frame per AU as before.