cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/drop-hero-notarization-note
goq / crates / sigil-protocol / src / lib.rs
lib.rs 8.3K
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 control_v2;10mod error;11mod feedback;12mod framing;13mod handshake;14mod input;15mod input_v2;16mod invitation;17mod media;18mod media_auth;19mod media_v3;20mod moq_catalog;21mod subscription;2223pub use audio::{AUDIO_HEADER_LEN, AudioCodec, AudioFlags, AudioPacket, AudioPacketHeader};24pub use control_v2::{25 ClientControlEnvelopeV2, ClientHelloV2, ControlKeyframeReasonV2, FocusCommandActionV2,26 FocusCommandResultV2, FocusCommandV2, FocusProposalV2, FocusStateV2, FocusTransitionReasonV2,27 HostHelloV2, MAX_CONTROL_V2_MESSAGE_LEN, MAX_SESSION_ROSTER_VIEWERS,28 MediaGenerationDescriptorV2, ServerControlEnvelopeV2, SessionSnapshotV2, ViewerPresenceId,29 ViewerPresenceV2, read_client_control_v2, read_client_hello_v2, read_host_hello_v2,30 read_server_control_v2, write_client_control_v2, write_client_hello_v2, write_host_hello_v2,31 write_server_control_v2,32};33pub use error::{ProtocolError, Result};34pub use feedback::{35 ADAPTIVE_BITRATE_DECISION_V1_LEN, AdaptiveBitrateDecisionV1, AdaptiveBitrateReasonFlagsV1,36 AdaptiveBitrateStateV1, MEDIA_FEEDBACK_REPORT_V1_LEN, MediaFeedbackFlags,37 MediaFeedbackReportV1, read_adaptive_bitrate_decision_v1, read_media_feedback_report_v1,38 write_adaptive_bitrate_decision_v1, write_media_feedback_report_v1,39};40pub use handshake::{41 Capability, ClientHello, HostHello, MAX_POINTER_SURFACE_HEIGHT, MAX_POINTER_SURFACE_WIDTH,42 MIN_POINTER_SURFACE_HEIGHT, MIN_POINTER_SURFACE_WIDTH, PointerSurfaceDimensions,43 read_client_hello, read_host_hello, write_client_hello, write_host_hello,44};45pub use input::{46 GAMEPAD_AXIS_MAX, GAMEPAD_AXIS_MIN, GAMEPAD_TRIGGER_MAX, GamepadState, InputAck, InputEvent,47 POINTER_POSITION_MAX, POINTER_POSITION_MIN, PointerPosition, RELATIVE_POINTER_DELTA_MAX,48 RELATIVE_POINTER_DELTA_MIN, read_input_ack, read_input_event, write_input_ack,49 write_input_event,50};51pub use input_v2::{52 ControllerSlot, InputAckV2, InputClientHelloV2, InputEventV2, InputHostHelloV2,53 read_input_ack_v2, read_input_client_hello_v2, read_input_event_v2, read_input_host_hello_v2,54 write_input_ack_v2, write_input_client_hello_v2, write_input_event_v2,55 write_input_host_hello_v2,56};57pub use invitation::{58 INVITATION_CLOCK_SKEW_SECS, INVITATION_TOKEN_PREFIX, InvitationClaims, InvitationGrants,59 MAX_INVITATION_TOKEN_LEN, MAX_INVITATION_TTL_SECS, SignedInvitation,60};61pub use media::{62 FrameFlags, MEDIA_HEADER_LEN, MediaCodec, MediaFrame, MediaFrameHeader,63 decode_media_frame_object, encode_media_frame_object,64};65pub use media_auth::{66 AUTHENTICATED_MEDIA_OBJECT_HEADER_LEN, AuthenticatedMediaObject,67 MAX_MEDIA_GENERATION_CERTIFICATE_TOKEN_LEN, MEDIA_GENERATION_CERTIFICATE_TOKEN_PREFIX,68 MediaAuthMode, MediaGenerationCertificateClaims, MediaGenerationSigningKey,69 MediaObjectCoordinates, MediaTrack, SignedMediaGenerationCertificate,70};71pub use media_v3::{72 KeyframeRequestReasonV3, MAX_MEDIA_GROUP_BYTES_V3, MAX_MEDIA_OBJECT_DELIVERY_TIMEOUT_MS,73 MAX_MEDIA_OBJECT_ID_V3, MEDIA_CONTROL_REQUEST_V3_LEN, MEDIA_OBJECT_V3_HEADER_LEN,74 MIN_MEDIA_OBJECT_DELIVERY_TIMEOUT_MS, MediaControlRequestTypeV3, MediaControlRequestV3,75 MediaObjectHeaderV3, MediaObjectV3, read_media_control_request_v3, read_media_object_v3,76 write_media_control_request_v3, write_media_object_v3,77};78pub use moq_catalog::{79 GoqCatalogDocument, GoqCatalogDocumentV2, MAX_MOQ_CATALOG_BYTES, MOQ_AUDIO_GROUP_FORMAT_V1,80 MOQ_AUDIO_OBJECT_FORMAT_V1, MOQ_AUDIO_TRACK_PRIORITY, MOQ_AUTHENTICATED_MEDIA_OBJECT_FORMAT_V1,81 MOQ_CATALOG_EXTENSION_VERSION_V1, MOQ_CATALOG_EXTENSION_VERSION_V2, MOQ_GOP_GROUP_FORMAT_V1,82 MOQ_MEDIA_OBJECT_FORMAT_V1, MOQ_VIDEO_TRACK_PRIORITY, MoqAudioCatalogV2, MoqCatalogExtensionV1,83 MoqCatalogExtensionV2, MoqTrackAuthenticationV2, MoqTrackDescriptorV1, MoqVideoCatalogV1,84 MoqVideoCatalogV2,85};86pub use subscription::{87 MAX_SUBSCRIPTION_CAPABILITY_TOKEN_LEN, MAX_SUBSCRIPTION_TTL_SECS,88 SUBSCRIPTION_CAPABILITY_TOKEN_PREFIX, SignedSubscriptionCapability, SubscriptionClaims,89 SubscriptionTracks,90};9192/// Protocol version encoded in v1 messages.93pub const PROTOCOL_VERSION: u16 = 1;94/// Protocol version encoded in v2 control and input messages.95pub const PROTOCOL_VERSION_V2: u16 = 2;9697/// ALPN for custom MoQ-style grouped media objects and keyframe control.98///99/// This is a Sigil protocol and is not IETF MoQ Transport compatible.100pub const MEDIA_ALPN_V3: &[u8] = b"sigil/media/3";101/// ALPN for bounded receiver telemetry and host bitrate decisions.102pub const MEDIA_FEEDBACK_ALPN_V1: &[u8] = b"sigil/media-feedback/1";103/// ALPN for the v1 latency-independent input stream.104pub const INPUT_ALPN_V1: &[u8] = b"sigil/input/1";105/// ALPN for focus-generation-bound v2 input.106pub const INPUT_ALPN_V2: &[u8] = b"sigil/input/2";107/// ALPN for the v1 session-control stream.108pub const CONTROL_ALPN_V1: &[u8] = b"sigil/control/1";109/// ALPN for revisioned multi-viewer session control.110pub const CONTROL_ALPN_V2: &[u8] = b"sigil/control/2";111/// Static upstream MoQ track carrying bounded H.264 access-unit objects.112pub const MOQ_VIDEO_H264_TRACK: &str = "video/h264";113/// Static upstream MoQ track carrying authenticated 20 ms Opus packets.114pub const MOQ_AUDIO_OPUS_TRACK: &str = "audio/opus";115/// ALPN for the v1 low-latency Opus datagram connection.116pub const AUDIO_ALPN_V1: &[u8] = b"sigil/audio/1";117118/// Maximum encoded video access-unit size accepted from the network (16 MiB).119pub const MAX_MEDIA_PAYLOAD_LEN: usize = 16 * 1024 * 1024;120/// Maximum dimension carried by a v1 video header.121pub const MAX_VIDEO_DIMENSION: u16 = 8_192;122/// Maximum pixel count carried by a v1 video header (8K UHD).123pub const MAX_VIDEO_PIXELS: u32 = 7_680 * 4_320;124/// Maximum length-prefixed JSON input message.125pub const MAX_INPUT_MESSAGE_LEN: usize = 4 * 1024;126/// Maximum length-prefixed JSON handshake message.127pub const MAX_HANDSHAKE_MESSAGE_LEN: usize = 16 * 1024;128/// Maximum Opus payload carried in one QUIC datagram.129pub const MAX_AUDIO_PAYLOAD_LEN: usize = 512;130pub const OPUS_SAMPLE_RATE: u32 = 48_000;131pub const OPUS_CHANNELS: u8 = 2;132pub const OPUS_FRAME_SAMPLES: u16 = 960;133134/// Derive the session-scoped upstream MoQ broadcast name advertised by Sigil.135///136/// The authenticated control handshake supplies the non-zero session id, so137/// Portal can derive this application namespace without accepting a138/// peer-controlled path.139pub fn media_moq_broadcast_name(session_id: u64) -> Result<String> {140 if session_id == 0 {141 return Err(ProtocolError::InvalidMessage {142 message_type: "MoQ broadcast name",143 reason: "session id must be non-zero",144 });145 }146 Ok(format!("sigil/session/{session_id}/video"))147}148149/// Derive the host-generation-scoped MoQ broadcast name used by control v2.150pub fn media_generation_moq_broadcast_name(generation_id: u64) -> Result<String> {151 if generation_id == 0 {152 return Err(ProtocolError::InvalidMessage {153 message_type: "MoQ generation broadcast name",154 reason: "generation id must be non-zero",155 });156 }157 Ok(format!("sigil/generation/{generation_id}/media"))158}159160#[cfg(test)]161mod tests {162 use super::*;163164 #[test]165 fn alpn_values_are_deployment_golden_vectors() {166 assert_eq!(MEDIA_ALPN_V3, b"sigil/media/3");167 assert_eq!(MEDIA_FEEDBACK_ALPN_V1, b"sigil/media-feedback/1");168 assert_eq!(INPUT_ALPN_V1, b"sigil/input/1");169 assert_eq!(INPUT_ALPN_V2, b"sigil/input/2");170 assert_eq!(CONTROL_ALPN_V1, b"sigil/control/1");171 assert_eq!(CONTROL_ALPN_V2, b"sigil/control/2");172 assert_eq!(AUDIO_ALPN_V1, b"sigil/audio/1");173 }174175 #[test]176 fn moq_media_namespace_is_session_scoped_and_stable() {177 assert_eq!(MOQ_VIDEO_H264_TRACK, "video/h264");178 assert_eq!(MOQ_AUDIO_OPUS_TRACK, "audio/opus");179 assert_eq!(180 media_moq_broadcast_name(42).unwrap(),181 "sigil/session/42/video"182 );183 assert!(media_moq_broadcast_name(0).is_err());184 assert_eq!(185 media_generation_moq_broadcast_name(42).unwrap(),186 "sigil/generation/42/media"187 );188 assert!(media_generation_moq_broadcast_name(0).is_err());189 }190}