# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.0.8](https://github.com/OxideAV/oxideav-basic/compare/v0.0.7...v0.0.8) - 2026-05-30
### Other
- parse plst (Playlist) chunk per Microsoft RIFF MCI §3
- parse smpl (Sampler) + inst (Instrument) chunks
- parse cue + LIST adtl per Microsoft RIFF MCI §3
- parse bext Broadcast Audio Extension chunk (EBU Tech 3285)
- fix intra-doc links to WavMuxOptions / WavDemuxer methods
- A-law/μ-law tag 6/7 + WAVEFORMATEXTENSIBLE end-to-end
### Added
- WAV demuxer parses the `plst` (Playlist) chunk per
`docs/container/riff/metadata/microsoft-riffmci.pdf` §3 ("Playlist
Chunk"). The 4-byte `dwSegments` count is followed by N × 12-byte
`<play-segment>` records (`dwName`, `dwLength`, `dwLoops`). Each
segment surfaces under `wav:plst.count` plus per-segment
`wav:plst.<n>.cue_id` / `.length` / `.loops` keys. Unlike the `cue `
chunk which keys by `dwName`, the playlist is keyed by zero-based
segment position because the spec explicitly allows a single cue
id to appear in multiple segments (a cue replayed N times = N
segments with identical `dwName`), so a `dwName`-indexed key would
collide. A `dwSegments` count that exceeds the chunk body is
clamped to the records that actually fit; bodies shorter than the
4-byte segment-count header are treated as opaque and skipped.
- WAV demuxer parses the `smpl` (Sampler) and `inst` (Instrument)
chunks per `docs/container/riff/metadata/exiftool-riff-tags.html` §
"RIFF Sampler Tags" / "RIFF Instrument Tags" and summarised in
`docs/container/riff/metadata/README.md` § "Sampler / Instrument
chunks". `smpl` is a 36-byte fixed header (`Manufacturer`,
`Product`, `SamplePeriod`, `MIDIUnityNote`, `MIDIPitchFraction`,
`SMPTEFormat`, `SMPTEOffset`, `cSampleLoops`, `cbSamplerData`)
followed by N × 24-byte loop records (`dwCuePointID`, `dwType`,
`dwStart`, `dwEnd`, `dwFraction`, `dwPlayCount`). `inst` is a
7-byte fixed struct (`UnshiftedNote`, `FineTune`, `Gain`, `LowNote`,
`HighNote`, `LowVelocity`, `HighVelocity`). Both surface through
`Demuxer::metadata` under `wav:smpl.*` / `wav:inst.*` keys; the
`SMPTEOffset` `DWORD` is rendered as canonical `HH:MM:SS:FF`, and
`FineTune` / `Gain` are decoded as signed `i8` (so `-3` cents shows
as `-3`, not `253`). A `cSampleLoops` count exceeding what the chunk
body actually carries is clamped to the records that fit; bodies
shorter than the 36-byte (`smpl`) or 7-byte (`inst`) fixed header
are treated as opaque and skipped.
- WAV demuxer parses the `cue ` chunk and the `LIST adtl` (Associated
Data List) sub-chunks per
`docs/container/riff/metadata/microsoft-riffmci.pdf` §3
("Cue-Points Chunk" + "Playlist Chunk" + "Associated Data Chunk").
The cue-point table surfaces under `wav:cue.count` plus per-point
`wav:cue.<dwName>.position` / `.fcc_chunk` / `.chunk_start` /
`.block_start` / `.sample_offset` (all values as decimal strings;
`fcc_chunk` rendered as a 4-byte ASCII FOURCC when printable). The
`LIST adtl` body's `labl` / `note` / `ltxt` sub-chunks surface
under `wav:adtl.labl.<dwName>` / `wav:adtl.note.<dwName>` /
`wav:adtl.ltxt.<dwName>.{length,purpose,text}`. `file` sub-chunks
(embedded media files) are skipped — their bytes do not fit the
string-typed metadata API. A `dwCuePoints` count that exceeds the
chunk body is clamped to the records that actually fit so a writer
that lies about the count cannot panic the parser; `labl` / `note`
shorter than the 4-byte `dwName` header and `ltxt` shorter than the
20-byte fixed header are treated as opaque and skipped.
- WAV demuxer parses the `bext` Broadcast Audio Extension chunk per
`docs/container/riff/metadata/ebu-tech3285-bwf.pdf` (EBU Tech 3285 v2
§2.3 `BROADCAST_EXT`). The 602-byte fixed struct plus variable
`CodingHistory` is surfaced through `Demuxer::metadata` under
`wav:bext.description` / `.originator` / `.originator_reference` /
`.origination_date` / `.origination_time` / `.time_reference` (64-bit
sample count reassembled from the low/high `DWORD`s) / `.version` /
`.umid` (hex-encoded SMPTE-330M UMID, v1+ only when non-zero) /
`.coding_history`. For BWF v2 the five loudness `WORD`s are decoded
from the §2.4 `round(100 × value)` fixed-point to two decimals under
`.loudness_value` / `.loudness_range` / `.max_true_peak_level` /
`.max_momentary_loudness` / `.max_short_term_loudness`. Loudness and
UMID keys are omitted for v0/v1 streams that leave those fields zero;
a `bext` chunk shorter than 602 bytes is skipped as opaque.
- WAV demuxer dispatches `WAVE_FORMAT_ALAW (0x0006)` and
`WAVE_FORMAT_MULAW (0x0007)` streams to the `pcm_alaw` / `pcm_mulaw`
codecs (host runtime applies G.711 decode through `oxideav-g711`).
Decoded `SampleFormat` hint surfaces as `S16`; `bit_rate` reflects
the on-wire 8-bit rate (not the post-decode S16 rate).
- WAV `WAVE_FORMAT_EXTENSIBLE (0xFFFE)` end-to-end handling per
`docs/container/riff/waveformatextensible/README.md`: the 22-byte
extension's `wValidBitsPerSample`, `dwChannelMask` and SubFormat
GUID are parsed and exposed both through typed accessors on
`WavDemuxer` (`format_tag`, `valid_bits_per_sample`, `channel_mask`,
`subformat`, `subformat_text`) and through `Demuxer::metadata`
under the keys `wav:fmt.valid_bits_per_sample` /
`wav:fmt.channel_mask` / `wav:fmt.subformat`. Well-known
`KSDATAFORMAT_SUBTYPE_*` GUIDs (PCM, IEEE_FLOAT, ALAW, MULAW)
resolve to the legacy codec ids; unknown GUIDs synthesise a
`wav:guid_<canonical-text>` id so downstream `make_decoder`
failures name the actual GUID rather than the opaque `0xFFFE` tag.
- WAV muxer `open_muxer_with` + `WavMuxOptions::with_extensible(mask)`
emits a 40-byte `WAVEFORMATEXTENSIBLE` `fmt ` chunk with caller-
supplied `dwChannelMask`. `wValidBitsPerSample` defaults to the
container `wBitsPerSample` and SubFormat defaults to the well-known
GUID for the codec; both can be overridden via
`with_valid_bits_per_sample` / `with_subformat`.
- New public constants `WAVE_FORMAT_PCM` / `WAVE_FORMAT_IEEE_FLOAT` /
`WAVE_FORMAT_ALAW` / `WAVE_FORMAT_MULAW` / `WAVE_FORMAT_EXTENSIBLE`
in the `wav` module for muxer callers.
### Fixed
- WAV demuxer rejects `WAVE_FORMAT_EXTENSIBLE` streams whose
`cbSize < 22` (the spec mandates a 22-byte extension; previously
the demuxer silently dropped the extension fields).
- WAV demuxer stamps the on-wire `wFormatTag` onto
`CodecParameters.tag` so consumers can distinguish legacy
`WAVEFORMATEX` from EXTENSIBLE round-trips without reparsing.
## [0.0.7](https://github.com/OxideAV/oxideav-basic/compare/v0.0.6...v0.0.7) - 2026-05-06
### Other
- reframe FFI claim — HW-engine crates use OS FFI by necessity
- drop stale REGISTRARS / with_all_features intra-doc links
- drop dead `linkme` dep
- auto-register via oxideav_core::register! macro (linkme distributed slice)
- unify entry point on register(&mut RuntimeContext) ([#502](https://github.com/OxideAV/oxideav-basic/pull/502))
## [0.0.6](https://github.com/OxideAV/oxideav-basic/compare/v0.0.5...v0.0.6) - 2026-05-03
### Other
- replace never-match regex with semver_check = false
- migrate to centralized OxideAV/.github reusable workflows
- add YUV4MPEG2 demuxer + muxer
- drop unused PcmDecoder.sample_rate field (slim-frame leftover)
- adopt slim AudioFrame shape
- pin release-plz to patch-only bumps
### Added
- Y4M (YUV4MPEG2) raw-video demuxer + muxer (`y4m` module). Handles
4:2:0 / 4:2:2 / 4:4:4 / mono at 8/10/12-bit, preserves header
`X<key>=<val>` extensions in `Demuxer::metadata`, and probes on the
`YUV4MPEG2 ` magic. Frames are emitted as `rawvideo` packets.
## [0.0.5](https://github.com/OxideAV/oxideav-basic/compare/v0.0.4...v0.0.5) - 2026-04-25
### Other
- drop oxideav-codec/oxideav-container shims, import from oxideav-core
## [0.0.4](https://github.com/OxideAV/oxideav-basic/compare/v0.0.3...v0.0.4) - 2026-04-19
### Other
- bump oxideav-container dep to "0.1"
- drop Cargo.lock — this crate is a library
- bump to oxideav-core 0.1.1 + codec 0.1.1
- migrate register() to CodecInfo builder
- bump oxideav-core + oxideav-codec deps to "0.1"
- thread &dyn CodecResolver through open()