Vendor LiveKit protocol (#11672)

Marshall Bowers created

This PR vendors the protobuf files from the LiveKit protocol so that we
don't need to have that entire LiveKit protocol repo as a submodule.

---

Eventually I would like to replace this with the
[`livekit-protocol`](https://crates.io/crates/livekit-protocol) crate,
but there is some churn that needs to happen for that.

The main problem is that we're currently on a different version of
`prost` used by `livekit-protocol`, and upgrading our version of `prost`
means that we now need to source `protoc` ourselves (since it is no
longer available to be compiled from source as part of `prost-build`).

Release Notes:

- N/A

Change summary

.gitmodules                                                         |   3 
crates/live_kit_server/build.rs                                     |   5 
crates/live_kit_server/protocol                                     |   1 
crates/live_kit_server/vendored/protocol/README.md                  |   5 
crates/live_kit_server/vendored/protocol/livekit_analytics.proto    | 118 
crates/live_kit_server/vendored/protocol/livekit_egress.proto       | 263 
crates/live_kit_server/vendored/protocol/livekit_ingress.proto      | 134 
crates/live_kit_server/vendored/protocol/livekit_internal.proto     | 138 
crates/live_kit_server/vendored/protocol/livekit_models.proto       | 330 
crates/live_kit_server/vendored/protocol/livekit_room.proto         | 159 
crates/live_kit_server/vendored/protocol/livekit_rpc_internal.proto |  83 
crates/live_kit_server/vendored/protocol/livekit_rtc.proto          | 301 
crates/live_kit_server/vendored/protocol/livekit_webhook.proto      |  36 
13 files changed, 1,571 insertions(+), 5 deletions(-)

Detailed changes

.gitmodules 🔗

@@ -1,3 +0,0 @@
-[submodule "crates/live_kit_server/protocol"]
-	path = crates/live_kit_server/protocol
-	url = https://github.com/livekit/protocol

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();
 }

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).

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;
+}

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;
+}

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;
+}

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 {
+}

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<int32, uint32> 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;
+}

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;
+}

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;
+}

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;
+  }
+}

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;
+}