From dfe88fa18d5e0fa6229e4a9ddddd7aa8eb6d5448 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 18 Jun 2026 17:25:15 +0100 Subject: [PATCH] Wrap Muninn media sender payloads with channel --- crates/muninn-daemon/src/media_packetizer.rs | 104 +++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/crates/muninn-daemon/src/media_packetizer.rs b/crates/muninn-daemon/src/media_packetizer.rs index 09eda62..324302c 100644 --- a/crates/muninn-daemon/src/media_packetizer.rs +++ b/crates/muninn-daemon/src/media_packetizer.rs @@ -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, @@ -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, +} + #[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> { + 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 { + encode_audio_packet_wire_record(options, payload).map(wire_payload_to_media_send) +} + +fn wire_payloads_to_media_send(payloads: Vec>) -> Vec { + payloads + .into_iter() + .map(wire_payload_to_media_send) + .collect() +} + +fn wire_payload_to_media_send(payload: Vec) -> MuninnMediaSendPayload { + MuninnMediaSendPayload { + channel_id: MUNINN_MEDIA_RUDP_CHANNEL, + payload, + } +} + pub fn decode_media_wire_record(payload: &[u8]) -> Result { 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(