diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index b1dad4cbbe..0000000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "crates/live_kit_server/protocol"] - path = crates/live_kit_server/protocol - url = https://github.com/livekit/protocol diff --git a/crates/live_kit_server/build.rs b/crates/live_kit_server/build.rs index b12b36ffe0..ce780dfc47 100644 --- a/crates/live_kit_server/build.rs +++ b/crates/live_kit_server/build.rs @@ -1,6 +1,9 @@ fn main() { prost_build::Config::new() .type_attribute("SendDataResponse", "#[allow(clippy::empty_docs)]") - .compile_protos(&["protocol/livekit_room.proto"], &["protocol"]) + .compile_protos( + &["vendored/protocol/livekit_room.proto"], + &["vendored/protocol"], + ) .unwrap(); } diff --git a/crates/live_kit_server/protocol b/crates/live_kit_server/protocol deleted file mode 160000 index 8645a138fb..0000000000 --- a/crates/live_kit_server/protocol +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8645a138fb2ea72c4dab13e739b1f3c9ea29ac84 diff --git a/crates/live_kit_server/vendored/protocol/README.md b/crates/live_kit_server/vendored/protocol/README.md new file mode 100644 index 0000000000..9f3d818bb5 --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/README.md @@ -0,0 +1,5 @@ +# LiveKit Protocol + +This is a vendored copy of the [LiveKit protocol](https://github.com/livekit/protocol). + +Vendored at [`8645a138fb2ea72c4dab13e739b1f3c9ea29ac84`](https://github.com/livekit/protocol/tree/8645a138fb2ea72c4dab13e739b1f3c9ea29ac84). diff --git a/crates/live_kit_server/vendored/protocol/livekit_analytics.proto b/crates/live_kit_server/vendored/protocol/livekit_analytics.proto new file mode 100644 index 0000000000..f9b33fb408 --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_analytics.proto @@ -0,0 +1,118 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "google/protobuf/empty.proto"; +import "google/protobuf/timestamp.proto"; +import "livekit_models.proto"; +import "livekit_egress.proto"; +import "livekit_ingress.proto"; + +service AnalyticsRecorderService { + rpc IngestStats(stream AnalyticsStats) returns (google.protobuf.Empty){}; + rpc IngestEvents(stream AnalyticsEvents) returns (google.protobuf.Empty){}; +} + +enum StreamType { + UPSTREAM = 0; + DOWNSTREAM = 1; +} + +message AnalyticsVideoLayer { + int32 layer = 1; + uint32 packets = 2; + uint64 bytes = 3; + uint32 frames = 4; +} + +message AnalyticsStream { + uint32 ssrc = 1; + uint32 primary_packets = 2; + uint64 primary_bytes = 3; + uint32 retransmit_packets = 4; + uint64 retransmit_bytes = 5; + uint32 padding_packets = 6; + uint64 padding_bytes = 7; + uint32 packets_lost = 8; + uint32 frames = 9; + uint32 rtt = 10; + uint32 jitter = 11; + uint32 nacks = 12; + uint32 plis = 13; + uint32 firs = 14; + repeated AnalyticsVideoLayer video_layers = 15; +} + +message AnalyticsStat { + string analytics_key = 1; + StreamType kind = 2; + google.protobuf.Timestamp time_stamp = 3; + string node = 4; + string room_id = 5; + string room_name = 6; + string participant_id = 7; + string track_id = 8; + float score = 9; + repeated AnalyticsStream streams = 10; + string mime = 11; +} + +message AnalyticsStats { + repeated AnalyticsStat stats = 1; +} + +enum AnalyticsEventType { + ROOM_CREATED = 0; + ROOM_ENDED = 1; + PARTICIPANT_JOINED = 2; + PARTICIPANT_LEFT = 3; + TRACK_PUBLISHED = 4; + TRACK_UNPUBLISHED = 5; + TRACK_SUBSCRIBED = 6; + TRACK_UNSUBSCRIBED = 7; + TRACK_PUBLISHED_UPDATE = 10; + PARTICIPANT_ACTIVE = 11; + EGRESS_STARTED = 12; + EGRESS_ENDED = 13; + TRACK_MAX_SUBSCRIBED_VIDEO_QUALITY = 14; + RECONNECTED = 15; + INGRESS_STARTED = 16; + INGRESS_ENDED = 17; +} + +message AnalyticsClientMeta { + string region = 1; + string node = 2; + string client_addr = 3; + uint32 client_connect_time = 4; + // udp, tcp, turn + string connection_type = 5; +} + +message AnalyticsEvent { + AnalyticsEventType type = 1; + google.protobuf.Timestamp timestamp = 2; + string room_id = 3; + Room room = 4; + string participant_id = 5; + ParticipantInfo participant = 6; + string track_id = 7; + TrackInfo track = 8; + string analytics_key = 10; + ClientInfo client_info = 11; + AnalyticsClientMeta client_meta = 12; + string egress_id = 13; + VideoQuality max_subscribed_video_quality = 14; + ParticipantInfo publisher = 15; + string mime = 16; + EgressInfo egress = 17; + IngressInfo ingress = 18; + +} + +message AnalyticsEvents { + repeated AnalyticsEvent events = 1; +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_egress.proto b/crates/live_kit_server/vendored/protocol/livekit_egress.proto new file mode 100644 index 0000000000..76cdf1e086 --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_egress.proto @@ -0,0 +1,263 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +service Egress { + // start recording or streaming a room, participant, or tracks + rpc StartRoomCompositeEgress(RoomCompositeEgressRequest) returns (EgressInfo); + rpc StartTrackCompositeEgress(TrackCompositeEgressRequest) returns (EgressInfo); + rpc StartTrackEgress(TrackEgressRequest) returns (EgressInfo); + + // update web composite layout + rpc UpdateLayout(UpdateLayoutRequest) returns (EgressInfo); + + // add or remove stream endpoints + rpc UpdateStream(UpdateStreamRequest) returns (EgressInfo); + + // list available egress + rpc ListEgress(ListEgressRequest) returns (ListEgressResponse); + + // stop a recording or stream + rpc StopEgress(StopEgressRequest) returns (EgressInfo); +} + +// composite using a web browser +message RoomCompositeEgressRequest { + string room_name = 1; // required + string layout = 2; // (optional) + bool audio_only = 3; // (default false) + bool video_only = 4; // (default false) + string custom_base_url = 5; // (default https://recorder.livekit.io) + oneof output { // required + EncodedFileOutput file = 6; + StreamOutput stream = 7; + SegmentedFileOutput segments = 10; + } + oneof options { + EncodingOptionsPreset preset = 8; // (default H264_720P_30) + EncodingOptions advanced = 9; // (optional) + } +} + +// containerize up to one audio and one video track +message TrackCompositeEgressRequest { + string room_name = 1; // required + string audio_track_id = 2; // (optional) + string video_track_id = 3; // (optional) + oneof output { // required + EncodedFileOutput file = 4; + StreamOutput stream = 5; + SegmentedFileOutput segments = 8; + } + oneof options { + EncodingOptionsPreset preset = 6; // (default H264_720P_30) + EncodingOptions advanced = 7; // (optional) + } +} + +// record tracks individually, without transcoding +message TrackEgressRequest { + string room_name = 1; // required + string track_id = 2; // required + oneof output { // required + DirectFileOutput file = 3; + string websocket_url = 4; + } +} + +enum EncodedFileType { + DEFAULT_FILETYPE = 0; // file type chosen based on codecs + MP4 = 1; + OGG = 2; +} + +message EncodedFileOutput { + EncodedFileType file_type = 1; // (optional) + string filepath = 2; // (optional) + oneof output { // required + S3Upload s3 = 3; + GCPUpload gcp = 4; + AzureBlobUpload azure = 5; + } +} + +// Used to generate HLS segments or other kind of segmented output +message SegmentedFileOutput { + SegmentedFileProtocol protocol = 1; // (optional) + string filename_prefix = 2; // (optional) + string playlist_name = 3; // (optional) + uint32 segment_duration = 4; // (optional) + oneof output { // required + S3Upload s3 = 5; + GCPUpload gcp = 6; + AzureBlobUpload azure = 7; + } +} + +message DirectFileOutput { + string filepath = 1; // (optional) + oneof output { // required + S3Upload s3 = 2; + GCPUpload gcp = 3; + AzureBlobUpload azure = 4; + } +} + +message S3Upload { + string access_key = 1; + string secret = 2; + string region = 3; + string endpoint = 4; + string bucket = 5; +} + +message GCPUpload { + bytes credentials = 1; + string bucket = 2; +} + +message AzureBlobUpload { + string account_name = 1; + string account_key = 2; + string container_name = 3; +} + +enum StreamProtocol { + DEFAULT_PROTOCOL = 0; // protocol chosen based on urls + RTMP = 1; +} + +enum SegmentedFileProtocol { + DEFAULT_SEGMENTED_FILE_PROTOCOL = 0; + HLS_PROTOCOL = 1; +} + +message StreamOutput { + StreamProtocol protocol = 1; // required + repeated string urls = 2; // required +} + +enum AudioCodec { + DEFAULT_AC = 0; + OPUS = 1; + AAC = 2; +} + +enum VideoCodec { + DEFAULT_VC = 0; + H264_BASELINE = 1; + H264_MAIN = 2; + H264_HIGH = 3; +} + +message EncodingOptions { + int32 width = 1; // (default 1920) + int32 height = 2; // (default 1080) + int32 depth = 3; // (default 24) + int32 framerate = 4; // (default 30) + AudioCodec audio_codec = 5; // (default OPUS) + int32 audio_bitrate = 6; // (default 128) + int32 audio_frequency = 7; // (default 44100) + VideoCodec video_codec = 8; // (default H264_MAIN) + int32 video_bitrate = 9; // (default 4500) +} + +enum EncodingOptionsPreset { + H264_720P_30 = 0; // 1280x720, 30fps, 3000kpbs, H.264_MAIN / OPUS + H264_720P_60 = 1; // 1280x720, 60fps, 4500kbps, H.264_MAIN / OPUS + H264_1080P_30 = 2; // 1920x1080, 30fps, 4500kbps, H.264_MAIN / OPUS + H264_1080P_60 = 3; // 1920x1080, 60fps, 6000kbps, H.264_MAIN / OPUS + PORTRAIT_H264_720P_30 = 4; // 720x1280, 30fps, 3000kpbs, H.264_MAIN / OPUS + PORTRAIT_H264_720P_60 = 5; // 720x1280, 60fps, 4500kbps, H.264_MAIN / OPUS + PORTRAIT_H264_1080P_30 = 6; // 1080x1920, 30fps, 4500kbps, H.264_MAIN / OPUS + PORTRAIT_H264_1080P_60 = 7; // 1080x1920, 60fps, 6000kbps, H.264_MAIN / OPUS +} + +message UpdateLayoutRequest { + string egress_id = 1; + string layout = 2; +} + +message UpdateStreamRequest { + string egress_id = 1; + repeated string add_output_urls = 2; + repeated string remove_output_urls = 3; +} + +message ListEgressRequest { + string room_name = 1; // (optional, used to filter results) +} + +message ListEgressResponse { + repeated EgressInfo items = 1; +} + +message StopEgressRequest { + string egress_id = 1; +} + +enum EgressStatus { + EGRESS_STARTING = 0; + EGRESS_ACTIVE = 1; + EGRESS_ENDING = 2; + EGRESS_COMPLETE = 3; + EGRESS_FAILED = 4; + EGRESS_ABORTED = 5; + EGRESS_LIMIT_REACHED = 6; +} + +message EgressInfo { + string egress_id = 1; + string room_id = 2; + string room_name = 13; + EgressStatus status = 3; + int64 started_at = 10; + int64 ended_at = 11; + string error = 9; + oneof request { + RoomCompositeEgressRequest room_composite = 4; + TrackCompositeEgressRequest track_composite = 5; + TrackEgressRequest track = 6; + } + oneof result { + StreamInfoList stream = 7; + FileInfo file = 8; + SegmentsInfo segments = 12; + } +} + +message StreamInfoList { + repeated StreamInfo info = 1; +} + +message StreamInfo { + enum Status { + ACTIVE = 0; + FINISHED = 1; + FAILED = 2; + } + + string url = 1; + int64 started_at = 2; + int64 ended_at = 3; + int64 duration = 4; + Status status = 5; +} + +message FileInfo { + string filename = 1; + int64 duration = 6; + int64 size = 4; + string location = 5; +} + +message SegmentsInfo { + string playlist_name = 1; + int64 duration = 2; + int64 size = 3; + string playlist_location = 4; + int64 segment_count = 5; +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_ingress.proto b/crates/live_kit_server/vendored/protocol/livekit_ingress.proto new file mode 100644 index 0000000000..8a2611da80 --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_ingress.proto @@ -0,0 +1,134 @@ +syntax = "proto3"; + +package livekit; + +import "livekit_models.proto"; + +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +service Ingress { + // Create a new Ingress + rpc CreateIngress(CreateIngressRequest) returns (IngressInfo); + // Update an existing Ingress. Ingress can only be updated when it's in ENDPOINT_WAITING state. + rpc UpdateIngress(UpdateIngressRequest) returns (IngressInfo); + rpc ListIngress(ListIngressRequest) returns (ListIngressResponse); + rpc DeleteIngress(DeleteIngressRequest) returns (IngressInfo); +} + +message CreateIngressRequest { + IngressInput input_type = 1; + // User provided identifier for the ingress + string name = 2; + // room to publish to + string room_name = 3; + // publish as participant + string participant_identity = 4; + // name of publishing participant (used for display only) + string participant_name = 5; + IngressAudioOptions audio = 6; + IngressVideoOptions video = 7; +} + +enum IngressInput { + RTMP_INPUT = 0; + // FILE_INPUT = 1; + // SRT_INPUT = 2; + // URL_INPUT = 3; +} + +message IngressAudioOptions { + string name = 1; + TrackSource source = 2; + // desired mime_type to publish to room + string mime_type = 3; + uint32 bitrate = 4; + bool disable_dtx = 5; + uint32 channels = 6; +} + +message IngressVideoOptions { + string name = 1; + TrackSource source = 2; + // desired mime_type to publish to room + string mime_type = 3; + // simulcast layers to publish, when empty, it'll pick default simulcast + // layers at 1/2 and 1/4 of the dimensions + repeated VideoLayer layers = 4; +} + +message IngressInfo { + string ingress_id = 1; + string name = 2; + string stream_key = 3; + string url = 4; + // for RTMP input, it'll be a rtmp:// URL + // for FILE input, it'll be a http:// URL + // for SRT input, it'll be a srt:// URL + IngressInput input_type = 5; + IngressAudioOptions audio = 6; + IngressVideoOptions video = 7; + string room_name = 8; + string participant_identity = 9; + string participant_name = 10; + bool reusable = 11; + IngressState state = 12; // Description of error/stream non compliance and debug info for publisher otherwise (received bitrate, resolution, bandwidth) + + // NEXT_ID: 13 +} + +message IngressState { + enum Status { + ENDPOINT_INACTIVE = 0; + ENDPOINT_BUFFERING = 1; + ENDPOINT_PUBLISHING = 2; + ENDPOINT_ERROR = 3; + } + + Status status = 1; + string error = 2; // Error/non compliance description if any + InputVideoState video = 3; + InputAudioState audio = 4; + string room_id = 5; // ID of the current/previous room published to + int64 started_at = 7; + repeated TrackInfo tracks = 6; +} + +message InputVideoState { + uint32 mime_type = 1; +// uint32 bitrate = 2; + uint32 width = 3; + uint32 height = 4; + uint32 framerate = 5; +} + +message InputAudioState { + uint32 mime_type = 1; +// uint32 bitrate = 2; + uint32 channels = 3; + uint32 sample_rate = 4; +} + +message UpdateIngressRequest { + string ingress_id = 1; + string name = 2; + string room_name = 3; + string participant_identity = 4; + string participant_name = 5; + IngressAudioOptions audio = 6; + IngressVideoOptions video = 7; +} + +message ListIngressRequest { + // when blank, lists all ingress endpoints + string room_name = 1; +} + +message ListIngressResponse { + repeated IngressInfo items = 1; +} + +message DeleteIngressRequest { + string ingress_id = 1; +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_internal.proto b/crates/live_kit_server/vendored/protocol/livekit_internal.proto new file mode 100644 index 0000000000..02724fb56c --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_internal.proto @@ -0,0 +1,138 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +// internal protos, not exposed to clients +import "livekit_models.proto"; +import "livekit_rtc.proto"; +import "livekit_room.proto"; + +enum NodeType { + SERVER = 0; + CONTROLLER = 1; + MEDIA = 2; + TURN = 4; +} + +enum NodeState { + STARTING_UP = 0; + SERVING = 1; + SHUTTING_DOWN = 2; +} + +message Node { + string id = 1; + string ip = 2; + uint32 num_cpus = 3; + NodeStats stats = 4; + NodeType type = 5; + NodeState state = 6; + string region = 7; +} + +message NodeStats { + // when server was started + int64 started_at = 1; + // when server last reported its status + int64 updated_at = 2; + + // room + int32 num_rooms = 3; + int32 num_clients = 4; + int32 num_tracks_in = 5; + int32 num_tracks_out = 6; + + // packet + uint64 bytes_in = 7; + uint64 bytes_out = 8; + uint64 packets_in = 9; + uint64 packets_out = 10; + uint64 nack_total = 11; + float bytes_in_per_sec = 12; + float bytes_out_per_sec = 13; + float packets_in_per_sec = 14; + float packets_out_per_sec = 15; + float nack_per_sec = 16; + + // system + uint32 num_cpus = 17; + float load_avg_last1min = 18; + float load_avg_last5min = 19; + float load_avg_last15min = 20; + float cpu_load = 21; + uint32 sys_packets_out = 28; + uint32 sys_packets_dropped = 29; + float sys_packets_out_per_sec = 30; + float sys_packets_dropped_per_sec = 31; + float sys_packets_dropped_pct_per_sec = 32; + + // retransmissions + uint64 retransmit_bytes_out = 22; + uint64 retransmit_packets_out = 23; + float retransmit_bytes_out_per_sec = 24; + float retransmit_packets_out_per_sec = 25; + + // participant joins + uint64 participant_join = 26; + float participant_join_per_sec = 27; +} + +// message to RTC nodes +message RTCNodeMessage { + string participant_key = 1; + int64 sender_time = 11; + string connection_id = 13; + oneof message { + StartSession start_session = 2; + SignalRequest request = 3; + // internal messages + RoomParticipantIdentity remove_participant = 4; + MuteRoomTrackRequest mute_track = 5; + UpdateParticipantRequest update_participant = 6; + DeleteRoomRequest delete_room = 7; + UpdateSubscriptionsRequest update_subscriptions = 8; + SendDataRequest send_data = 9; + UpdateRoomMetadataRequest update_room_metadata = 10; + KeepAlive keep_alive = 12; + } +} + +// message to Signal nodes +message SignalNodeMessage { + string connection_id = 1; + oneof message { + SignalResponse response = 2; + EndSession end_session = 3; + } +} + +message StartSession { + string room_name = 1; + string identity = 2; + string connection_id = 3; + // if a client is reconnecting (i.e. resume instead of restart) + bool reconnect = 4; + bool auto_subscribe = 9; + bool hidden = 10; + ClientInfo client = 11; + bool recorder = 12; + string name = 13; + // A user's ClaimGrants serialized in JSON + string grants_json = 14; + bool adaptive_stream = 15; + //if reconnect, client will set current sid + string participant_id = 16; +} + +message EndSession { +} + +message RemoveParticipant { + string participant_id = 1; +} + +message KeepAlive { +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_models.proto b/crates/live_kit_server/vendored/protocol/livekit_models.proto new file mode 100644 index 0000000000..ea43e8674e --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_models.proto @@ -0,0 +1,330 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "google/protobuf/timestamp.proto"; +import "livekit_egress.proto"; + +message Room { + string sid = 1; + string name = 2; + uint32 empty_timeout = 3; + uint32 max_participants = 4; + int64 creation_time = 5; + string turn_password = 6; + repeated Codec enabled_codecs = 7; + string metadata = 8; + uint32 num_participants = 9; + bool active_recording = 10; +} + +// room info that should not be returned to clients +message RoomInternal { + AutoTrackEgress track_egress = 1; +} + +message AutoTrackEgress { + string file_prefix = 1; + oneof output { + S3Upload s3 = 2; + GCPUpload gcp = 3; + AzureBlobUpload azure = 4; + } +} + +message Codec { + string mime = 1; + string fmtp_line = 2; +} + +message ParticipantPermission { + // allow participant to subscribe to other tracks in the room + bool can_subscribe = 1; + // allow participant to publish new tracks to room + bool can_publish = 2; + // allow participant to publish data + bool can_publish_data = 3; + // indicates that it's hidden to others + bool hidden = 7; + // indicates it's a recorder instance + bool recorder = 8; +} + +message ParticipantInfo { + enum State { + // websocket' connected, but not offered yet + JOINING = 0; + // server received client offer + JOINED = 1; + // ICE connectivity established + ACTIVE = 2; + // WS disconnected + DISCONNECTED = 3; + } + string sid = 1; + string identity = 2; + State state = 3; + repeated TrackInfo tracks = 4; + string metadata = 5; + // timestamp when participant joined room, in seconds + int64 joined_at = 6; + string name = 9; + uint32 version = 10; + ParticipantPermission permission = 11; + string region = 12; + // indicates the participant has an active publisher connection + // and can publish to the server + bool is_publisher = 13; +} + +enum TrackType { + AUDIO = 0; + VIDEO = 1; + DATA = 2; +} + +enum TrackSource { + UNKNOWN = 0; + CAMERA = 1; + MICROPHONE = 2; + SCREEN_SHARE = 3; + SCREEN_SHARE_AUDIO = 4; +} + +message SimulcastCodecInfo { + string mime_type = 1; + string mid = 2; + string cid = 3; + repeated VideoLayer layers = 4; +} + +message TrackInfo { + string sid = 1; + TrackType type = 2; + string name = 3; + bool muted = 4; + // original width of video (unset for audio) + // clients may receive a lower resolution version with simulcast + uint32 width = 5; + // original height of video (unset for audio) + uint32 height = 6; + // true if track is simulcasted + bool simulcast = 7; + // true if DTX (Discontinuous Transmission) is disabled for audio + bool disable_dtx = 8; + // source of media + TrackSource source = 9; + repeated VideoLayer layers = 10; + // mime type of codec + string mime_type = 11; + string mid = 12; + repeated SimulcastCodecInfo codecs = 13; +} + +enum VideoQuality { + LOW = 0; + MEDIUM = 1; + HIGH = 2; + OFF = 3; +} + +// provide information about available spatial layers +message VideoLayer { + // for tracks with a single layer, this should be HIGH + VideoQuality quality = 1; + uint32 width = 2; + uint32 height = 3; + // target bitrate, server will measure actual + uint32 bitrate = 4; + uint32 ssrc = 5; +} + +// new DataPacket API +message DataPacket { + enum Kind { + RELIABLE = 0; + LOSSY = 1; + } + Kind kind = 1; + oneof value { + UserPacket user = 2; + ActiveSpeakerUpdate speaker = 3; + } +} + +message ActiveSpeakerUpdate { + repeated SpeakerInfo speakers = 1; +} + +message SpeakerInfo { + string sid = 1; + // audio level, 0-1.0, 1 is loudest + float level = 2; + // true if speaker is currently active + bool active = 3; +} + +message UserPacket { + // participant ID of user that sent the message + string participant_sid = 1; + // user defined payload + bytes payload = 2; + // the ID of the participants who will receive the message (the message will be sent to all the people in the room if this variable is empty) + repeated string destination_sids = 3; +} + +enum ConnectionQuality { + POOR = 0; + GOOD = 1; + EXCELLENT = 2; +} + +message ParticipantTracks { + // participant ID of participant to whom the tracks belong + string participant_sid = 1; + repeated string track_sids = 2; +} + +// details about the server +message ServerInfo { + enum Edition { + Standard = 0; + Cloud = 1; + } + Edition edition = 1; + string version = 2; + int32 protocol = 3; + string region = 4; + string node_id = 5; + // additional debugging information. sent only if server is in development mode + string debug_info = 6; +} + +// details about the client +message ClientInfo { + enum SDK { + UNKNOWN = 0; + JS = 1; + SWIFT = 2; + ANDROID = 3; + FLUTTER = 4; + GO = 5; + UNITY = 6; + } + + SDK sdk = 1; + string version = 2; + int32 protocol = 3; + string os = 4; + string os_version = 5; + string device_model = 6; + string browser = 7; + string browser_version = 8; + string address = 9; + // wifi, wired, cellular, vpn, empty if not known + string network = 10; +} + +// server provided client configuration +message ClientConfiguration { + VideoConfiguration video = 1; + VideoConfiguration screen = 2; + + ClientConfigSetting resume_connection = 3; + DisabledCodecs disabled_codecs = 4; + ClientConfigSetting force_relay = 5; +} + +enum ClientConfigSetting { + UNSET = 0; + DISABLED = 1; + ENABLED = 2; +} + +message VideoConfiguration { + ClientConfigSetting hardware_encoder = 1; +} + +message DisabledCodecs { + repeated Codec codecs = 1; +} + +enum DisconnectReason { + UNKNOWN_REASON = 0; + CLIENT_INITIATED = 1; + DUPLICATE_IDENTITY = 2; + SERVER_SHUTDOWN = 3; + PARTICIPANT_REMOVED = 4; + ROOM_DELETED = 5; + STATE_MISMATCH = 6; + JOIN_FAILURE = 7; +} + +message RTPStats { + google.protobuf.Timestamp start_time = 1; + google.protobuf.Timestamp end_time = 2; + double duration = 3; + + uint32 packets = 4; + double packet_rate = 5; + + uint64 bytes = 6; + uint64 header_bytes = 39; + double bitrate = 7; + + uint32 packets_lost = 8; + double packet_loss_rate = 9; + float packet_loss_percentage = 10; + + uint32 packets_duplicate = 11; + double packet_duplicate_rate = 12; + + uint64 bytes_duplicate = 13; + uint64 header_bytes_duplicate = 40; + double bitrate_duplicate = 14; + + uint32 packets_padding = 15; + double packet_padding_rate = 16; + + uint64 bytes_padding = 17; + uint64 header_bytes_padding = 41; + double bitrate_padding = 18; + + uint32 packets_out_of_order = 19; + + uint32 frames = 20; + double frame_rate = 21; + + double jitter_current = 22; + double jitter_max = 23; + + map gap_histogram = 24; + + uint32 nacks = 25; + uint32 nack_acks = 37; + uint32 nack_misses = 26; + uint32 nack_repeated = 38; + + uint32 plis = 27; + google.protobuf.Timestamp last_pli = 28; + + uint32 firs = 29; + google.protobuf.Timestamp last_fir = 30; + + uint32 rtt_current = 31; + uint32 rtt_max = 32; + + uint32 key_frames = 33; + google.protobuf.Timestamp last_key_frame = 34; + + uint32 layer_lock_plis = 35; + google.protobuf.Timestamp last_layer_lock_pli = 36; +} + +message TimedVersion { + int64 unix_micro = 1; + int32 ticks = 2; +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_room.proto b/crates/live_kit_server/vendored/protocol/livekit_room.proto new file mode 100644 index 0000000000..a3c10bf68c --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_room.proto @@ -0,0 +1,159 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "livekit_models.proto"; +import "livekit_egress.proto"; + +// Room service that can be performed on any node +// they are Twirp-based HTTP req/responses +service RoomService { + // Creates a room with settings. Requires `roomCreate` permission. + // This method is optional; rooms are automatically created when clients connect to them for the first time. + rpc CreateRoom(CreateRoomRequest) returns (Room); + + // List rooms that are active on the server. Requires `roomList` permission. + rpc ListRooms(ListRoomsRequest) returns (ListRoomsResponse); + + // Deletes an existing room by name or id. Requires `roomCreate` permission. + // DeleteRoom will disconnect all participants that are currently in the room. + rpc DeleteRoom(DeleteRoomRequest) returns (DeleteRoomResponse); + + // Lists participants in a room, Requires `roomAdmin` + rpc ListParticipants(ListParticipantsRequest) returns (ListParticipantsResponse); + + // Get information on a specific participant, Requires `roomAdmin` + rpc GetParticipant(RoomParticipantIdentity) returns (ParticipantInfo); + + // Removes a participant from room. Requires `roomAdmin` + rpc RemoveParticipant(RoomParticipantIdentity) returns (RemoveParticipantResponse); + + // Mute/unmute a participant's track, Requires `roomAdmin` + rpc MutePublishedTrack(MuteRoomTrackRequest) returns (MuteRoomTrackResponse); + + // Update participant metadata, will cause updates to be broadcasted to everyone in the room. Requires `roomAdmin` + rpc UpdateParticipant(UpdateParticipantRequest) returns (ParticipantInfo); + + // Subscribes or unsubscribe a participant from tracks. Requires `roomAdmin` + rpc UpdateSubscriptions(UpdateSubscriptionsRequest) returns (UpdateSubscriptionsResponse); + + // Send data over data channel to participants in a room, Requires `roomAdmin` + rpc SendData(SendDataRequest) returns (SendDataResponse); + + // Update room metadata, will cause updates to be broadcasted to everyone in the room, Requires `roomAdmin` + rpc UpdateRoomMetadata (UpdateRoomMetadataRequest) returns (Room); +} + +message CreateRoomRequest { + // name of the room + string name = 1; + // number of seconds to keep the room open if no one joins + uint32 empty_timeout = 2; + // limit number of participants that can be in a room + uint32 max_participants = 3; + // override the node room is allocated to, for debugging + string node_id = 4; + // metadata of room + string metadata = 5; + // egress + RoomEgress egress = 6; +} + +message RoomEgress { + RoomCompositeEgressRequest room = 1; + AutoTrackEgress tracks = 2; +} + +message ListRoomsRequest { + // when set, will only return rooms with name match + repeated string names = 1; +} + +message ListRoomsResponse { + repeated Room rooms = 1; +} + +message DeleteRoomRequest { + // name of the room + string room = 1; +} + +message DeleteRoomResponse { +} + +message ListParticipantsRequest { + // name of the room + string room = 1; +} + +message ListParticipantsResponse { + repeated ParticipantInfo participants = 1; +} + +message RoomParticipantIdentity { + // name of the room + string room = 1; + // identity of the participant + string identity = 2; +} + +message RemoveParticipantResponse { +} + +message MuteRoomTrackRequest { + // name of the room + string room = 1; + string identity = 2; + // sid of the track to mute + string track_sid = 3; + // set to true to mute, false to unmute + bool muted = 4; +} + +message MuteRoomTrackResponse { + TrackInfo track = 1; +} + +message UpdateParticipantRequest { + string room = 1; + string identity = 2; + // metadata to update. skipping updates if left empty + string metadata = 3; + // set to update the participant's permissions + ParticipantPermission permission = 4; +} + +message UpdateSubscriptionsRequest { + string room = 1; + string identity = 2; + // list of sids of tracks + repeated string track_sids = 3; + // set to true to subscribe, false to unsubscribe from tracks + bool subscribe = 4; + // list of participants and their tracks + repeated ParticipantTracks participant_tracks = 5; +} + +message UpdateSubscriptionsResponse { + // empty for now +} + +message SendDataRequest { + string room = 1; + bytes data = 2; + DataPacket.Kind kind = 3; + repeated string destination_sids = 4; +} + +message SendDataResponse { + // +} + +message UpdateRoomMetadataRequest { + string room = 1; + // metadata to update. skipping updates if left empty + string metadata = 2; +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_rpc_internal.proto b/crates/live_kit_server/vendored/protocol/livekit_rpc_internal.proto new file mode 100644 index 0000000000..7fc268a4fb --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_rpc_internal.proto @@ -0,0 +1,83 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "livekit_egress.proto"; +import "livekit_ingress.proto"; + +message StartEgressRequest { + // request metadata + string egress_id = 1; + string request_id = 2; + string sender_id = 10; + int64 sent_at = 4; + + // request + oneof request { + RoomCompositeEgressRequest room_composite = 5; + TrackCompositeEgressRequest track_composite = 6; + TrackEgressRequest track = 7; + } + + // connection info + string room_id = 3; + string token = 8; + string ws_url = 9; +} + +message EgressRequest { + // request metadata + string egress_id = 1; + string request_id = 2; + string sender_id = 5; + + // request + oneof request { + UpdateStreamRequest update_stream = 3; + StopEgressRequest stop = 4; + } +} + +message EgressResponse { + EgressInfo info = 1; + string error = 2; + string request_id = 3; +} + +message IngressRequest { + // request metadata + string ingress_id = 1; + string request_id = 2; + string sender_id = 3; + + oneof request { + UpdateIngressRequest update = 4; + DeleteIngressRequest delete = 5; + } +} + +// Query an ingress info from an ingress ID or stream key +message GetIngressInfoRequest { + string ingress_id = 1; + string stream_key = 2; + string request_id = 3; + string sender_id = 4; + int64 sent_at = 5; +} + +message IngressResponse { + IngressInfo info = 1; + string error = 2; + string request_id = 3; +} + +message GetIngressInfoResponse { + IngressInfo info = 1; + string token = 2; + string ws_url = 3; + string error = 4; + string request_id = 5; +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_rtc.proto b/crates/live_kit_server/vendored/protocol/livekit_rtc.proto new file mode 100644 index 0000000000..e0df62ada8 --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_rtc.proto @@ -0,0 +1,301 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "livekit_models.proto"; + +message SignalRequest { + oneof message { + // initial join exchange, for publisher + SessionDescription offer = 1; + // participant answering publisher offer + SessionDescription answer = 2; + TrickleRequest trickle = 3; + AddTrackRequest add_track = 4; + // mute the participant's published tracks + MuteTrackRequest mute = 5; + // Subscribe or unsubscribe from tracks + UpdateSubscription subscription = 6; + // Update settings of subscribed tracks + UpdateTrackSettings track_setting = 7; + // Immediately terminate session + LeaveRequest leave = 8; + // Set active published layers, deprecated in favor of automatic tracking +// SetSimulcastLayers simulcast = 9; + // Update published video layers + UpdateVideoLayers update_layers = 10; + // Update subscriber permissions + SubscriptionPermission subscription_permission = 11; + // sync client's subscribe state to server during reconnect + SyncState sync_state = 12; + // Simulate conditions, for client validations + SimulateScenario simulate = 13; + // client triggered ping to server + int64 ping = 14; + } +} + +message SignalResponse { + oneof message { + // sent when join is accepted + JoinResponse join = 1; + // sent when server answers publisher + SessionDescription answer = 2; + // sent when server is sending subscriber an offer + SessionDescription offer = 3; + // sent when an ICE candidate is available + TrickleRequest trickle = 4; + // sent when participants in the room has changed + ParticipantUpdate update = 5; + // sent to the participant when their track has been published + TrackPublishedResponse track_published = 6; + // Immediately terminate session + LeaveRequest leave = 8; + // server initiated mute + MuteTrackRequest mute = 9; + // indicates changes to speaker status, including when they've gone to not speaking + SpeakersChanged speakers_changed = 10; + // sent when metadata of the room has changed + RoomUpdate room_update = 11; + // when connection quality changed + ConnectionQualityUpdate connection_quality = 12; + // when streamed tracks state changed, used to notify when any of the streams were paused due to + // congestion + StreamStateUpdate stream_state_update = 13; + // when max subscribe quality changed, used by dynamic broadcasting to disable unused layers + SubscribedQualityUpdate subscribed_quality_update = 14; + // when subscription permission changed + SubscriptionPermissionUpdate subscription_permission_update = 15; + // update the token the client was using, to prevent an active client from using an expired token + string refresh_token = 16; + // server initiated track unpublish + TrackUnpublishedResponse track_unpublished = 17; + // respond to ping + int64 pong = 18; + } +} + +enum SignalTarget { + PUBLISHER = 0; + SUBSCRIBER = 1; +} + +message SimulcastCodec { + string codec = 1; + string cid = 2; + bool enable_simulcast_layers = 3; +} + +message AddTrackRequest { + // client ID of track, to match it when RTC track is received + string cid = 1; + string name = 2; + TrackType type = 3; + // to be deprecated in favor of layers + uint32 width = 4; + uint32 height = 5; + // true to add track and initialize to muted + bool muted = 6; + // true if DTX (Discontinuous Transmission) is disabled for audio + bool disable_dtx = 7; + TrackSource source = 8; + repeated VideoLayer layers = 9; + + repeated SimulcastCodec simulcast_codecs = 10; + + // server ID of track, publish new codec to exist track + string sid = 11; +} + +message TrickleRequest { + string candidateInit = 1; + SignalTarget target = 2; +} + +message MuteTrackRequest { + string sid = 1; + bool muted = 2; +} + +message JoinResponse { + Room room = 1; + ParticipantInfo participant = 2; + repeated ParticipantInfo other_participants = 3; + // deprecated. use server_info.version instead. + string server_version = 4; + repeated ICEServer ice_servers = 5; + // use subscriber as the primary PeerConnection + bool subscriber_primary = 6; + // when the current server isn't available, return alternate url to retry connection + // when this is set, the other fields will be largely empty + string alternative_url = 7; + ClientConfiguration client_configuration = 8; + // deprecated. use server_info.region instead. + string server_region = 9; + int32 ping_timeout = 10; + int32 ping_interval = 11; + ServerInfo server_info = 12; +} + +message TrackPublishedResponse { + string cid = 1; + TrackInfo track = 2; +} + +message TrackUnpublishedResponse { + string track_sid = 1; +} + +message SessionDescription { + string type = 1; // "answer" | "offer" | "pranswer" | "rollback" + string sdp = 2; +} + +message ParticipantUpdate { + repeated ParticipantInfo participants = 1; +} + +message UpdateSubscription { + repeated string track_sids = 1; + bool subscribe = 2; + repeated ParticipantTracks participant_tracks = 3; +} + +message UpdateTrackSettings { + repeated string track_sids = 1; + // when true, the track is placed in a paused state, with no new data returned + bool disabled = 3; + // deprecated in favor of width & height + VideoQuality quality = 4; + // for video, width to receive + uint32 width = 5; + // for video, height to receive + uint32 height = 6; +} + +message LeaveRequest { + // sent when server initiates the disconnect due to server-restart + // indicates clients should attempt full-reconnect sequence + bool can_reconnect = 1; + DisconnectReason reason = 2; +} + +// message to indicate published video track dimensions are changing +message UpdateVideoLayers { + string track_sid = 1; + repeated VideoLayer layers = 2; +} + +message ICEServer { + repeated string urls = 1; + string username = 2; + string credential = 3; +} + +message SpeakersChanged { + repeated SpeakerInfo speakers = 1; +} + +message RoomUpdate { + Room room = 1; +} + +message ConnectionQualityInfo { + string participant_sid = 1; + ConnectionQuality quality = 2; + float score = 3; +} + +message ConnectionQualityUpdate { + repeated ConnectionQualityInfo updates = 1; +} + +enum StreamState { + ACTIVE = 0; + PAUSED = 1; +} + +message StreamStateInfo { + string participant_sid = 1; + string track_sid = 2; + StreamState state = 3; +} + +message StreamStateUpdate { + repeated StreamStateInfo stream_states = 1; +} + +message SubscribedQuality { + VideoQuality quality = 1; + bool enabled = 2; +} + +message SubscribedCodec { + string codec = 1; + repeated SubscribedQuality qualities = 2; +} + +message SubscribedQualityUpdate { + string track_sid = 1; + repeated SubscribedQuality subscribed_qualities = 2; + repeated SubscribedCodec subscribed_codecs = 3; +} + +message TrackPermission { + // permission could be granted either by participant sid or identity + string participant_sid = 1; + bool all_tracks = 2; + repeated string track_sids = 3; + string participant_identity = 4; +} + +message SubscriptionPermission { + bool all_participants = 1; + repeated TrackPermission track_permissions = 2; +} + +message SubscriptionPermissionUpdate { + string participant_sid = 1; + string track_sid = 2; + bool allowed = 3; +} + +message SyncState { + // last subscribe answer before reconnecting + SessionDescription answer = 1; + UpdateSubscription subscription = 2; + repeated TrackPublishedResponse publish_tracks = 3; + repeated DataChannelInfo data_channels = 4; + // last received server side offer before reconnecting + SessionDescription offer = 5; +} + +message DataChannelInfo { + string label = 1; + uint32 id = 2; + SignalTarget target = 3; +} + +enum CandidateProtocol { + UDP = 0; + TCP = 1; + TLS = 2; +} + +message SimulateScenario { + oneof scenario { + // simulate N seconds of speaker activity + int32 speaker_update = 1; + // simulate local node failure + bool node_failure = 2; + // simulate migration + bool migration = 3; + // server to send leave + bool server_leave = 4; + // switch candidate protocol to tcp + CandidateProtocol switch_candidate_protocol = 5; + } +} diff --git a/crates/live_kit_server/vendored/protocol/livekit_webhook.proto b/crates/live_kit_server/vendored/protocol/livekit_webhook.proto new file mode 100644 index 0000000000..7428748afd --- /dev/null +++ b/crates/live_kit_server/vendored/protocol/livekit_webhook.proto @@ -0,0 +1,36 @@ +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "livekit_models.proto"; +import "livekit_egress.proto"; +import "livekit_ingress.proto"; + +message WebhookEvent { + // one of room_started, room_finished, participant_joined, participant_left, + // track_published, track_unpublished, egress_started, egress_updated, egress_ended, ingress_started, ingress_ended + string event = 1; + + Room room = 2; + + // set when event is participant_* or track_* + ParticipantInfo participant = 3; + + // set when event is egress_* + EgressInfo egress_info = 9; + + // set when event is ingress_* + IngressInfo ingress_info = 10; + + // set when event is track_* + TrackInfo track = 8; + + // unique event uuid + string id = 6; + + // timestamp in seconds + int64 created_at = 7; +}