Expand description
SMPTE RDD 29:2019 Dolby Atmos® bitstream — frame/element framing plus bed/object metadata.
This crate implements exactly the wire structures described in the
curated spec transcription at rdd29/docs/rdd29.md (fetched directly
from https://pub.smpte.org/pub/rdd29/rdd29-2019.pdf) — cite that file,
not this doc comment, as the field-semantics oracle.
AtmosFrame— one complete frame: the top-levelATMOSFrameelement (§2.1/§4.2), containing zero or more sub-AnyElements.BedDefinition1— a channel-based audio bed’s channel-to-audio-asset mapping (§2.2/§4.3).ObjectDefinition1— one panned audio object’s per-sub-block pan/ rendering metadata: 3D position, snap, zone gain, spread, decorrelation, plus an optional text description (§2.3/§4.4).AudioDataDlc— one track’s audio essence pointer + opaque payload (§2.4/§4.5).
What this crate is not: an audio codec. AudioDataDlc’s payload is
the Dolby Lossless Coding (DLC) codec’s own bit-packed bitstream (linear-
predictive + Rice-Golomb entropy-coded residual audio samples) — this
crate treats it as opaque bytes, the same “parse the container, not the
codec” discipline this workspace’s transmux/st337 crates use for
media containers and AES3 non-PCM bursts respectively. See
docs/rdd29.md’s “Scope decisions” for the exact boundary and the two
honest gaps in the source disclosure document this crate had to resolve
(the Plex(8) pseudocode’s internal inconsistency, and the completely
undocumented AudioDescription field semantics).
Depends only on broadcast-common. #![no_std] (+ alloc) when the
std feature is disabled.
§Examples
Build a frame with a bed and an audio-essence element, and round-trip it:
use broadcast_common::{Parse, Serialize};
use rdd29::{
AtmosFrame, AudioDataDlc, BedChannel, BedDefinition1, BitDepth, ChannelId, FrameRate,
SampleRate,
};
let bed = BedDefinition1::new(
1,
vec![BedChannel {
channel_id: ChannelId::LeftScreen,
audio_data_id: 10,
}],
);
let dlc = AudioDataDlc::new(10, &[0xDE, 0xAD, 0xBE, 0xEF]).unwrap();
let frame = AtmosFrame::new(
SampleRate::Hz48000,
BitDepth::Bits24,
FrameRate::Fps24,
1,
vec![
rdd29::AnyElement::BedDefinition1(bed),
rdd29::AnyElement::AudioDataDlc(dlc),
],
);
let bytes = frame.to_bytes();
assert_eq!(AtmosFrame::parse(&bytes).unwrap(), frame);§Runnable examples
Run with cargo run -p rdd29 --example <name>.
§build_atmos_frame
//! Build an `ATMOSFrame` from typed bed/object/audio-essence elements, and
//! serialize it to wire bytes — SMPTE RDD 29:2019 §2/§4.
//!
//! Run with `cargo run -p rdd29 --example build_atmos_frame`.
use broadcast_common::Serialize;
use rdd29::{
AnyElement, AtmosFrame, AudioDataDlc, AudioDescription, BedChannel, BedDefinition1, BitDepth,
ChannelId, DecorCoefPrefix, FrameRate, ObjectDefinition1, ObjectSpreadMode, PanInfo,
PanSubBlock, SampleRate,
};
fn main() {
// A 2.0 bed: Left/Right screen speakers, pointing at two AudioDataDLC
// tracks (audio_data_id 10/11).
let bed = BedDefinition1::new(
1,
vec![
BedChannel {
channel_id: ChannelId::LeftScreen,
audio_data_id: 10,
},
BedChannel {
channel_id: ChannelId::RightScreen,
audio_data_id: 11,
},
],
);
// One panned object, centered in the room, snapping to the closest
// speaker. FrameRate::Fps24 requires 8 pan sub-blocks (Table 7); only
// sub-block 0 carries real pan info here, the rest repeat it.
let mut pan_sub_blocks = vec![PanSubBlock {
pan: Some(PanInfo {
pos_x: 0x8000,
pos_y: 0x8000,
pos_z: 0x4000,
snap: true,
zone_gains: None,
spread_mode: ObjectSpreadMode::Lowrez,
spread: 32,
decor_coef_prefix: DecorCoefPrefix::NoDecorrelation,
decor_coef: None,
}),
}];
pan_sub_blocks.resize(8, PanSubBlock { pan: None });
let object = ObjectDefinition1::new(
2,
12,
pan_sub_blocks,
AudioDescription::with_text(b"footsteps").unwrap(),
)
.expect("build ObjectDefinition1");
// Opaque audio-essence payloads -- this crate never decodes DLC audio,
// so any bytes work here (see docs/rdd29.md scope decision 3).
let left = AudioDataDlc::new(10, b"left channel DLC payload").unwrap();
let right = AudioDataDlc::new(11, b"right channel DLC payload").unwrap();
let object_audio = AudioDataDlc::new(12, b"object DLC payload").unwrap();
let frame = AtmosFrame::new(
SampleRate::Hz48000,
BitDepth::Bits24,
FrameRate::Fps24,
3, // MaxRendered: 2 bed channels + 1 object
vec![
AnyElement::BedDefinition1(bed),
AnyElement::ObjectDefinition1(object),
AnyElement::AudioDataDlc(left),
AnyElement::AudioDataDlc(right),
AnyElement::AudioDataDlc(object_audio),
],
);
let bytes = frame.to_bytes();
println!("serialized ATMOSFrame: {} bytes", bytes.len());
println!(
"version={} sample_rate={} bit_depth={} frame_rate={} max_rendered={}",
frame.version, frame.sample_rate, frame.bit_depth, frame.frame_rate, frame.max_rendered
);
println!("sub-elements: {}", frame.elements.len());
}§parse_atmos_frame
//! Wrap the committed real-fixture E-AC-3 frame
//! (`tests/fixtures/eac3_frame0.bin`) as an opaque `AudioDataDLC` payload
//! inside a hand-built `ATMOSFrame`, parse it back, and confirm the payload
//! is byte-identical to the real capture — SMPTE RDD 29:2019 §2.4/§4.5.
//!
//! Run with `cargo run -p rdd29 --example parse_atmos_frame`.
use broadcast_common::{Parse, Serialize};
use rdd29::{
AnyElement, AtmosFrame, AudioDataDlc, BedChannel, BedDefinition1, BitDepth, ChannelId,
FrameRate, SampleRate,
};
fn main() {
// See st337/docs/st337-PROVENANCE.md for how this fixture was extracted
// (a real E-AC-3 syncframe from fixtures/ts/dolby/eac3.ts). RDD 29 never
// decodes audio essence -- this crate treats it as an opaque
// AudioDataDLC payload, exactly like st337 treats its burst_payload.
let path = concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/eac3_frame0.bin"
);
let real_audio = std::fs::read(path).expect("fixture must exist");
let bed = BedDefinition1::new(
1,
vec![BedChannel {
channel_id: ChannelId::LeftScreen,
audio_data_id: 10,
}],
);
let dlc = AudioDataDlc::new(10, &real_audio).expect("build AudioDataDLC");
let frame = AtmosFrame::new(
SampleRate::Hz48000,
BitDepth::Bits24,
FrameRate::Fps24,
1,
vec![
AnyElement::BedDefinition1(bed),
AnyElement::AudioDataDlc(dlc),
],
);
let bytes = frame.to_bytes();
let parsed = AtmosFrame::parse(&bytes).expect("parse ATMOSFrame");
println!("version: {}", parsed.version);
println!("sample_rate: {}", parsed.sample_rate);
println!("frame_rate: {}", parsed.frame_rate);
println!("sub-elements: {}", parsed.elements.len());
let AnyElement::AudioDataDlc(parsed_dlc) = &parsed.elements[1] else {
panic!("expected AudioDataDLC as the second element");
};
assert_eq!(
parsed_dlc.payload, real_audio,
"byte-identical real E-AC-3 payload carried as opaque AudioDataDLC bytes"
);
println!(
"AudioDataDLC payload ({} bytes) matches the real E-AC-3 fixture byte-for-byte.",
parsed_dlc.payload.len()
);
}Modules§
- distance
- Relative distance coding — RDD 29:2019 §3.2.
Structs§
- Atmos
Frame - The
ATMOSFrameelement (§2.1/§4.2/§5.2): the entire Dolby Atmos frame. - Audio
Data Dlc - The
AudioDataDLCelement (§4.5/§5.5): one track’s audio essence, referenced bycrate::BedDefinition1/crate::ObjectDefinition1viaAudioDataID. - Audio
Description AudioDescription— RDD 29 §4.4 (syntax only; no5.4.xprose section documents this field’s semantics — seedocs/rdd29.mdscope decision 2).- BedChannel
- One bed channel: a
ChannelIdpaired with thecrate::AudioDataDlcAudioDataIDit draws audio from (§4.3, thefor(n...)loop body). - BedDefinition1
- The
BedDefinition1element — RDD 29 §2.2/§4.3/§5.3: metadata and pointers to audio essence for one frame of one audio bed. - Object
Definition1 - The
ObjectDefinition1element — RDD 29 §2.3/§4.4/§5.4: pan/rendering metadata (position, snap, zone gain, spread, decorrelation) and a pointer to audio essence, for one frame of one panned audio object. - PanInfo
- One sub-block’s pan/rendering info (present when
PanInfoExists == 1) — RDD 29 §4.4/§5.4.2-§5.4.11. - PanSub
Block - One
NumPanSubBlockssub-block (§4.4’sfor(sb...)loop body).
Enums§
- AnyElement
- Any parsed Dolby Atmos bitstream element — the result of one
ReadElement()dispatch (§4.1). A tag-dispatch enum (see this crate’stests/label_coverage.rsSKIP list), not itself a spec/field label. - BitDepth
- The 2-bit
BitDepthfield (§5.2.3 Table 3). “Only 24-bits per audio sample are currently supported” (spec’s own emphasis). - Channel
Id - The 4-bit
ChannelIDfield (§5.3.3 Table 6): the nominal loudspeaker a bed channel is assigned to. - Decor
Coef Prefix - The 2-bit
ObjectDecorCoefPrefixcode — RDD 29 §5.4.10 Table 11. - Element
Id - The
ElementIDfield (Table 1, §5.1.1): identifies the kind and contents of a Dolby Atmos bitstream element. - Error
- An RDD 29 element/frame parse or serialize error.
- Frame
Rate - The 4-bit
FrameRatefield (§5.2.4 Table 4): the Dolby Atmos frame rate. - Object
Spread Mode - The 2-bit
ObjectSpreadModecode — RDD 29 §5.4.8 Table 10. - Sample
Rate - The 2-bit
SampleRatefield (§5.2.2 Table 2). - Zone
Gain - The 2-bit
ZoneGaincode — RDD 29 §5.4.7 Table 9. - ZoneId
- Zone identifiers — RDD 29 §5.4.5 Table 8. Informative: this is the
array position a
ZoneGainoccupies in wire order, not a value that itself appears on the wire.
Constants§
- ATMOS_
VERSION ATMOSVersionthis crate implements (“This document describes the protocol with ATMOSVersion = 1”, §5.2.1).- ELEMENT_
ID_ ATMOS_ FRAME ATMOS_FRAME(Table 1, §5.1.1): the frame-header element.- ELEMENT_
ID_ AUDIO_ DATA_ DLC AUDIO_DATA_DLC(Table 1).- ELEMENT_
ID_ BED_ DEFINITIO N1 BED_DEFINITION1(Table 1).- ELEMENT_
ID_ OBJECT_ DEFINITIO N1 OBJECT_DEFINITION1(Table 1).- MAX_
KNOWN_ ZONES - Number of known zones (§5.4.5 Table 8 has 9 rows, IDs
0-8). - PLEX_
MAX_ VALUE - Largest value a
Plex-coded symbol may hold (RDD 29 §3.4: “symbols to be Plex encoded shall have a value less than or equal to0xFFFFFFFE”).
Type Aliases§
- Result
- Result alias for
rdd29parsing/serialization.