1//! Shared, platform-independent wire protocol for Sigil.2//!3//! Every allocation driven by a peer-controlled length is checked against a4//! protocol limit first. The version is carried both in the ALPN and in each5//! independently decoded header or handshake so mismatched implementations6//! fail closed.78mod audio;9mod error;10mod feedback;11mod framing;12mod handshake;13mod input;14mod invitation;15mod media;16mod media_v3;17mod moq_catalog;1819pub use audio::{AUDIO_HEADER_LEN, AudioCodec, AudioFlags, AudioPacket, AudioPacketHeader};20pub use error::{ProtocolError, Result};21pub use feedback::{22 ADAPTIVE_BITRATE_DECISION_V1_LEN, AdaptiveBitrateDecisionV1, AdaptiveBitrateReasonFlagsV1,23 AdaptiveBitrateStateV1, MEDIA_FEEDBACK_REPORT_V1_LEN, MediaFeedbackFlags,24 MediaFeedbackReportV1, read_adaptive_bitrate_decision_v1, read_media_feedback_report_v1,25 write_adaptive_bitrate_decision_v1, write_media_feedback_report_v1,26};27pub use handshake::{28 Capability, ClientHello, HostHello, MAX_POINTER_SURFACE_HEIGHT, MAX_POINTER_SURFACE_WIDTH,29 MIN_POINTER_SURFACE_HEIGHT, MIN_POINTER_SURFACE_WIDTH, PointerSurfaceDimensions,30 read_client_hello, read_host_hello, write_client_hello, write_host_hello,31};32pub use input::{33 GAMEPAD_AXIS_MAX, GAMEPAD_AXIS_MIN, GAMEPAD_TRIGGER_MAX, GamepadState, InputAck, InputEvent,34 POINTER_POSITION_MAX, POINTER_POSITION_MIN, PointerPosition, RELATIVE_POINTER_DELTA_MAX,35 RELATIVE_POINTER_DELTA_MIN, read_input_ack, read_input_event, write_input_ack,36 write_input_event,37};38pub use invitation::{39 INVITATION_CLOCK_SKEW_SECS, INVITATION_TOKEN_PREFIX, InvitationClaims, InvitationGrants,40 MAX_INVITATION_TOKEN_LEN, MAX_INVITATION_TTL_SECS, SignedInvitation,41};42pub use media::{43 FrameFlags, MEDIA_HEADER_LEN, MediaCodec, MediaFrame, MediaFrameHeader,44 decode_media_frame_object, encode_media_frame_object,45};46pub use media_v3::{47 KeyframeRequestReasonV3, MAX_MEDIA_GROUP_BYTES_V3, MAX_MEDIA_OBJECT_DELIVERY_TIMEOUT_MS,48 MAX_MEDIA_OBJECT_ID_V3, MEDIA_CONTROL_REQUEST_V3_LEN, MEDIA_OBJECT_V3_HEADER_LEN,49 MIN_MEDIA_OBJECT_DELIVERY_TIMEOUT_MS, MediaControlRequestTypeV3, MediaControlRequestV3,50 MediaObjectHeaderV3, MediaObjectV3, read_media_control_request_v3, read_media_object_v3,51 write_media_control_request_v3, write_media_object_v3,52};53pub use moq_catalog::{54 GoqCatalogDocument, MAX_MOQ_CATALOG_BYTES, MOQ_CATALOG_EXTENSION_VERSION_V1,55 MOQ_GOP_GROUP_FORMAT_V1, MOQ_MEDIA_OBJECT_FORMAT_V1, MOQ_VIDEO_TRACK_PRIORITY,56 MoqCatalogExtensionV1, MoqTrackDescriptorV1, MoqVideoCatalogV1,57};5859/// Protocol version encoded in v1 messages.60pub const PROTOCOL_VERSION: u16 = 1;6162/// ALPN for custom MoQ-style grouped media objects and keyframe control.63///64/// This is a Sigil protocol and is not IETF MoQ Transport compatible.65pub const MEDIA_ALPN_V3: &[u8] = b"sigil/media/3";66/// ALPN for bounded receiver telemetry and host bitrate decisions.67pub const MEDIA_FEEDBACK_ALPN_V1: &[u8] = b"sigil/media-feedback/1";68/// ALPN for the v1 latency-independent input stream.69pub const INPUT_ALPN_V1: &[u8] = b"sigil/input/1";70/// ALPN for the v1 session-control stream.71pub const CONTROL_ALPN_V1: &[u8] = b"sigil/control/1";72/// Static upstream MoQ track carrying bounded H.264 access-unit objects.73pub const MOQ_VIDEO_H264_TRACK: &str = "video/h264";74/// ALPN for the v1 low-latency Opus datagram connection.75pub const AUDIO_ALPN_V1: &[u8] = b"sigil/audio/1";7677/// Maximum encoded video access-unit size accepted from the network (16 MiB).78pub const MAX_MEDIA_PAYLOAD_LEN: usize = 16 * 1024 * 1024;79/// Maximum dimension carried by a v1 video header.80pub const MAX_VIDEO_DIMENSION: u16 = 8_192;81/// Maximum pixel count carried by a v1 video header (8K UHD).82pub const MAX_VIDEO_PIXELS: u32 = 7_680 * 4_320;83/// Maximum length-prefixed JSON input message.84pub const MAX_INPUT_MESSAGE_LEN: usize = 4 * 1024;85/// Maximum length-prefixed JSON handshake message.86pub const MAX_HANDSHAKE_MESSAGE_LEN: usize = 16 * 1024;87/// Maximum Opus payload carried in one QUIC datagram.88pub const MAX_AUDIO_PAYLOAD_LEN: usize = 512;89pub const OPUS_SAMPLE_RATE: u32 = 48_000;90pub const OPUS_CHANNELS: u8 = 2;91pub const OPUS_FRAME_SAMPLES: u16 = 960;9293/// Derive the session-scoped upstream MoQ broadcast name advertised by Sigil.94///95/// The authenticated control handshake supplies the non-zero session id, so96/// Portal can derive this application namespace without accepting a97/// peer-controlled path.98pub fn media_moq_broadcast_name(session_id: u64) -> Result<String> {99 if session_id == 0 {100 return Err(ProtocolError::InvalidMessage {101 message_type: "MoQ broadcast name",102 reason: "session id must be non-zero",103 });104 }105 Ok(format!("sigil/session/{session_id}/video"))106}107108109mod tests {110 use super::*;111112 113 fn alpn_values_are_deployment_golden_vectors() {114 assert_eq!(MEDIA_ALPN_V3, b"sigil/media/3");115 assert_eq!(MEDIA_FEEDBACK_ALPN_V1, b"sigil/media-feedback/1");116 assert_eq!(INPUT_ALPN_V1, b"sigil/input/1");117 assert_eq!(CONTROL_ALPN_V1, b"sigil/control/1");118 assert_eq!(AUDIO_ALPN_V1, b"sigil/audio/1");119 }120121 122 fn moq_media_namespace_is_session_scoped_and_stable() {123 assert_eq!(MOQ_VIDEO_H264_TRACK, "video/h264");124 assert_eq!(125 media_moq_broadcast_name(42).unwrap(),126 "sigil/session/42/video"127 );128 assert!(media_moq_broadcast_name(0).is_err());129 }130}