Wrap Muninn media sender payloads with channel

This commit is contained in:
Meta Vens 2026-06-18 17:25:15 +01:00
commit dfe88fa18d

View file

@ -11,6 +11,8 @@ use odin_core::{
use serde::{Serialize, de::DeserializeOwned};
use std::collections::BTreeMap;
pub const MUNINN_MEDIA_RUDP_CHANNEL: &str = "media";
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VideoAccessUnit {
pub bytes: Vec<u8>,
@ -218,6 +220,12 @@ pub enum MuninnMediaWireRecord {
Feedback(MuninnMediaReceiverFeedbackRecord),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MuninnMediaSendPayload {
pub channel_id: &'static str,
pub payload: Vec<u8>,
}
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct VideoChunkKey {
pub frame_id: u64,
@ -1031,6 +1039,13 @@ pub fn encode_video_annex_b_stream_wire_records(
)
}
pub fn video_annex_b_stream_send_payloads(
options: VideoAnnexBStreamWireOptions<'_>,
input: &[u8],
) -> Result<Vec<MuninnMediaSendPayload>> {
encode_video_annex_b_stream_wire_records(options, input).map(wire_payloads_to_media_send)
}
pub fn encode_audio_packet_wire_record(
options: AudioPacketWireOptions<'_>,
payload: &[u8],
@ -1044,6 +1059,27 @@ pub fn encode_audio_packet_wire_record(
)
}
pub fn audio_packet_send_payload(
options: AudioPacketWireOptions<'_>,
payload: &[u8],
) -> Result<MuninnMediaSendPayload> {
encode_audio_packet_wire_record(options, payload).map(wire_payload_to_media_send)
}
fn wire_payloads_to_media_send(payloads: Vec<Vec<u8>>) -> Vec<MuninnMediaSendPayload> {
payloads
.into_iter()
.map(wire_payload_to_media_send)
.collect()
}
fn wire_payload_to_media_send(payload: Vec<u8>) -> MuninnMediaSendPayload {
MuninnMediaSendPayload {
channel_id: MUNINN_MEDIA_RUDP_CHANNEL,
payload,
}
}
pub fn decode_media_wire_record(payload: &[u8]) -> Result<MuninnMediaWireRecord> {
let message = decode_cultnet_message_from_slice(payload, CultNetWireContract::CultNetSchemaV0)?;
let CultNetMessage::DocumentPutRaw { document, .. } = message else {
@ -2494,6 +2530,43 @@ mod tests {
Ok(())
}
#[test]
fn media_send_payloads_pin_video_to_media_channel() -> Result<()> {
let mut stream = Vec::new();
stream.extend_from_slice(&start_code());
stream.extend_from_slice(&[0x65, 0x80]);
let payloads = video_annex_b_stream_send_payloads(
VideoAnnexBStreamWireOptions {
packetize: VideoAnnexBStreamPacketizeOptions {
stream_id: "muninn.raven.av.rudp",
session_id: "session-1",
codec: "h264",
first_frame_id: 9,
first_pts_ticks: 27_000,
frame_duration_ticks: 3_000,
timebase_num: 1,
timebase_den: 90_000,
deadline_delay_ticks: 1_800,
max_payload_bytes: 16,
},
stored_at: "2026-06-18T00:00:00Z",
source_runtime_id: "muninn-test",
source_role: "media-test",
},
&stream,
)?;
assert_eq!(payloads.len(), 1);
assert_eq!(payloads[0].channel_id, MUNINN_MEDIA_RUDP_CHANNEL);
let MuninnMediaWireRecord::Video(record) = decode_media_wire_record(&payloads[0].payload)?
else {
panic!("expected video media record");
};
assert_eq!(record.frame_id, 9);
Ok(())
}
#[test]
fn media_wire_encodes_audio_packet_for_sender() -> Result<()> {
let wire = encode_audio_packet_wire_record(
@ -2526,6 +2599,37 @@ mod tests {
Ok(())
}
#[test]
fn media_send_payload_pins_audio_to_media_channel() -> Result<()> {
let payload = audio_packet_send_payload(
AudioPacketWireOptions {
packetize: AudioPacketizeOptions {
stream_id: "muninn.raven.av.rudp",
session_id: "session-1",
codec: "opus",
packet_id: 12,
pts_ticks: 48_000,
duration_ticks: 960,
timebase_num: 1,
timebase_den: 48_000,
deadline_ticks: 48_960,
},
stored_at: "2026-06-18T00:00:00Z",
source_runtime_id: "muninn-test",
source_role: "media-test",
},
&[0xf8, 0xff, 0xfe],
)?;
assert_eq!(payload.channel_id, MUNINN_MEDIA_RUDP_CHANNEL);
let MuninnMediaWireRecord::Audio(record) = decode_media_wire_record(&payload.payload)?
else {
panic!("expected audio media record");
};
assert_eq!(record.packet_id, 12);
Ok(())
}
#[test]
fn media_wire_round_trips_audio_document() -> Result<()> {
let record = packetize_audio_packet(