use crate::bytes::{Reader, Writer, vec_for_count};
use crate::error::{Error, Result};
use crate::message::{self, NAT_FRAMEOFDATA};
use crate::version::{Layout, ParsePolicy, Version};
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RigidBody {
pub id: i32,
pub position: [f32; 3],
pub orientation: [f32; 4],
pub mean_marker_error: f32,
pub params: u16,
}
impl RigidBody {
#[must_use]
pub const fn tracking_valid(&self) -> bool {
self.params & 0x01 != 0
}
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct MarkerSet {
pub name: String,
pub markers: Vec<[f32; 3]>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct LabeledMarker {
pub id: i32,
pub position: [f32; 3],
pub size: f32,
pub params: u16,
pub residual: f32,
}
#[must_use]
pub const fn decode_id(composite_id: i32) -> (i32, i32) {
(composite_id >> 16, composite_id & 0xffff)
}
pub const fn encode_id(entity_id: i32, member_id: i32) -> Result<i32> {
if entity_id < i16::MIN as i32 || entity_id > i16::MAX as i32 {
return Err(Error::InvalidValue {
field: "composite entity id",
reason: "entity id must fit in 16 signed bits",
});
}
if member_id < 0 || member_id > 0xffff {
return Err(Error::InvalidValue {
field: "composite member id",
reason: "member id must fit in 16 unsigned bits",
});
}
Ok((entity_id << 16) | member_id)
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Skeleton {
pub id: i32,
pub rigid_bodies: Vec<RigidBody>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Asset {
pub id: i32,
pub rigid_bodies: Vec<RigidBody>,
pub markers: Vec<LabeledMarker>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ForcePlate {
pub id: i32,
pub channels: Vec<Vec<f32>>,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Device {
pub id: i32,
pub channels: Vec<Vec<f32>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct RawSection {
pub count: i32,
pub payload: Vec<u8>,
}
impl RawSection {
pub fn from_skeletons(skeletons: &[Skeleton]) -> Result<Self> {
encode_raw(
skeletons,
"skeleton count",
"skeleton section size",
|w, skeleton| {
w.i32(skeleton.id);
write_counted(
w,
&skeleton.rigid_bodies,
"skeleton rigid body count",
|w, body| {
write_rigid_body(w, body);
Ok(())
},
)
},
)
}
pub fn from_assets(assets: &[Asset]) -> Result<Self> {
encode_raw(assets, "asset count", "asset section size", |w, asset| {
w.i32(asset.id);
write_counted(
w,
&asset.rigid_bodies,
"asset rigid body count",
|w, body| {
write_rigid_body(w, body);
Ok(())
},
)?;
write_counted(w, &asset.markers, "asset marker count", |w, marker| {
write_labeled_marker(w, marker);
Ok(())
})
})
}
pub fn from_force_plates(force_plates: &[ForcePlate]) -> Result<Self> {
encode_raw(
force_plates,
"force plate count",
"force plate section size",
|w, plate| {
w.i32(plate.id);
write_channels(
w,
&plate.channels,
"force plate channel count",
"force plate sample count",
)
},
)
}
pub fn from_devices(devices: &[Device]) -> Result<Self> {
encode_raw(
devices,
"device count",
"device section size",
|w, device| {
w.i32(device.id);
write_channels(
w,
&device.channels,
"device channel count",
"device sample count",
)
},
)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FrameTiming {
pub timecode: u32,
pub timecode_subframe: u32,
pub timestamp: f64,
pub camera_mid_exposure: u64,
pub camera_data_received: u64,
pub transmit: u64,
pub precision_timestamp_secs: u32,
pub precision_timestamp_fractional_secs: u32,
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct FrameOfData {
pub frame_number: i32,
pub marker_sets: Vec<MarkerSet>,
pub other_markers: Vec<[f32; 3]>,
pub rigid_bodies: Vec<RigidBody>,
pub skeletons: RawSection,
pub assets: RawSection,
pub labeled_markers: Vec<LabeledMarker>,
pub force_plates: RawSection,
pub devices: RawSection,
pub timing: FrameTiming,
pub params: u16,
}
fn typed_count(r: &mut Reader<'_>) -> Result<i32> {
let offset = r.position();
let count = r.i32()?;
if count < 0 {
return Err(Error::InvalidCount { count, offset });
}
Ok(count)
}
fn count_len(r: &Reader<'_>, count: i32, min_entry: usize) -> Result<usize> {
let Ok(count) = usize::try_from(count) else {
return Err(Error::InvalidCount {
count,
offset: r.position(),
});
};
if count > r.remaining().checked_div(min_entry).unwrap_or(0) {
return Err(Error::ImplausibleCount {
count,
available: r.remaining(),
});
}
Ok(count)
}
fn sized_header(r: &mut Reader<'_>, policy: ParsePolicy, raw: bool) -> Result<(i32, usize)> {
let count_offset = r.position();
let count = r.i32()?;
if count < 0 && (!raw || matches!(policy, ParsePolicy::Strict)) {
return Err(Error::InvalidCount {
count,
offset: count_offset,
});
}
let size_offset = r.position();
let raw_size = r.i32()?;
let Ok(size) = usize::try_from(raw_size) else {
return Err(Error::InvalidCount {
count: raw_size,
offset: size_offset,
});
};
if size > r.remaining() {
return Err(Error::UnexpectedEof {
offset: r.position(),
needed: size,
available: r.remaining(),
});
}
Ok((count, size))
}
fn sized_typed_section<T>(
r: &mut Reader<'_>,
min_entry: usize,
policy: ParsePolicy,
mut entry: impl FnMut(&mut Reader<'_>) -> Result<T>,
) -> Result<Vec<T>> {
let (count, size) = sized_header(r, policy, false)?;
let mut body = r.subreader(size)?;
let count = count_len(&body, count, min_entry)?;
let mut out = vec_for_count(count);
for _ in 0..count {
out.push(entry(&mut body)?);
}
let consumed = size - body.remaining();
if body.remaining() != 0 {
return Err(Error::SectionSizeMismatch {
declared: size,
consumed,
});
}
Ok(out)
}
fn legacy_typed_section<T>(
r: &mut Reader<'_>,
min_entry: usize,
mut entry: impl FnMut(&mut Reader<'_>) -> Result<T>,
) -> Result<Vec<T>> {
let count = typed_count(r)?;
let count = count_len(r, count, min_entry)?;
let mut out = vec_for_count(count);
for _ in 0..count {
out.push(entry(r)?);
}
Ok(out)
}
#[derive(Clone, Copy)]
enum RawKind {
Skeleton,
Asset,
ForcePlate,
Device,
}
const RIGID_BODY_BYTES: usize = 38;
const LABELED_MARKER_BYTES: usize = 26;
const MIN_RAW_ENTRY_BYTES: usize = 8;
fn read_marker_set(r: &mut Reader<'_>) -> Result<MarkerSet> {
let name = r.cstr()?;
let n = typed_count(r)?;
let n = count_len(r, n, 12)?;
let mut markers = vec_for_count(n);
for _ in 0..n {
markers.push(r.vec3()?);
}
Ok(MarkerSet { name, markers })
}
fn read_rigid_body(r: &mut Reader<'_>) -> Result<RigidBody> {
Ok(RigidBody {
id: r.i32()?,
position: r.vec3()?,
orientation: r.quat()?,
mean_marker_error: r.f32()?,
params: r.u16()?,
})
}
fn write_rigid_body(w: &mut Writer, body: &RigidBody) {
w.i32(body.id);
w.vec3(body.position);
w.quat(body.orientation);
w.f32(body.mean_marker_error);
w.u16(body.params);
}
fn read_labeled_marker(r: &mut Reader<'_>) -> Result<LabeledMarker> {
Ok(LabeledMarker {
id: r.i32()?,
position: r.vec3()?,
size: r.f32()?,
params: r.u16()?,
residual: r.f32()?,
})
}
fn write_labeled_marker(w: &mut Writer, marker: &LabeledMarker) {
w.i32(marker.id);
w.vec3(marker.position);
w.f32(marker.size);
w.u16(marker.params);
w.f32(marker.residual);
}
fn read_entries<T>(
r: &mut Reader<'_>,
count: i32,
min_entry: usize,
mut entry: impl FnMut(&mut Reader<'_>) -> Result<T>,
) -> Result<Vec<T>> {
let count = count_len(r, count, min_entry)?;
let mut out = vec_for_count(count);
for _ in 0..count {
out.push(entry(r)?);
}
Ok(out)
}
fn read_counted<T>(
r: &mut Reader<'_>,
min_entry: usize,
entry: impl FnMut(&mut Reader<'_>) -> Result<T>,
) -> Result<Vec<T>> {
let count = typed_count(r)?;
read_entries(r, count, min_entry, entry)
}
fn write_counted<T>(
w: &mut Writer,
items: &[T],
count_field: &'static str,
mut entry: impl FnMut(&mut Writer, &T) -> Result<()>,
) -> Result<()> {
w.i32(crate::bytes::checked_i32(items.len(), count_field)?);
for item in items {
entry(w, item)?;
}
Ok(())
}
fn read_skeleton(r: &mut Reader<'_>) -> Result<Skeleton> {
let id = r.i32()?;
let rigid_bodies = read_counted(r, RIGID_BODY_BYTES, read_rigid_body)?;
Ok(Skeleton { id, rigid_bodies })
}
fn read_asset(r: &mut Reader<'_>) -> Result<Asset> {
let id = r.i32()?;
let rigid_bodies = read_counted(r, RIGID_BODY_BYTES, read_rigid_body)?;
let markers = read_counted(r, LABELED_MARKER_BYTES, read_labeled_marker)?;
Ok(Asset {
id,
rigid_bodies,
markers,
})
}
fn read_channels(r: &mut Reader<'_>) -> Result<Vec<Vec<f32>>> {
read_counted(r, 4, |r| read_counted(r, 4, |r| r.f32()))
}
fn write_channels(
w: &mut Writer,
channels: &[Vec<f32>],
count_field: &'static str,
sample_field: &'static str,
) -> Result<()> {
write_counted(w, channels, count_field, |w, samples| {
write_counted(w, samples, sample_field, |w, sample| {
w.f32(*sample);
Ok(())
})
})
}
fn read_force_plate(r: &mut Reader<'_>) -> Result<ForcePlate> {
Ok(ForcePlate {
id: r.i32()?,
channels: read_channels(r)?,
})
}
fn read_device(r: &mut Reader<'_>) -> Result<Device> {
Ok(Device {
id: r.i32()?,
channels: read_channels(r)?,
})
}
fn walk_raw_body(r: &mut Reader<'_>, count: i32, kind: RawKind) -> Result<()> {
match kind {
RawKind::Skeleton => {
read_entries(r, count, MIN_RAW_ENTRY_BYTES, read_skeleton)?;
}
RawKind::Asset => {
read_entries(r, count, MIN_RAW_ENTRY_BYTES, read_asset)?;
}
RawKind::ForcePlate => {
read_entries(r, count, MIN_RAW_ENTRY_BYTES, read_force_plate)?;
}
RawKind::Device => {
read_entries(r, count, MIN_RAW_ENTRY_BYTES, read_device)?;
}
}
Ok(())
}
fn decode_raw<T>(
section: &RawSection,
entry: impl FnMut(&mut Reader<'_>) -> Result<T>,
) -> Result<Vec<T>> {
let mut body = Reader::new(§ion.payload);
let declared = body.remaining();
let out = read_entries(&mut body, section.count, MIN_RAW_ENTRY_BYTES, entry)?;
if body.remaining() != 0 {
let consumed = declared - body.remaining();
return Err(Error::SectionSizeMismatch { declared, consumed });
}
Ok(out)
}
fn encode_raw<T>(
items: &[T],
count_field: &'static str,
size_field: &'static str,
mut entry: impl FnMut(&mut Writer, &T) -> Result<()>,
) -> Result<RawSection> {
let count = crate::bytes::checked_i32(items.len(), count_field)?;
let mut w = Writer::new();
for item in items {
entry(&mut w, item)?;
}
let payload = w.into_vec();
crate::bytes::checked_i32(payload.len(), size_field)?;
Ok(RawSection { count, payload })
}
fn validate_raw_body(body: &mut Reader<'_>, count: i32, kind: RawKind) -> Result<()> {
let declared = body.remaining();
walk_raw_body(body, count, kind)?;
if body.remaining() != 0 {
let consumed = declared - body.remaining();
return Err(Error::SectionSizeMismatch { declared, consumed });
}
Ok(())
}
fn legacy_raw_section(r: &mut Reader<'_>, kind: RawKind) -> Result<RawSection> {
let count = typed_count(r)?;
let body_start = r.position();
walk_raw_body(r, count, kind)?;
let body_end = r.position();
Ok(RawSection {
count,
payload: r.span(body_start, body_end)?.to_vec(),
})
}
fn sized_raw_section(r: &mut Reader<'_>, kind: RawKind, policy: ParsePolicy) -> Result<RawSection> {
let (count, size) = sized_header(r, policy, true)?;
let body_start = r.position();
let mut body = r.subreader(size)?;
if matches!(policy, ParsePolicy::Strict) {
validate_raw_body(&mut body, count, kind)?;
}
let body_end = r.position();
Ok(RawSection {
count,
payload: r.span(body_start, body_end)?.to_vec(),
})
}
fn validate_legacy_raw(s: &RawSection, kind: RawKind) -> Result<()> {
if s.count < 0 {
return Err(Error::InvalidValue {
field: "raw section count",
reason: "count must be non-negative",
});
}
let mut body = Reader::new(&s.payload);
validate_raw_body(&mut body, s.count, kind)
}
fn checked_typed_section<T>(
w: &mut Writer,
items: &[T],
sized: bool,
count_field: &'static str,
size_field: &'static str,
mut entry: impl FnMut(&mut Writer, &T) -> Result<()>,
) -> Result<()> {
let count = crate::bytes::checked_i32(items.len(), count_field)?;
w.i32(count);
let size_at = w.len();
if sized {
w.i32(0);
}
let body_start = w.len();
for item in items {
entry(w, item)?;
}
if sized {
let size = crate::bytes::checked_i32(w.len() - body_start, size_field)?;
w.patch_i32(size_at, size);
}
Ok(())
}
fn lossy_typed_section<T>(
w: &mut Writer,
items: &[T],
sized: bool,
mut entry: impl FnMut(&mut Writer, &T),
) {
w.i32(i32::try_from(items.len()).unwrap_or(i32::MAX));
let size_at = w.len();
if sized {
w.i32(0);
}
let body_start = w.len();
for item in items {
entry(w, item);
}
if sized {
w.patch_i32(
size_at,
i32::try_from(w.len() - body_start).unwrap_or(i32::MAX),
);
}
}
fn checked_raw_section(
w: &mut Writer,
s: &RawSection,
sized: bool,
count_field: &'static str,
size_field: &'static str,
) -> Result<()> {
let payload_len = crate::bytes::checked_i32(s.payload.len(), size_field)?;
if s.count < 0 {
return Err(Error::InvalidValue {
field: count_field,
reason: "count must be non-negative",
});
}
w.i32(s.count);
if sized {
w.i32(payload_len);
}
w.bytes(&s.payload);
Ok(())
}
fn lossy_raw_section(w: &mut Writer, s: &RawSection, sized: bool) {
w.i32(s.count);
if sized {
w.i32(i32::try_from(s.payload.len()).unwrap_or(i32::MAX));
}
w.bytes(&s.payload);
}
impl FrameOfData {
#[must_use]
pub const fn bitstream_changed(&self) -> bool {
self.params & 0x08 != 0
}
pub fn decode_skeletons(&self) -> Result<Vec<Skeleton>> {
decode_raw(&self.skeletons, read_skeleton)
}
pub fn decode_assets(&self) -> Result<Vec<Asset>> {
decode_raw(&self.assets, read_asset)
}
pub fn decode_force_plates(&self) -> Result<Vec<ForcePlate>> {
decode_raw(&self.force_plates, read_force_plate)
}
pub fn decode_devices(&self) -> Result<Vec<Device>> {
decode_raw(&self.devices, read_device)
}
pub fn parse(datagram: &[u8], version: Version) -> Result<Self> {
Self::parse_with_policy(datagram, version, ParsePolicy::Compatible)
}
pub fn parse_with_policy(
datagram: &[u8],
version: Version,
policy: ParsePolicy,
) -> Result<Self> {
version.layout()?;
let payload = message::expect(datagram, NAT_FRAMEOFDATA)?;
Self::parse_payload_with_policy(payload, version, policy)
}
pub fn parse_payload(payload: &[u8], version: Version) -> Result<Self> {
Self::parse_payload_with_policy(payload, version, ParsePolicy::Compatible)
}
#[allow(
clippy::too_many_lines,
reason = "wire fields must remain in protocol order"
)]
pub fn parse_payload_with_policy(
payload: &[u8],
version: Version,
policy: ParsePolicy,
) -> Result<Self> {
let layout = version.layout()?;
let mut r = Reader::new(payload);
let frame_number = r.i32()?;
let marker_sets = if layout.has_section_sizes() {
sized_typed_section(&mut r, 5, policy, read_marker_set)?
} else {
legacy_typed_section(&mut r, 5, read_marker_set)?
};
let other_markers = if layout.has_section_sizes() {
sized_typed_section(&mut r, 12, policy, |r| r.vec3())?
} else {
legacy_typed_section(&mut r, 12, |r| r.vec3())?
};
let rigid_bodies = if layout.has_section_sizes() {
sized_typed_section(&mut r, RIGID_BODY_BYTES, policy, read_rigid_body)?
} else {
legacy_typed_section(&mut r, RIGID_BODY_BYTES, read_rigid_body)?
};
let skeletons = if layout.has_section_sizes() {
sized_raw_section(&mut r, RawKind::Skeleton, policy)?
} else {
legacy_raw_section(&mut r, RawKind::Skeleton)?
};
let assets = if layout.has_assets() {
sized_raw_section(&mut r, RawKind::Asset, policy)?
} else {
RawSection {
count: 0,
payload: Vec::new(),
}
};
let labeled_markers = if layout.has_section_sizes() {
sized_typed_section(&mut r, LABELED_MARKER_BYTES, policy, read_labeled_marker)?
} else {
legacy_typed_section(&mut r, LABELED_MARKER_BYTES, read_labeled_marker)?
};
let force_plates = if layout.has_section_sizes() {
sized_raw_section(&mut r, RawKind::ForcePlate, policy)?
} else {
legacy_raw_section(&mut r, RawKind::ForcePlate)?
};
let devices = if layout.has_section_sizes() {
sized_raw_section(&mut r, RawKind::Device, policy)?
} else {
legacy_raw_section(&mut r, RawKind::Device)?
};
let timecode = r.u32()?;
let timecode_subframe = r.u32()?;
let timestamp = r.f64()?;
let camera_mid_exposure = r.u64()?;
let camera_data_received = r.u64()?;
let transmit = r.u64()?;
let (precision_timestamp_secs, precision_timestamp_fractional_secs) =
if layout.has_precision() {
(r.u32()?, r.u32()?)
} else {
(0, 0)
};
let params = r.u16()?;
let end_offset = r.position();
let end_of_data = r.i32()?;
if matches!(policy, ParsePolicy::Strict) && end_of_data != 0 {
return Err(Error::InvalidTerminator {
value: end_of_data,
offset: end_offset,
});
}
if matches!(policy, ParsePolicy::Strict) {
r.finish()?;
}
Ok(Self {
frame_number,
marker_sets,
other_markers,
rigid_bodies,
skeletons,
assets,
labeled_markers,
force_plates,
devices,
timing: FrameTiming {
timecode,
timecode_subframe,
timestamp,
camera_mid_exposure,
camera_data_received,
transmit,
precision_timestamp_secs,
precision_timestamp_fractional_secs,
},
params,
})
}
pub fn to_payload(&self, version: Version) -> Result<Vec<u8>> {
let layout = version.layout()?;
self.validate_for_layout(layout)?;
self.encode_payload_checked(layout)
}
pub fn to_payload_lossy(&self, version: Version) -> Result<Vec<u8>> {
Ok(self.encode_payload_lossy(version.layout()?))
}
pub fn to_datagram(&self, version: Version) -> Result<Vec<u8>> {
let payload = self.to_payload(version)?;
message::frame_checked(NAT_FRAMEOFDATA, &payload)
}
pub fn to_datagram_lossy(&self, version: Version) -> Result<Vec<u8>> {
let payload = self.to_payload_lossy(version)?;
Ok(message::frame(NAT_FRAMEOFDATA, &payload))
}
fn validate_for_layout(&self, layout: Layout) -> Result<()> {
if !layout.has_assets() && (self.assets.count != 0 || !self.assets.payload.is_empty()) {
return Err(Error::InvalidValue {
field: "assets",
reason: "assets are not present in this protocol layout",
});
}
if !layout.has_precision()
&& (self.timing.precision_timestamp_secs != 0
|| self.timing.precision_timestamp_fractional_secs != 0)
{
return Err(Error::InvalidValue {
field: "precision timestamp",
reason: "precision timestamp is not present in this protocol layout",
});
}
validate_legacy_raw(&self.skeletons, RawKind::Skeleton)?;
validate_legacy_raw(&self.force_plates, RawKind::ForcePlate)?;
validate_legacy_raw(&self.devices, RawKind::Device)?;
if layout.has_assets() {
validate_legacy_raw(&self.assets, RawKind::Asset)?;
}
Ok(())
}
#[allow(
clippy::too_many_lines,
reason = "wire fields must remain in protocol order"
)]
fn encode_payload_checked(&self, layout: Layout) -> Result<Vec<u8>> {
let sized = layout.has_section_sizes();
let mut w = Writer::new();
w.i32(self.frame_number);
checked_typed_section(
&mut w,
&self.marker_sets,
sized,
"marker set count",
"marker set section size",
|w, ms| {
w.try_cstr(&ms.name, "marker set name")?;
w.i32(crate::bytes::checked_i32(ms.markers.len(), "marker count")?);
for marker in &ms.markers {
w.vec3(*marker);
}
Ok(())
},
)?;
checked_typed_section(
&mut w,
&self.other_markers,
sized,
"other marker count",
"other marker section size",
|w, marker| {
w.vec3(*marker);
Ok(())
},
)?;
checked_typed_section(
&mut w,
&self.rigid_bodies,
sized,
"rigid body count",
"rigid body section size",
|w, body| {
write_rigid_body(w, body);
Ok(())
},
)?;
checked_raw_section(
&mut w,
&self.skeletons,
sized,
"skeleton count",
"skeleton section size",
)?;
if layout.has_assets() {
checked_raw_section(
&mut w,
&self.assets,
sized,
"asset count",
"asset section size",
)?;
}
checked_typed_section(
&mut w,
&self.labeled_markers,
sized,
"labeled marker count",
"labeled marker section size",
|w, marker| {
write_labeled_marker(w, marker);
Ok(())
},
)?;
checked_raw_section(
&mut w,
&self.force_plates,
sized,
"force plate count",
"force plate section size",
)?;
checked_raw_section(
&mut w,
&self.devices,
sized,
"device count",
"device section size",
)?;
w.u32(self.timing.timecode);
w.u32(self.timing.timecode_subframe);
w.f64(self.timing.timestamp);
w.u64(self.timing.camera_mid_exposure);
w.u64(self.timing.camera_data_received);
w.u64(self.timing.transmit);
if layout.has_precision() {
w.u32(self.timing.precision_timestamp_secs);
w.u32(self.timing.precision_timestamp_fractional_secs);
}
w.u16(self.params);
w.i32(0);
Ok(w.into_vec())
}
fn encode_payload_lossy(&self, layout: Layout) -> Vec<u8> {
let sized = layout.has_section_sizes();
let mut w = Writer::new();
w.i32(self.frame_number);
lossy_typed_section(&mut w, &self.marker_sets, sized, |w, ms| {
w.cstr(&ms.name);
w.i32(i32::try_from(ms.markers.len()).unwrap_or(i32::MAX));
for marker in &ms.markers {
w.vec3(*marker);
}
});
lossy_typed_section(&mut w, &self.other_markers, sized, |w, marker| {
w.vec3(*marker);
});
lossy_typed_section(&mut w, &self.rigid_bodies, sized, write_rigid_body);
lossy_raw_section(&mut w, &self.skeletons, sized);
if layout.has_assets() {
lossy_raw_section(&mut w, &self.assets, sized);
}
lossy_typed_section(&mut w, &self.labeled_markers, sized, write_labeled_marker);
lossy_raw_section(&mut w, &self.force_plates, sized);
lossy_raw_section(&mut w, &self.devices, sized);
w.u32(self.timing.timecode);
w.u32(self.timing.timecode_subframe);
w.f64(self.timing.timestamp);
w.u64(self.timing.camera_mid_exposure);
w.u64(self.timing.camera_data_received);
w.u64(self.timing.transmit);
if layout.has_precision() {
w.u32(self.timing.precision_timestamp_secs);
w.u32(self.timing.precision_timestamp_fractional_secs);
}
w.u16(self.params);
w.i32(0);
w.into_vec()
}
}