From d0979b5bdc357115861357edde6151c5bfa4c0fb Mon Sep 17 00:00:00 2001 From: Tommaso Barbugli Date: Tue, 11 Nov 2025 15:05:09 +0100 Subject: [PATCH 1/4] attempt number 1 to make python deps work --- .github/workflows/release.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 390163d2..a942708d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -36,8 +36,6 @@ jobs: - name: Sync environment & install dev extras run: | uv sync --all-packages --all-extras --dev --all-groups - uv remove twirp@git+https://github.com/tbarbugli/twirpy --group dev || true - uv add twirp || true - name: Build core distributions run: | From 28b001a844dfec2cce03bc6cb311dbdc2e6d24ab Mon Sep 17 00:00:00 2001 From: Tommaso Barbugli Date: Tue, 11 Nov 2025 16:13:01 +0100 Subject: [PATCH 2/4] use forked go generator for twirp --- generate_webrtc.sh | 30 ++-- .../pb/stream/video/sfu/event/events_pb2.py | 96 +++++------ .../pb/stream/video/sfu/event/events_pb2.pyi | 14 ++ .../pb/stream/video/sfu/models/models_pb2.py | 150 +++++++++--------- .../pb/stream/video/sfu/models/models_pb2.pyi | 97 +++++++++++ .../stream/video/sfu/signal_rpc/signal_pb2.py | 4 +- .../video/sfu/signal_rpc/signal_twirp.py | 66 ++++++-- .../video/rtc/twirp_async_client_embed.py | 82 ---------- getstream/video/rtc/twirp_client_wrapper.py | 6 - pyproject.toml | 2 +- uv.lock | 12 +- 11 files changed, 318 insertions(+), 241 deletions(-) delete mode 100644 getstream/video/rtc/twirp_async_client_embed.py diff --git a/generate_webrtc.sh b/generate_webrtc.sh index 22b2b70b..f77e87b2 100755 --- a/generate_webrtc.sh +++ b/generate_webrtc.sh @@ -43,25 +43,21 @@ fi echo "Installing Python dependencies..." uv sync --all-extras --dev --all-packages -# Install Twirp Python generator if not available -if ! command -v protoc-gen-twirpy &> /dev/null; then - echo "Installing protoc-gen-twirpy..." - - # Ensure Go tools are available for Twirp - if ! command -v go &> /dev/null; then - echo "go is not installed. Please install it to generate Twirp client code." - exit 1 - fi - - go install github.com/verloop/twirpy/protoc-gen-twirpy@latest - - # Ensure that Go path is in path otherwise protoc will not - if ! echo "$PATH" | grep -q "$(go env GOPATH)/bin"; then - echo "$(go env GOPATH)/bin is not in PATH" - exit 1 - fi +# Ensure Go tools are available for Twirp +if ! command -v go &> /dev/null; then + echo "go is not installed. Please install it to generate Twirp client code." + exit 1 fi +# Ensure that Go path is in path otherwise protoc will not +if ! echo "$PATH" | grep -q "$(go env GOPATH)/bin"; then + echo "$(go env GOPATH)/bin is not in PATH" + exit 1 +fi + +echo "Installing protoc-gen-twirpy..." +go install github.com/tbarbugli/twirpy/protoc-gen-twirpy@111deed + # Get the path to protoc PROTOC_PATH=$(command -v protoc) if [ -z "$PROTOC_PATH" ]; then diff --git a/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.py b/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.py index beb5de5b..e0e90424 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.py +++ b/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: video/sfu/event/events.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.28.2 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor @@ -12,7 +12,7 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, 6, 31, 1, "", "video/sfu/event/events.proto" + _runtime_version.Domain.PUBLIC, 5, 28, 2, "", "video/sfu/event/events.proto" ) # @@protoc_insertion_point(imports) @@ -28,7 +28,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1cvideo/sfu/event/events.proto\x12\x16stream.video.sfu.event\x1a\x1dvideo/sfu/models/models.proto\x1a!video/sfu/signal_rpc/signal.proto"\xfd\x0c\n\x08SfuEvent\x12\x43\n\x10subscriber_offer\x18\x01 \x01(\x0b\x32\'.stream.video.sfu.event.SubscriberOfferH\x00\x12\x43\n\x10publisher_answer\x18\x02 \x01(\x0b\x32\'.stream.video.sfu.event.PublisherAnswerH\x00\x12V\n\x1a\x63onnection_quality_changed\x18\x03 \x01(\x0b\x32\x30.stream.video.sfu.event.ConnectionQualityChangedH\x00\x12H\n\x13\x61udio_level_changed\x18\x04 \x01(\x0b\x32).stream.video.sfu.event.AudioLevelChangedH\x00\x12:\n\x0bice_trickle\x18\x05 \x01(\x0b\x32#.stream.video.sfu.models.ICETrickleH\x00\x12N\n\x16\x63hange_publish_quality\x18\x06 \x01(\x0b\x32,.stream.video.sfu.event.ChangePublishQualityH\x00\x12G\n\x12participant_joined\x18\n \x01(\x0b\x32).stream.video.sfu.event.ParticipantJoinedH\x00\x12\x43\n\x10participant_left\x18\x0b \x01(\x0b\x32\'.stream.video.sfu.event.ParticipantLeftH\x00\x12R\n\x18\x64ominant_speaker_changed\x18\x0c \x01(\x0b\x32..stream.video.sfu.event.DominantSpeakerChangedH\x00\x12=\n\rjoin_response\x18\r \x01(\x0b\x32$.stream.video.sfu.event.JoinResponseH\x00\x12L\n\x15health_check_response\x18\x0e \x01(\x0b\x32+.stream.video.sfu.event.HealthCheckResponseH\x00\x12\x41\n\x0ftrack_published\x18\x10 \x01(\x0b\x32&.stream.video.sfu.event.TrackPublishedH\x00\x12\x45\n\x11track_unpublished\x18\x11 \x01(\x0b\x32(.stream.video.sfu.event.TrackUnpublishedH\x00\x12.\n\x05\x65rror\x18\x12 \x01(\x0b\x32\x1d.stream.video.sfu.event.ErrorH\x00\x12H\n\x13\x63\x61ll_grants_updated\x18\x13 \x01(\x0b\x32).stream.video.sfu.event.CallGrantsUpdatedH\x00\x12\x31\n\x07go_away\x18\x14 \x01(\x0b\x32\x1e.stream.video.sfu.event.GoAwayH\x00\x12\x39\n\x0bice_restart\x18\x15 \x01(\x0b\x32".stream.video.sfu.event.ICERestartH\x00\x12;\n\x0cpins_updated\x18\x16 \x01(\x0b\x32#.stream.video.sfu.event.PinsChangedH\x00\x12\x37\n\ncall_ended\x18\x17 \x01(\x0b\x32!.stream.video.sfu.event.CallEndedH\x00\x12I\n\x13participant_updated\x18\x18 \x01(\x0b\x32*.stream.video.sfu.event.ParticipantUpdatedH\x00\x12^\n\x1eparticipant_migration_complete\x18\x19 \x01(\x0b\x32\x34.stream.video.sfu.event.ParticipantMigrationCompleteH\x00\x12N\n\x16\x63hange_publish_options\x18\x1b \x01(\x0b\x32,.stream.video.sfu.event.ChangePublishOptionsH\x00\x12V\n\x1ainbound_state_notification\x18\x1c \x01(\x0b\x32\x30.stream.video.sfu.event.InboundStateNotificationH\x00\x42\x0f\n\revent_payload"g\n\x14\x43hangePublishOptions\x12?\n\x0fpublish_options\x18\x01 \x03(\x0b\x32&.stream.video.sfu.models.PublishOption\x12\x0e\n\x06reason\x18\x02 \x01(\t"\x1e\n\x1c\x43hangePublishOptionsComplete"\x1e\n\x1cParticipantMigrationComplete"9\n\x0bPinsChanged\x12*\n\x04pins\x18\x01 \x03(\x0b\x32\x1c.stream.video.sfu.models.Pin"\x87\x01\n\x05\x45rror\x12-\n\x05\x65rror\x18\x04 \x01(\x0b\x32\x1e.stream.video.sfu.models.Error\x12O\n\x12reconnect_strategy\x18\x05 \x01(\x0e\x32\x33.stream.video.sfu.models.WebsocketReconnectStrategy"Y\n\nICETrickle\x12\x34\n\tpeer_type\x18\x01 \x01(\x0e\x32!.stream.video.sfu.models.PeerType\x12\x15\n\rice_candidate\x18\x02 \x01(\t"B\n\nICERestart\x12\x34\n\tpeer_type\x18\x01 \x01(\x0e\x32!.stream.video.sfu.models.PeerType"\xf0\x01\n\nSfuRequest\x12;\n\x0cjoin_request\x18\x01 \x01(\x0b\x32#.stream.video.sfu.event.JoinRequestH\x00\x12J\n\x14health_check_request\x18\x02 \x01(\x0b\x32*.stream.video.sfu.event.HealthCheckRequestH\x00\x12\x46\n\x12leave_call_request\x18\x03 \x01(\x0b\x32(.stream.video.sfu.event.LeaveCallRequestH\x00\x42\x11\n\x0frequest_payload"6\n\x10LeaveCallRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t"\x14\n\x12HealthCheckRequest"[\n\x13HealthCheckResponse\x12\x44\n\x11participant_count\x18\x01 \x01(\x0b\x32).stream.video.sfu.models.ParticipantCount"\xa2\x01\n\x0eTrackPublished\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x30\n\x04type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x39\n\x0bparticipant\x18\x04 \x01(\x0b\x32$.stream.video.sfu.models.Participant"\xe2\x01\n\x10TrackUnpublished\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x30\n\x04type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12<\n\x05\x63\x61use\x18\x04 \x01(\x0e\x32-.stream.video.sfu.models.TrackUnpublishReason\x12\x39\n\x0bparticipant\x18\x05 \x01(\x0b\x32$.stream.video.sfu.models.Participant"\x95\x04\n\x0bJoinRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x16\n\x0esubscriber_sdp\x18\x03 \x01(\t\x12\x15\n\rpublisher_sdp\x18\x08 \x01(\t\x12>\n\x0e\x63lient_details\x18\x04 \x01(\x0b\x32&.stream.video.sfu.models.ClientDetails\x12\x38\n\tmigration\x18\x05 \x01(\x0b\x32!.stream.video.sfu.event.MigrationB\x02\x18\x01\x12\x1a\n\x0e\x66\x61st_reconnect\x18\x06 \x01(\x08\x42\x02\x18\x01\x12\x43\n\x11reconnect_details\x18\x07 \x01(\x0b\x32(.stream.video.sfu.event.ReconnectDetails\x12I\n\x19preferred_publish_options\x18\t \x03(\x0b\x32&.stream.video.sfu.models.PublishOption\x12M\n\x1bpreferred_subscribe_options\x18\n \x03(\x0b\x32(.stream.video.sfu.models.SubscribeOption\x12?\n\x0c\x63\x61pabilities\x18\x0b \x03(\x0e\x32).stream.video.sfu.models.ClientCapability"\xbe\x02\n\x10ReconnectDetails\x12\x45\n\x08strategy\x18\x01 \x01(\x0e\x32\x33.stream.video.sfu.models.WebsocketReconnectStrategy\x12<\n\x10\x61nnounced_tracks\x18\x03 \x03(\x0b\x32".stream.video.sfu.models.TrackInfo\x12H\n\rsubscriptions\x18\x04 \x03(\x0b\x32\x31.stream.video.sfu.signal.TrackSubscriptionDetails\x12\x19\n\x11reconnect_attempt\x18\x05 \x01(\r\x12\x13\n\x0b\x66rom_sfu_id\x18\x06 \x01(\t\x12\x1b\n\x13previous_session_id\x18\x07 \x01(\t\x12\x0e\n\x06reason\x18\x08 \x01(\t"\xa8\x01\n\tMigration\x12\x13\n\x0b\x66rom_sfu_id\x18\x01 \x01(\t\x12<\n\x10\x61nnounced_tracks\x18\x02 \x03(\x0b\x32".stream.video.sfu.models.TrackInfo\x12H\n\rsubscriptions\x18\x03 \x03(\x0b\x32\x31.stream.video.sfu.signal.TrackSubscriptionDetails"\xc5\x01\n\x0cJoinResponse\x12\x36\n\ncall_state\x18\x01 \x01(\x0b\x32".stream.video.sfu.models.CallState\x12\x13\n\x0breconnected\x18\x02 \x01(\x08\x12\'\n\x1f\x66\x61st_reconnect_deadline_seconds\x18\x03 \x01(\x05\x12?\n\x0fpublish_options\x18\x04 \x03(\x0b\x32&.stream.video.sfu.models.PublishOption"`\n\x11ParticipantJoined\x12\x10\n\x08\x63\x61ll_cid\x18\x01 \x01(\t\x12\x39\n\x0bparticipant\x18\x02 \x01(\x0b\x32$.stream.video.sfu.models.Participant"^\n\x0fParticipantLeft\x12\x10\n\x08\x63\x61ll_cid\x18\x01 \x01(\t\x12\x39\n\x0bparticipant\x18\x02 \x01(\x0b\x32$.stream.video.sfu.models.Participant"a\n\x12ParticipantUpdated\x12\x10\n\x08\x63\x61ll_cid\x18\x01 \x01(\t\x12\x39\n\x0bparticipant\x18\x02 \x01(\x0b\x32$.stream.video.sfu.models.Participant"3\n\x0fSubscriberOffer\x12\x13\n\x0bice_restart\x18\x01 \x01(\x08\x12\x0b\n\x03sdp\x18\x02 \x01(\t"\x1e\n\x0fPublisherAnswer\x12\x0b\n\x03sdp\x18\x01 \x01(\t"m\n\x18\x43onnectionQualityChanged\x12Q\n\x1a\x63onnection_quality_updates\x18\x01 \x03(\x0b\x32-.stream.video.sfu.event.ConnectionQualityInfo"\x84\x01\n\x15\x43onnectionQualityInfo\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x46\n\x12\x63onnection_quality\x18\x03 \x01(\x0e\x32*.stream.video.sfu.models.ConnectionQuality"=\n\x16\x44ominantSpeakerChanged\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t"U\n\nAudioLevel\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\r\n\x05level\x18\x03 \x01(\x02\x12\x13\n\x0bis_speaking\x18\x04 \x01(\x08"M\n\x11\x41udioLevelChanged\x12\x38\n\x0c\x61udio_levels\x18\x01 \x03(\x0b\x32".stream.video.sfu.event.AudioLevel"\x8f\x01\n\x0b\x41udioSender\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x36\n\ntrack_type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x19\n\x11publish_option_id\x18\x04 \x01(\x05"\xc8\x01\n\x11VideoLayerSetting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x13\n\x0bmax_bitrate\x18\x03 \x01(\x05\x12 \n\x18scale_resolution_down_by\x18\x04 \x01(\x02\x12-\n\x05\x63odec\x18\x06 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x15\n\rmax_framerate\x18\x07 \x01(\r\x12\x18\n\x10scalability_mode\x18\x08 \x01(\t"\xca\x01\n\x0bVideoSender\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x39\n\x06layers\x18\x03 \x03(\x0b\x32).stream.video.sfu.event.VideoLayerSetting\x12\x36\n\ntrack_type\x18\x04 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x19\n\x11publish_option_id\x18\x05 \x01(\x05"\x8e\x01\n\x14\x43hangePublishQuality\x12:\n\raudio_senders\x18\x01 \x03(\x0b\x32#.stream.video.sfu.event.AudioSender\x12:\n\rvideo_senders\x18\x02 \x03(\x0b\x32#.stream.video.sfu.event.VideoSender"a\n\x11\x43\x61llGrantsUpdated\x12;\n\x0e\x63urrent_grants\x18\x01 \x01(\x0b\x32#.stream.video.sfu.models.CallGrants\x12\x0f\n\x07message\x18\x02 \x01(\t"?\n\x06GoAway\x12\x35\n\x06reason\x18\x01 \x01(\x0e\x32%.stream.video.sfu.models.GoAwayReason"E\n\tCallEnded\x12\x38\n\x06reason\x18\x01 \x01(\x0e\x32(.stream.video.sfu.models.CallEndedReason"c\n\x18InboundStateNotification\x12G\n\x14inbound_video_states\x18\x01 \x03(\x0b\x32).stream.video.sfu.event.InboundVideoState"\x80\x01\n\x11InboundVideoState\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x36\n\ntrack_type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x0e\n\x06paused\x18\x04 \x01(\x08\x42\x64\x42\x0bSfuEventsV1P\x01Z6github.com/GetStream/protocol/protobuf/video/sfu/event\xaa\x02\x1aStream.Video.v1.Sfu.Eventsb\x06proto3' + b'\n\x1cvideo/sfu/event/events.proto\x12\x16stream.video.sfu.event\x1a\x1dvideo/sfu/models/models.proto\x1a!video/sfu/signal_rpc/signal.proto"\xfd\x0c\n\x08SfuEvent\x12\x43\n\x10subscriber_offer\x18\x01 \x01(\x0b\x32\'.stream.video.sfu.event.SubscriberOfferH\x00\x12\x43\n\x10publisher_answer\x18\x02 \x01(\x0b\x32\'.stream.video.sfu.event.PublisherAnswerH\x00\x12V\n\x1a\x63onnection_quality_changed\x18\x03 \x01(\x0b\x32\x30.stream.video.sfu.event.ConnectionQualityChangedH\x00\x12H\n\x13\x61udio_level_changed\x18\x04 \x01(\x0b\x32).stream.video.sfu.event.AudioLevelChangedH\x00\x12:\n\x0bice_trickle\x18\x05 \x01(\x0b\x32#.stream.video.sfu.models.ICETrickleH\x00\x12N\n\x16\x63hange_publish_quality\x18\x06 \x01(\x0b\x32,.stream.video.sfu.event.ChangePublishQualityH\x00\x12G\n\x12participant_joined\x18\n \x01(\x0b\x32).stream.video.sfu.event.ParticipantJoinedH\x00\x12\x43\n\x10participant_left\x18\x0b \x01(\x0b\x32\'.stream.video.sfu.event.ParticipantLeftH\x00\x12R\n\x18\x64ominant_speaker_changed\x18\x0c \x01(\x0b\x32..stream.video.sfu.event.DominantSpeakerChangedH\x00\x12=\n\rjoin_response\x18\r \x01(\x0b\x32$.stream.video.sfu.event.JoinResponseH\x00\x12L\n\x15health_check_response\x18\x0e \x01(\x0b\x32+.stream.video.sfu.event.HealthCheckResponseH\x00\x12\x41\n\x0ftrack_published\x18\x10 \x01(\x0b\x32&.stream.video.sfu.event.TrackPublishedH\x00\x12\x45\n\x11track_unpublished\x18\x11 \x01(\x0b\x32(.stream.video.sfu.event.TrackUnpublishedH\x00\x12.\n\x05\x65rror\x18\x12 \x01(\x0b\x32\x1d.stream.video.sfu.event.ErrorH\x00\x12H\n\x13\x63\x61ll_grants_updated\x18\x13 \x01(\x0b\x32).stream.video.sfu.event.CallGrantsUpdatedH\x00\x12\x31\n\x07go_away\x18\x14 \x01(\x0b\x32\x1e.stream.video.sfu.event.GoAwayH\x00\x12\x39\n\x0bice_restart\x18\x15 \x01(\x0b\x32".stream.video.sfu.event.ICERestartH\x00\x12;\n\x0cpins_updated\x18\x16 \x01(\x0b\x32#.stream.video.sfu.event.PinsChangedH\x00\x12\x37\n\ncall_ended\x18\x17 \x01(\x0b\x32!.stream.video.sfu.event.CallEndedH\x00\x12I\n\x13participant_updated\x18\x18 \x01(\x0b\x32*.stream.video.sfu.event.ParticipantUpdatedH\x00\x12^\n\x1eparticipant_migration_complete\x18\x19 \x01(\x0b\x32\x34.stream.video.sfu.event.ParticipantMigrationCompleteH\x00\x12N\n\x16\x63hange_publish_options\x18\x1b \x01(\x0b\x32,.stream.video.sfu.event.ChangePublishOptionsH\x00\x12V\n\x1ainbound_state_notification\x18\x1c \x01(\x0b\x32\x30.stream.video.sfu.event.InboundStateNotificationH\x00\x42\x0f\n\revent_payload"g\n\x14\x43hangePublishOptions\x12?\n\x0fpublish_options\x18\x01 \x03(\x0b\x32&.stream.video.sfu.models.PublishOption\x12\x0e\n\x06reason\x18\x02 \x01(\t"\x1e\n\x1c\x43hangePublishOptionsComplete"\x1e\n\x1cParticipantMigrationComplete"9\n\x0bPinsChanged\x12*\n\x04pins\x18\x01 \x03(\x0b\x32\x1c.stream.video.sfu.models.Pin"\x87\x01\n\x05\x45rror\x12-\n\x05\x65rror\x18\x04 \x01(\x0b\x32\x1e.stream.video.sfu.models.Error\x12O\n\x12reconnect_strategy\x18\x05 \x01(\x0e\x32\x33.stream.video.sfu.models.WebsocketReconnectStrategy"Y\n\nICETrickle\x12\x34\n\tpeer_type\x18\x01 \x01(\x0e\x32!.stream.video.sfu.models.PeerType\x12\x15\n\rice_candidate\x18\x02 \x01(\t"B\n\nICERestart\x12\x34\n\tpeer_type\x18\x01 \x01(\x0e\x32!.stream.video.sfu.models.PeerType"\xf0\x01\n\nSfuRequest\x12;\n\x0cjoin_request\x18\x01 \x01(\x0b\x32#.stream.video.sfu.event.JoinRequestH\x00\x12J\n\x14health_check_request\x18\x02 \x01(\x0b\x32*.stream.video.sfu.event.HealthCheckRequestH\x00\x12\x46\n\x12leave_call_request\x18\x03 \x01(\x0b\x32(.stream.video.sfu.event.LeaveCallRequestH\x00\x42\x11\n\x0frequest_payload"6\n\x10LeaveCallRequest\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0e\n\x06reason\x18\x02 \x01(\t"\x14\n\x12HealthCheckRequest"[\n\x13HealthCheckResponse\x12\x44\n\x11participant_count\x18\x01 \x01(\x0b\x32).stream.video.sfu.models.ParticipantCount"\xa2\x01\n\x0eTrackPublished\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x30\n\x04type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x39\n\x0bparticipant\x18\x04 \x01(\x0b\x32$.stream.video.sfu.models.Participant"\xe2\x01\n\x10TrackUnpublished\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x30\n\x04type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12<\n\x05\x63\x61use\x18\x04 \x01(\x0e\x32-.stream.video.sfu.models.TrackUnpublishReason\x12\x39\n\x0bparticipant\x18\x05 \x01(\x0b\x32$.stream.video.sfu.models.Participant"\xed\x04\n\x0bJoinRequest\x12\r\n\x05token\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x1a\n\x12unified_session_id\x18\r \x01(\t\x12\x16\n\x0esubscriber_sdp\x18\x03 \x01(\t\x12\x15\n\rpublisher_sdp\x18\x08 \x01(\t\x12>\n\x0e\x63lient_details\x18\x04 \x01(\x0b\x32&.stream.video.sfu.models.ClientDetails\x12\x38\n\tmigration\x18\x05 \x01(\x0b\x32!.stream.video.sfu.event.MigrationB\x02\x18\x01\x12\x1a\n\x0e\x66\x61st_reconnect\x18\x06 \x01(\x08\x42\x02\x18\x01\x12\x43\n\x11reconnect_details\x18\x07 \x01(\x0b\x32(.stream.video.sfu.event.ReconnectDetails\x12I\n\x19preferred_publish_options\x18\t \x03(\x0b\x32&.stream.video.sfu.models.PublishOption\x12M\n\x1bpreferred_subscribe_options\x18\n \x03(\x0b\x32(.stream.video.sfu.models.SubscribeOption\x12?\n\x0c\x63\x61pabilities\x18\x0b \x03(\x0e\x32).stream.video.sfu.models.ClientCapability\x12:\n\x06source\x18\x0c \x01(\x0e\x32*.stream.video.sfu.models.ParticipantSource"\xbe\x02\n\x10ReconnectDetails\x12\x45\n\x08strategy\x18\x01 \x01(\x0e\x32\x33.stream.video.sfu.models.WebsocketReconnectStrategy\x12<\n\x10\x61nnounced_tracks\x18\x03 \x03(\x0b\x32".stream.video.sfu.models.TrackInfo\x12H\n\rsubscriptions\x18\x04 \x03(\x0b\x32\x31.stream.video.sfu.signal.TrackSubscriptionDetails\x12\x19\n\x11reconnect_attempt\x18\x05 \x01(\r\x12\x13\n\x0b\x66rom_sfu_id\x18\x06 \x01(\t\x12\x1b\n\x13previous_session_id\x18\x07 \x01(\t\x12\x0e\n\x06reason\x18\x08 \x01(\t"\xa8\x01\n\tMigration\x12\x13\n\x0b\x66rom_sfu_id\x18\x01 \x01(\t\x12<\n\x10\x61nnounced_tracks\x18\x02 \x03(\x0b\x32".stream.video.sfu.models.TrackInfo\x12H\n\rsubscriptions\x18\x03 \x03(\x0b\x32\x31.stream.video.sfu.signal.TrackSubscriptionDetails"\xc5\x01\n\x0cJoinResponse\x12\x36\n\ncall_state\x18\x01 \x01(\x0b\x32".stream.video.sfu.models.CallState\x12\x13\n\x0breconnected\x18\x02 \x01(\x08\x12\'\n\x1f\x66\x61st_reconnect_deadline_seconds\x18\x03 \x01(\x05\x12?\n\x0fpublish_options\x18\x04 \x03(\x0b\x32&.stream.video.sfu.models.PublishOption"`\n\x11ParticipantJoined\x12\x10\n\x08\x63\x61ll_cid\x18\x01 \x01(\t\x12\x39\n\x0bparticipant\x18\x02 \x01(\x0b\x32$.stream.video.sfu.models.Participant"^\n\x0fParticipantLeft\x12\x10\n\x08\x63\x61ll_cid\x18\x01 \x01(\t\x12\x39\n\x0bparticipant\x18\x02 \x01(\x0b\x32$.stream.video.sfu.models.Participant"a\n\x12ParticipantUpdated\x12\x10\n\x08\x63\x61ll_cid\x18\x01 \x01(\t\x12\x39\n\x0bparticipant\x18\x02 \x01(\x0b\x32$.stream.video.sfu.models.Participant"3\n\x0fSubscriberOffer\x12\x13\n\x0bice_restart\x18\x01 \x01(\x08\x12\x0b\n\x03sdp\x18\x02 \x01(\t"\x1e\n\x0fPublisherAnswer\x12\x0b\n\x03sdp\x18\x01 \x01(\t"m\n\x18\x43onnectionQualityChanged\x12Q\n\x1a\x63onnection_quality_updates\x18\x01 \x03(\x0b\x32-.stream.video.sfu.event.ConnectionQualityInfo"\x84\x01\n\x15\x43onnectionQualityInfo\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x46\n\x12\x63onnection_quality\x18\x03 \x01(\x0e\x32*.stream.video.sfu.models.ConnectionQuality"=\n\x16\x44ominantSpeakerChanged\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t"U\n\nAudioLevel\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\r\n\x05level\x18\x03 \x01(\x02\x12\x13\n\x0bis_speaking\x18\x04 \x01(\x08"M\n\x11\x41udioLevelChanged\x12\x38\n\x0c\x61udio_levels\x18\x01 \x03(\x0b\x32".stream.video.sfu.event.AudioLevel"\x8f\x01\n\x0b\x41udioSender\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x36\n\ntrack_type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x19\n\x11publish_option_id\x18\x04 \x01(\x05"\xc8\x01\n\x11VideoLayerSetting\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x61\x63tive\x18\x02 \x01(\x08\x12\x13\n\x0bmax_bitrate\x18\x03 \x01(\x05\x12 \n\x18scale_resolution_down_by\x18\x04 \x01(\x02\x12-\n\x05\x63odec\x18\x06 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x15\n\rmax_framerate\x18\x07 \x01(\r\x12\x18\n\x10scalability_mode\x18\x08 \x01(\t"\xca\x01\n\x0bVideoSender\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x39\n\x06layers\x18\x03 \x03(\x0b\x32).stream.video.sfu.event.VideoLayerSetting\x12\x36\n\ntrack_type\x18\x04 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x19\n\x11publish_option_id\x18\x05 \x01(\x05"\x8e\x01\n\x14\x43hangePublishQuality\x12:\n\raudio_senders\x18\x01 \x03(\x0b\x32#.stream.video.sfu.event.AudioSender\x12:\n\rvideo_senders\x18\x02 \x03(\x0b\x32#.stream.video.sfu.event.VideoSender"a\n\x11\x43\x61llGrantsUpdated\x12;\n\x0e\x63urrent_grants\x18\x01 \x01(\x0b\x32#.stream.video.sfu.models.CallGrants\x12\x0f\n\x07message\x18\x02 \x01(\t"?\n\x06GoAway\x12\x35\n\x06reason\x18\x01 \x01(\x0e\x32%.stream.video.sfu.models.GoAwayReason"E\n\tCallEnded\x12\x38\n\x06reason\x18\x01 \x01(\x0e\x32(.stream.video.sfu.models.CallEndedReason"c\n\x18InboundStateNotification\x12G\n\x14inbound_video_states\x18\x01 \x03(\x0b\x32).stream.video.sfu.event.InboundVideoState"\x80\x01\n\x11InboundVideoState\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12\x36\n\ntrack_type\x18\x03 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x0e\n\x06paused\x18\x04 \x01(\x08\x42\x64\x42\x0bSfuEventsV1P\x01Z6github.com/GetStream/protocol/protobuf/video/sfu/event\xaa\x02\x1aStream.Video.v1.Sfu.Eventsb\x06proto3' ) _globals = globals() @@ -78,49 +78,49 @@ _globals["_TRACKUNPUBLISHED"]._serialized_start = 2891 _globals["_TRACKUNPUBLISHED"]._serialized_end = 3117 _globals["_JOINREQUEST"]._serialized_start = 3120 - _globals["_JOINREQUEST"]._serialized_end = 3653 - _globals["_RECONNECTDETAILS"]._serialized_start = 3656 - _globals["_RECONNECTDETAILS"]._serialized_end = 3974 - _globals["_MIGRATION"]._serialized_start = 3977 - _globals["_MIGRATION"]._serialized_end = 4145 - _globals["_JOINRESPONSE"]._serialized_start = 4148 - _globals["_JOINRESPONSE"]._serialized_end = 4345 - _globals["_PARTICIPANTJOINED"]._serialized_start = 4347 - _globals["_PARTICIPANTJOINED"]._serialized_end = 4443 - _globals["_PARTICIPANTLEFT"]._serialized_start = 4445 - _globals["_PARTICIPANTLEFT"]._serialized_end = 4539 - _globals["_PARTICIPANTUPDATED"]._serialized_start = 4541 - _globals["_PARTICIPANTUPDATED"]._serialized_end = 4638 - _globals["_SUBSCRIBEROFFER"]._serialized_start = 4640 - _globals["_SUBSCRIBEROFFER"]._serialized_end = 4691 - _globals["_PUBLISHERANSWER"]._serialized_start = 4693 - _globals["_PUBLISHERANSWER"]._serialized_end = 4723 - _globals["_CONNECTIONQUALITYCHANGED"]._serialized_start = 4725 - _globals["_CONNECTIONQUALITYCHANGED"]._serialized_end = 4834 - _globals["_CONNECTIONQUALITYINFO"]._serialized_start = 4837 - _globals["_CONNECTIONQUALITYINFO"]._serialized_end = 4969 - _globals["_DOMINANTSPEAKERCHANGED"]._serialized_start = 4971 - _globals["_DOMINANTSPEAKERCHANGED"]._serialized_end = 5032 - _globals["_AUDIOLEVEL"]._serialized_start = 5034 - _globals["_AUDIOLEVEL"]._serialized_end = 5119 - _globals["_AUDIOLEVELCHANGED"]._serialized_start = 5121 - _globals["_AUDIOLEVELCHANGED"]._serialized_end = 5198 - _globals["_AUDIOSENDER"]._serialized_start = 5201 - _globals["_AUDIOSENDER"]._serialized_end = 5344 - _globals["_VIDEOLAYERSETTING"]._serialized_start = 5347 - _globals["_VIDEOLAYERSETTING"]._serialized_end = 5547 - _globals["_VIDEOSENDER"]._serialized_start = 5550 - _globals["_VIDEOSENDER"]._serialized_end = 5752 - _globals["_CHANGEPUBLISHQUALITY"]._serialized_start = 5755 - _globals["_CHANGEPUBLISHQUALITY"]._serialized_end = 5897 - _globals["_CALLGRANTSUPDATED"]._serialized_start = 5899 - _globals["_CALLGRANTSUPDATED"]._serialized_end = 5996 - _globals["_GOAWAY"]._serialized_start = 5998 - _globals["_GOAWAY"]._serialized_end = 6061 - _globals["_CALLENDED"]._serialized_start = 6063 - _globals["_CALLENDED"]._serialized_end = 6132 - _globals["_INBOUNDSTATENOTIFICATION"]._serialized_start = 6134 - _globals["_INBOUNDSTATENOTIFICATION"]._serialized_end = 6233 - _globals["_INBOUNDVIDEOSTATE"]._serialized_start = 6236 - _globals["_INBOUNDVIDEOSTATE"]._serialized_end = 6364 + _globals["_JOINREQUEST"]._serialized_end = 3741 + _globals["_RECONNECTDETAILS"]._serialized_start = 3744 + _globals["_RECONNECTDETAILS"]._serialized_end = 4062 + _globals["_MIGRATION"]._serialized_start = 4065 + _globals["_MIGRATION"]._serialized_end = 4233 + _globals["_JOINRESPONSE"]._serialized_start = 4236 + _globals["_JOINRESPONSE"]._serialized_end = 4433 + _globals["_PARTICIPANTJOINED"]._serialized_start = 4435 + _globals["_PARTICIPANTJOINED"]._serialized_end = 4531 + _globals["_PARTICIPANTLEFT"]._serialized_start = 4533 + _globals["_PARTICIPANTLEFT"]._serialized_end = 4627 + _globals["_PARTICIPANTUPDATED"]._serialized_start = 4629 + _globals["_PARTICIPANTUPDATED"]._serialized_end = 4726 + _globals["_SUBSCRIBEROFFER"]._serialized_start = 4728 + _globals["_SUBSCRIBEROFFER"]._serialized_end = 4779 + _globals["_PUBLISHERANSWER"]._serialized_start = 4781 + _globals["_PUBLISHERANSWER"]._serialized_end = 4811 + _globals["_CONNECTIONQUALITYCHANGED"]._serialized_start = 4813 + _globals["_CONNECTIONQUALITYCHANGED"]._serialized_end = 4922 + _globals["_CONNECTIONQUALITYINFO"]._serialized_start = 4925 + _globals["_CONNECTIONQUALITYINFO"]._serialized_end = 5057 + _globals["_DOMINANTSPEAKERCHANGED"]._serialized_start = 5059 + _globals["_DOMINANTSPEAKERCHANGED"]._serialized_end = 5120 + _globals["_AUDIOLEVEL"]._serialized_start = 5122 + _globals["_AUDIOLEVEL"]._serialized_end = 5207 + _globals["_AUDIOLEVELCHANGED"]._serialized_start = 5209 + _globals["_AUDIOLEVELCHANGED"]._serialized_end = 5286 + _globals["_AUDIOSENDER"]._serialized_start = 5289 + _globals["_AUDIOSENDER"]._serialized_end = 5432 + _globals["_VIDEOLAYERSETTING"]._serialized_start = 5435 + _globals["_VIDEOLAYERSETTING"]._serialized_end = 5635 + _globals["_VIDEOSENDER"]._serialized_start = 5638 + _globals["_VIDEOSENDER"]._serialized_end = 5840 + _globals["_CHANGEPUBLISHQUALITY"]._serialized_start = 5843 + _globals["_CHANGEPUBLISHQUALITY"]._serialized_end = 5985 + _globals["_CALLGRANTSUPDATED"]._serialized_start = 5987 + _globals["_CALLGRANTSUPDATED"]._serialized_end = 6084 + _globals["_GOAWAY"]._serialized_start = 6086 + _globals["_GOAWAY"]._serialized_end = 6149 + _globals["_CALLENDED"]._serialized_start = 6151 + _globals["_CALLENDED"]._serialized_end = 6220 + _globals["_INBOUNDSTATENOTIFICATION"]._serialized_start = 6222 + _globals["_INBOUNDSTATENOTIFICATION"]._serialized_end = 6321 + _globals["_INBOUNDVIDEOSTATE"]._serialized_start = 6324 + _globals["_INBOUNDVIDEOSTATE"]._serialized_end = 6452 # @@protoc_insertion_point(module_scope) diff --git a/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.pyi b/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.pyi index 212ef99f..24a60dee 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.pyi +++ b/getstream/video/rtc/pb/stream/video/sfu/event/events_pb2.pyi @@ -689,6 +689,7 @@ class JoinRequest(google.protobuf.message.Message): TOKEN_FIELD_NUMBER: builtins.int SESSION_ID_FIELD_NUMBER: builtins.int + UNIFIED_SESSION_ID_FIELD_NUMBER: builtins.int SUBSCRIBER_SDP_FIELD_NUMBER: builtins.int PUBLISHER_SDP_FIELD_NUMBER: builtins.int CLIENT_DETAILS_FIELD_NUMBER: builtins.int @@ -698,8 +699,14 @@ class JoinRequest(google.protobuf.message.Message): PREFERRED_PUBLISH_OPTIONS_FIELD_NUMBER: builtins.int PREFERRED_SUBSCRIBE_OPTIONS_FIELD_NUMBER: builtins.int CAPABILITIES_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int token: builtins.str session_id: builtins.str + unified_session_id: builtins.str + """user_session id can change during reconnects, this helps us to + identify the user across reconnects and should remain consistent until the user explicitly + disconnects, is kicked or the call is ended. + """ subscriber_sdp: builtins.str """dumb SDP that allow us to extract subscriber's decode codecs""" publisher_sdp: builtins.str @@ -739,11 +746,13 @@ class JoinRequest(google.protobuf.message.Message): ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ video.sfu.models.models_pb2.ClientCapability.ValueType ]: ... + source: video.sfu.models.models_pb2.ParticipantSource.ValueType def __init__( self, *, token: builtins.str = ..., session_id: builtins.str = ..., + unified_session_id: builtins.str = ..., subscriber_sdp: builtins.str = ..., publisher_sdp: builtins.str = ..., client_details: video.sfu.models.models_pb2.ClientDetails | None = ..., @@ -762,6 +771,7 @@ class JoinRequest(google.protobuf.message.Message): video.sfu.models.models_pb2.ClientCapability.ValueType ] | None = ..., + source: video.sfu.models.models_pb2.ParticipantSource.ValueType = ..., ) -> None: ... def HasField( self, @@ -795,10 +805,14 @@ class JoinRequest(google.protobuf.message.Message): b"reconnect_details", "session_id", b"session_id", + "source", + b"source", "subscriber_sdp", b"subscriber_sdp", "token", b"token", + "unified_session_id", + b"unified_session_id", ], ) -> None: ... diff --git a/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.py b/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.py index c104e1eb..609e7c09 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.py +++ b/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: video/sfu/models/models.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.28.2 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor @@ -12,7 +12,7 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, 6, 31, 1, "", "video/sfu/models/models.proto" + _runtime_version.Domain.PUBLIC, 5, 28, 2, "", "video/sfu/models/models.proto" ) # @@protoc_insertion_point(imports) @@ -24,7 +24,7 @@ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( - b'\n\x1dvideo/sfu/models/models.proto\x12\x17stream.video.sfu.models\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xe9\x01\n\tCallState\x12:\n\x0cparticipants\x18\x01 \x03(\x0b\x32$.stream.video.sfu.models.Participant\x12.\n\nstarted_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x44\n\x11participant_count\x18\x03 \x01(\x0b\x32).stream.video.sfu.models.ParticipantCount\x12*\n\x04pins\x18\x04 \x03(\x0b\x32\x1c.stream.video.sfu.models.Pin"4\n\x10ParticipantCount\x12\r\n\x05total\x18\x01 \x01(\r\x12\x11\n\tanonymous\x18\x02 \x01(\r"*\n\x03Pin\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t"\xa0\x03\n\x0bParticipant\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12<\n\x10published_tracks\x18\x03 \x03(\x0e\x32".stream.video.sfu.models.TrackType\x12-\n\tjoined_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1b\n\x13track_lookup_prefix\x18\x05 \x01(\t\x12\x46\n\x12\x63onnection_quality\x18\x06 \x01(\x0e\x32*.stream.video.sfu.models.ConnectionQuality\x12\x13\n\x0bis_speaking\x18\x07 \x01(\x08\x12\x1b\n\x13is_dominant_speaker\x18\x08 \x01(\x08\x12\x13\n\x0b\x61udio_level\x18\t \x01(\x02\x12\x0c\n\x04name\x18\n \x01(\t\x12\r\n\x05image\x18\x0b \x01(\t\x12\'\n\x06\x63ustom\x18\x0c \x01(\x0b\x32\x17.google.protobuf.Struct\x12\r\n\x05roles\x18\r \x03(\t"^\n\rStreamQuality\x12<\n\rvideo_quality\x18\x01 \x01(\x0e\x32%.stream.video.sfu.models.VideoQuality\x12\x0f\n\x07user_id\x18\x02 \x01(\t"/\n\x0eVideoDimension\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r"\xb1\x01\n\nVideoLayer\x12\x0b\n\x03rid\x18\x01 \x01(\t\x12@\n\x0fvideo_dimension\x18\x02 \x01(\x0b\x32\'.stream.video.sfu.models.VideoDimension\x12\x0f\n\x07\x62itrate\x18\x04 \x01(\r\x12\x0b\n\x03\x66ps\x18\x05 \x01(\r\x12\x36\n\x07quality\x18\x06 \x01(\x0e\x32%.stream.video.sfu.models.VideoQuality"y\n\x0fSubscribeOption\x12\x36\n\ntrack_type\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12.\n\x06\x63odecs\x18\x02 \x03(\x0b\x32\x1e.stream.video.sfu.models.Codec"\xb5\x02\n\rPublishOption\x12\x36\n\ntrack_type\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x0f\n\x07\x62itrate\x18\x03 \x01(\x05\x12\x0b\n\x03\x66ps\x18\x04 \x01(\x05\x12\x1a\n\x12max_spatial_layers\x18\x05 \x01(\x05\x12\x1b\n\x13max_temporal_layers\x18\x06 \x01(\x05\x12@\n\x0fvideo_dimension\x18\x07 \x01(\x0b\x32\'.stream.video.sfu.models.VideoDimension\x12\n\n\x02id\x18\x08 \x01(\x05\x12\x18\n\x10use_single_layer\x18\t \x01(\x08"j\n\x05\x43odec\x12\x14\n\x0cpayload_type\x18\x10 \x01(\r\x12\x0c\n\x04name\x18\n \x01(\t\x12\x12\n\nclock_rate\x18\x0e \x01(\r\x12\x1b\n\x13\x65ncoding_parameters\x18\x0f \x01(\t\x12\x0c\n\x04\x66mtp\x18\x0c \x01(\t"m\n\nICETrickle\x12\x34\n\tpeer_type\x18\x01 \x01(\x0e\x32!.stream.video.sfu.models.PeerType\x12\x15\n\rice_candidate\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t"\x9a\x02\n\tTrackInfo\x12\x10\n\x08track_id\x18\x01 \x01(\t\x12\x36\n\ntrack_type\x18\x02 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x33\n\x06layers\x18\x05 \x03(\x0b\x32#.stream.video.sfu.models.VideoLayer\x12\x0b\n\x03mid\x18\x06 \x01(\t\x12\x0b\n\x03\x64tx\x18\x07 \x01(\x08\x12\x0e\n\x06stereo\x18\x08 \x01(\x08\x12\x0b\n\x03red\x18\t \x01(\x08\x12\r\n\x05muted\x18\n \x01(\x08\x12-\n\x05\x63odec\x18\x0b \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x19\n\x11publish_option_id\x18\x0c \x01(\x05"`\n\x05\x45rror\x12\x30\n\x04\x63ode\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.ErrorCode\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x14\n\x0cshould_retry\x18\x03 \x01(\x08"\xc7\x01\n\rClientDetails\x12)\n\x03sdk\x18\x01 \x01(\x0b\x32\x1c.stream.video.sfu.models.Sdk\x12\'\n\x02os\x18\x02 \x01(\x0b\x32\x1b.stream.video.sfu.models.OS\x12\x31\n\x07\x62rowser\x18\x03 \x01(\x0b\x32 .stream.video.sfu.models.Browser\x12/\n\x06\x64\x65vice\x18\x04 \x01(\x0b\x32\x1f.stream.video.sfu.models.Device"b\n\x03Sdk\x12.\n\x04type\x18\x01 \x01(\x0e\x32 .stream.video.sfu.models.SdkType\x12\r\n\x05major\x18\x02 \x01(\t\x12\r\n\x05minor\x18\x03 \x01(\t\x12\r\n\x05patch\x18\x04 \x01(\t"9\n\x02OS\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x14\n\x0c\x61rchitecture\x18\x03 \x01(\t"(\n\x07\x42rowser\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"\x89\x01\n\x0bRTMPIngress\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\x12\x12\n\nframe_rate\x18\x03 \x01(\x01\x12\x10\n\x08software\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x0f\n\x07\x65ncoder\x18\x06 \x01(\t\x12\x13\n\x0bremote_addr\x18\x07 \x01(\t"\'\n\x06\x44\x65vice\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"\xdb\x01\n\x04\x43\x61ll\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\x1a\n\x12\x63reated_by_user_id\x18\x03 \x01(\t\x12\x14\n\x0chost_user_id\x18\x04 \x01(\t\x12\'\n\x06\x63ustom\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\ncreated_at\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"[\n\nCallGrants\x12\x19\n\x11\x63\x61n_publish_audio\x18\x01 \x01(\x08\x12\x19\n\x11\x63\x61n_publish_video\x18\x02 \x01(\x08\x12\x17\n\x0f\x63\x61n_screenshare\x18\x03 \x01(\x08"W\n\x0cInputDevices\x12\x19\n\x11\x61vailable_devices\x18\x01 \x03(\t\x12\x16\n\x0e\x63urrent_device\x18\x02 \x01(\t\x12\x14\n\x0cis_permitted\x18\x03 \x01(\x08"p\n\x0c\x41ndroidState\x12\x43\n\rthermal_state\x18\x01 \x01(\x0e\x32,.stream.video.sfu.models.AndroidThermalState\x12\x1b\n\x13is_power_saver_mode\x18\x02 \x01(\x08"r\n\nAppleState\x12\x41\n\rthermal_state\x18\x01 \x01(\x0e\x32*.stream.video.sfu.models.AppleThermalState\x12!\n\x19is_low_power_mode_enabled\x18\x02 \x01(\x08"\xff\x01\n\x10PerformanceStats\x12\x36\n\ntrack_type\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x19\n\x11\x61vg_frame_time_ms\x18\x03 \x01(\x02\x12\x0f\n\x07\x61vg_fps\x18\x04 \x01(\x02\x12@\n\x0fvideo_dimension\x18\x05 \x01(\x0b\x32\'.stream.video.sfu.models.VideoDimension\x12\x16\n\x0etarget_bitrate\x18\x06 \x01(\x05*I\n\x08PeerType\x12#\n\x1fPEER_TYPE_PUBLISHER_UNSPECIFIED\x10\x00\x12\x18\n\x14PEER_TYPE_SUBSCRIBER\x10\x01*\x93\x01\n\x11\x43onnectionQuality\x12"\n\x1e\x43ONNECTION_QUALITY_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x43ONNECTION_QUALITY_POOR\x10\x01\x12\x1b\n\x17\x43ONNECTION_QUALITY_GOOD\x10\x02\x12 \n\x1c\x43ONNECTION_QUALITY_EXCELLENT\x10\x03*w\n\x0cVideoQuality\x12!\n\x1dVIDEO_QUALITY_LOW_UNSPECIFIED\x10\x00\x12\x15\n\x11VIDEO_QUALITY_MID\x10\x01\x12\x16\n\x12VIDEO_QUALITY_HIGH\x10\x02\x12\x15\n\x11VIDEO_QUALITY_OFF\x10\x03*\x93\x01\n\tTrackType\x12\x1a\n\x16TRACK_TYPE_UNSPECIFIED\x10\x00\x12\x14\n\x10TRACK_TYPE_AUDIO\x10\x01\x12\x14\n\x10TRACK_TYPE_VIDEO\x10\x02\x12\x1b\n\x17TRACK_TYPE_SCREEN_SHARE\x10\x03\x12!\n\x1dTRACK_TYPE_SCREEN_SHARE_AUDIO\x10\x04*\xea\x06\n\tErrorCode\x12\x1a\n\x16\x45RROR_CODE_UNSPECIFIED\x10\x00\x12&\n"ERROR_CODE_PUBLISH_TRACK_NOT_FOUND\x10\x64\x12&\n"ERROR_CODE_PUBLISH_TRACKS_MISMATCH\x10\x65\x12)\n%ERROR_CODE_PUBLISH_TRACK_OUT_OF_ORDER\x10\x66\x12\x32\n.ERROR_CODE_PUBLISH_TRACK_VIDEO_LAYER_NOT_FOUND\x10g\x12\x19\n\x15\x45RROR_CODE_LIVE_ENDED\x10h\x12%\n ERROR_CODE_PARTICIPANT_NOT_FOUND\x10\xc8\x01\x12)\n$ERROR_CODE_PARTICIPANT_MIGRATING_OUT\x10\xc9\x01\x12,\n\'ERROR_CODE_PARTICIPANT_MIGRATION_FAILED\x10\xca\x01\x12%\n ERROR_CODE_PARTICIPANT_MIGRATING\x10\xcb\x01\x12,\n\'ERROR_CODE_PARTICIPANT_RECONNECT_FAILED\x10\xcc\x01\x12\x33\n.ERROR_CODE_PARTICIPANT_MEDIA_TRANSPORT_FAILURE\x10\xcd\x01\x12\'\n"ERROR_CODE_PARTICIPANT_SIGNAL_LOST\x10\xce\x01\x12\x1e\n\x19\x45RROR_CODE_CALL_NOT_FOUND\x10\xac\x02\x12.\n)ERROR_CODE_CALL_PARTICIPANT_LIMIT_REACHED\x10\xad\x02\x12)\n$ERROR_CODE_REQUEST_VALIDATION_FAILED\x10\x90\x03\x12\x1f\n\x1a\x45RROR_CODE_UNAUTHENTICATED\x10\x91\x03\x12!\n\x1c\x45RROR_CODE_PERMISSION_DENIED\x10\x93\x03\x12!\n\x1c\x45RROR_CODE_TOO_MANY_REQUESTS\x10\xad\x03\x12%\n ERROR_CODE_INTERNAL_SERVER_ERROR\x10\xf4\x03\x12!\n\x1c\x45RROR_CODE_SFU_SHUTTING_DOWN\x10\xd8\x04\x12\x18\n\x13\x45RROR_CODE_SFU_FULL\x10\xbc\x05*\xea\x01\n\x07SdkType\x12\x18\n\x14SDK_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSDK_TYPE_REACT\x10\x01\x12\x14\n\x10SDK_TYPE_ANGULAR\x10\x02\x12\x14\n\x10SDK_TYPE_ANDROID\x10\x03\x12\x10\n\x0cSDK_TYPE_IOS\x10\x04\x12\x14\n\x10SDK_TYPE_FLUTTER\x10\x05\x12\x19\n\x15SDK_TYPE_REACT_NATIVE\x10\x06\x12\x12\n\x0eSDK_TYPE_UNITY\x10\x07\x12\x0f\n\x0bSDK_TYPE_GO\x10\x08\x12\x1d\n\x19SDK_TYPE_PLAIN_JAVASCRIPT\x10\t*\xbb\x01\n\x14TrackUnpublishReason\x12&\n"TRACK_UNPUBLISH_REASON_UNSPECIFIED\x10\x00\x12%\n!TRACK_UNPUBLISH_REASON_USER_MUTED\x10\x01\x12-\n)TRACK_UNPUBLISH_REASON_PERMISSION_REVOKED\x10\x02\x12%\n!TRACK_UNPUBLISH_REASON_MODERATION\x10\x03*n\n\x0cGoAwayReason\x12\x1e\n\x1aGO_AWAY_REASON_UNSPECIFIED\x10\x00\x12 \n\x1cGO_AWAY_REASON_SHUTTING_DOWN\x10\x01\x12\x1c\n\x18GO_AWAY_REASON_REBALANCE\x10\x02*\xb6\x01\n\x0f\x43\x61llEndedReason\x12!\n\x1d\x43\x41LL_ENDED_REASON_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x43\x41LL_ENDED_REASON_ENDED\x10\x01\x12 \n\x1c\x43\x41LL_ENDED_REASON_LIVE_ENDED\x10\x02\x12\x1c\n\x18\x43\x41LL_ENDED_REASON_KICKED\x10\x03\x12#\n\x1f\x43\x41LL_ENDED_REASON_SESSION_ENDED\x10\x04*\xf1\x01\n\x1aWebsocketReconnectStrategy\x12,\n(WEBSOCKET_RECONNECT_STRATEGY_UNSPECIFIED\x10\x00\x12+\n\'WEBSOCKET_RECONNECT_STRATEGY_DISCONNECT\x10\x01\x12%\n!WEBSOCKET_RECONNECT_STRATEGY_FAST\x10\x02\x12\'\n#WEBSOCKET_RECONNECT_STRATEGY_REJOIN\x10\x03\x12(\n$WEBSOCKET_RECONNECT_STRATEGY_MIGRATE\x10\x04*\xb0\x02\n\x13\x41ndroidThermalState\x12%\n!ANDROID_THERMAL_STATE_UNSPECIFIED\x10\x00\x12\x1e\n\x1a\x41NDROID_THERMAL_STATE_NONE\x10\x01\x12\x1f\n\x1b\x41NDROID_THERMAL_STATE_LIGHT\x10\x02\x12"\n\x1e\x41NDROID_THERMAL_STATE_MODERATE\x10\x03\x12 \n\x1c\x41NDROID_THERMAL_STATE_SEVERE\x10\x04\x12"\n\x1e\x41NDROID_THERMAL_STATE_CRITICAL\x10\x05\x12#\n\x1f\x41NDROID_THERMAL_STATE_EMERGENCY\x10\x06\x12"\n\x1e\x41NDROID_THERMAL_STATE_SHUTDOWN\x10\x07*\xba\x01\n\x11\x41ppleThermalState\x12#\n\x1f\x41PPLE_THERMAL_STATE_UNSPECIFIED\x10\x00\x12\x1f\n\x1b\x41PPLE_THERMAL_STATE_NOMINAL\x10\x01\x12\x1c\n\x18\x41PPLE_THERMAL_STATE_FAIR\x10\x02\x12\x1f\n\x1b\x41PPLE_THERMAL_STATE_SERIOUS\x10\x03\x12 \n\x1c\x41PPLE_THERMAL_STATE_CRITICAL\x10\x04*c\n\x10\x43lientCapability\x12!\n\x1d\x43LIENT_CAPABILITY_UNSPECIFIED\x10\x00\x12,\n(CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE\x10\x01\x42\x65\x42\x0bSfuModelsV1P\x01Z7github.com/GetStream/protocol/protobuf/video/sfu/models\xaa\x02\x1aStream.Video.v1.Sfu.Modelsb\x06proto3' + b'\n\x1dvideo/sfu/models/models.proto\x12\x17stream.video.sfu.models\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xe9\x01\n\tCallState\x12:\n\x0cparticipants\x18\x01 \x03(\x0b\x32$.stream.video.sfu.models.Participant\x12.\n\nstarted_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x44\n\x11participant_count\x18\x03 \x01(\x0b\x32).stream.video.sfu.models.ParticipantCount\x12*\n\x04pins\x18\x04 \x03(\x0b\x32\x1c.stream.video.sfu.models.Pin"4\n\x10ParticipantCount\x12\r\n\x05total\x18\x01 \x01(\r\x12\x11\n\tanonymous\x18\x02 \x01(\r"*\n\x03Pin\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t"\xdc\x03\n\x0bParticipant\x12\x0f\n\x07user_id\x18\x01 \x01(\t\x12\x12\n\nsession_id\x18\x02 \x01(\t\x12<\n\x10published_tracks\x18\x03 \x03(\x0e\x32".stream.video.sfu.models.TrackType\x12-\n\tjoined_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1b\n\x13track_lookup_prefix\x18\x05 \x01(\t\x12\x46\n\x12\x63onnection_quality\x18\x06 \x01(\x0e\x32*.stream.video.sfu.models.ConnectionQuality\x12\x13\n\x0bis_speaking\x18\x07 \x01(\x08\x12\x1b\n\x13is_dominant_speaker\x18\x08 \x01(\x08\x12\x13\n\x0b\x61udio_level\x18\t \x01(\x02\x12\x0c\n\x04name\x18\n \x01(\t\x12\r\n\x05image\x18\x0b \x01(\t\x12\'\n\x06\x63ustom\x18\x0c \x01(\x0b\x32\x17.google.protobuf.Struct\x12\r\n\x05roles\x18\r \x03(\t\x12:\n\x06source\x18\x0e \x01(\x0e\x32*.stream.video.sfu.models.ParticipantSource"^\n\rStreamQuality\x12<\n\rvideo_quality\x18\x01 \x01(\x0e\x32%.stream.video.sfu.models.VideoQuality\x12\x0f\n\x07user_id\x18\x02 \x01(\t"/\n\x0eVideoDimension\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r"\xb1\x01\n\nVideoLayer\x12\x0b\n\x03rid\x18\x01 \x01(\t\x12@\n\x0fvideo_dimension\x18\x02 \x01(\x0b\x32\'.stream.video.sfu.models.VideoDimension\x12\x0f\n\x07\x62itrate\x18\x04 \x01(\r\x12\x0b\n\x03\x66ps\x18\x05 \x01(\r\x12\x36\n\x07quality\x18\x06 \x01(\x0e\x32%.stream.video.sfu.models.VideoQuality"y\n\x0fSubscribeOption\x12\x36\n\ntrack_type\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12.\n\x06\x63odecs\x18\x02 \x03(\x0b\x32\x1e.stream.video.sfu.models.Codec"\xfc\x02\n\rPublishOption\x12\x36\n\ntrack_type\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x0f\n\x07\x62itrate\x18\x03 \x01(\x05\x12\x0b\n\x03\x66ps\x18\x04 \x01(\x05\x12\x1a\n\x12max_spatial_layers\x18\x05 \x01(\x05\x12\x1b\n\x13max_temporal_layers\x18\x06 \x01(\x05\x12@\n\x0fvideo_dimension\x18\x07 \x01(\x0b\x32\'.stream.video.sfu.models.VideoDimension\x12\n\n\x02id\x18\x08 \x01(\x05\x12\x18\n\x10use_single_layer\x18\t \x01(\x08\x12\x45\n\x16\x61udio_bitrate_profiles\x18\n \x03(\x0b\x32%.stream.video.sfu.models.AudioBitrate"j\n\x05\x43odec\x12\x14\n\x0cpayload_type\x18\x10 \x01(\r\x12\x0c\n\x04name\x18\n \x01(\t\x12\x12\n\nclock_rate\x18\x0e \x01(\r\x12\x1b\n\x13\x65ncoding_parameters\x18\x0f \x01(\t\x12\x0c\n\x04\x66mtp\x18\x0c \x01(\t"m\n\nICETrickle\x12\x34\n\tpeer_type\x18\x01 \x01(\x0e\x32!.stream.video.sfu.models.PeerType\x12\x15\n\rice_candidate\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t"^\n\x0c\x41udioBitrate\x12=\n\x07profile\x18\x01 \x01(\x0e\x32,.stream.video.sfu.models.AudioBitrateProfile\x12\x0f\n\x07\x62itrate\x18\x02 \x01(\x05"\x9a\x02\n\tTrackInfo\x12\x10\n\x08track_id\x18\x01 \x01(\t\x12\x36\n\ntrack_type\x18\x02 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12\x33\n\x06layers\x18\x05 \x03(\x0b\x32#.stream.video.sfu.models.VideoLayer\x12\x0b\n\x03mid\x18\x06 \x01(\t\x12\x0b\n\x03\x64tx\x18\x07 \x01(\x08\x12\x0e\n\x06stereo\x18\x08 \x01(\x08\x12\x0b\n\x03red\x18\t \x01(\x08\x12\r\n\x05muted\x18\n \x01(\x08\x12-\n\x05\x63odec\x18\x0b \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x19\n\x11publish_option_id\x18\x0c \x01(\x05"`\n\x05\x45rror\x12\x30\n\x04\x63ode\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.ErrorCode\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x14\n\x0cshould_retry\x18\x03 \x01(\x08"\xc7\x01\n\rClientDetails\x12)\n\x03sdk\x18\x01 \x01(\x0b\x32\x1c.stream.video.sfu.models.Sdk\x12\'\n\x02os\x18\x02 \x01(\x0b\x32\x1b.stream.video.sfu.models.OS\x12\x31\n\x07\x62rowser\x18\x03 \x01(\x0b\x32 .stream.video.sfu.models.Browser\x12/\n\x06\x64\x65vice\x18\x04 \x01(\x0b\x32\x1f.stream.video.sfu.models.Device"b\n\x03Sdk\x12.\n\x04type\x18\x01 \x01(\x0e\x32 .stream.video.sfu.models.SdkType\x12\r\n\x05major\x18\x02 \x01(\t\x12\r\n\x05minor\x18\x03 \x01(\t\x12\r\n\x05patch\x18\x04 \x01(\t"9\n\x02OS\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x14\n\x0c\x61rchitecture\x18\x03 \x01(\t"(\n\x07\x42rowser\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"\x89\x01\n\x0bRTMPIngress\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\x12\x12\n\nframe_rate\x18\x03 \x01(\x01\x12\x10\n\x08software\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t\x12\x0f\n\x07\x65ncoder\x18\x06 \x01(\t\x12\x13\n\x0bremote_addr\x18\x07 \x01(\t"\'\n\x06\x44\x65vice\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t"\xdb\x01\n\x04\x43\x61ll\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\x12\x1a\n\x12\x63reated_by_user_id\x18\x03 \x01(\t\x12\x14\n\x0chost_user_id\x18\x04 \x01(\t\x12\'\n\x06\x63ustom\x18\x05 \x01(\x0b\x32\x17.google.protobuf.Struct\x12.\n\ncreated_at\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp"[\n\nCallGrants\x12\x19\n\x11\x63\x61n_publish_audio\x18\x01 \x01(\x08\x12\x19\n\x11\x63\x61n_publish_video\x18\x02 \x01(\x08\x12\x17\n\x0f\x63\x61n_screenshare\x18\x03 \x01(\x08"W\n\x0cInputDevices\x12\x19\n\x11\x61vailable_devices\x18\x01 \x03(\t\x12\x16\n\x0e\x63urrent_device\x18\x02 \x01(\t\x12\x14\n\x0cis_permitted\x18\x03 \x01(\x08"p\n\x0c\x41ndroidState\x12\x43\n\rthermal_state\x18\x01 \x01(\x0e\x32,.stream.video.sfu.models.AndroidThermalState\x12\x1b\n\x13is_power_saver_mode\x18\x02 \x01(\x08"r\n\nAppleState\x12\x41\n\rthermal_state\x18\x01 \x01(\x0e\x32*.stream.video.sfu.models.AppleThermalState\x12!\n\x19is_low_power_mode_enabled\x18\x02 \x01(\x08"\xff\x01\n\x10PerformanceStats\x12\x36\n\ntrack_type\x18\x01 \x01(\x0e\x32".stream.video.sfu.models.TrackType\x12-\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x1e.stream.video.sfu.models.Codec\x12\x19\n\x11\x61vg_frame_time_ms\x18\x03 \x01(\x02\x12\x0f\n\x07\x61vg_fps\x18\x04 \x01(\x02\x12@\n\x0fvideo_dimension\x18\x05 \x01(\x0b\x32\'.stream.video.sfu.models.VideoDimension\x12\x16\n\x0etarget_bitrate\x18\x06 \x01(\x05*I\n\x08PeerType\x12#\n\x1fPEER_TYPE_PUBLISHER_UNSPECIFIED\x10\x00\x12\x18\n\x14PEER_TYPE_SUBSCRIBER\x10\x01*\x93\x01\n\x11\x43onnectionQuality\x12"\n\x1e\x43ONNECTION_QUALITY_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x43ONNECTION_QUALITY_POOR\x10\x01\x12\x1b\n\x17\x43ONNECTION_QUALITY_GOOD\x10\x02\x12 \n\x1c\x43ONNECTION_QUALITY_EXCELLENT\x10\x03*w\n\x0cVideoQuality\x12!\n\x1dVIDEO_QUALITY_LOW_UNSPECIFIED\x10\x00\x12\x15\n\x11VIDEO_QUALITY_MID\x10\x01\x12\x16\n\x12VIDEO_QUALITY_HIGH\x10\x02\x12\x15\n\x11VIDEO_QUALITY_OFF\x10\x03*\x93\x01\n\tTrackType\x12\x1a\n\x16TRACK_TYPE_UNSPECIFIED\x10\x00\x12\x14\n\x10TRACK_TYPE_AUDIO\x10\x01\x12\x14\n\x10TRACK_TYPE_VIDEO\x10\x02\x12\x1b\n\x17TRACK_TYPE_SCREEN_SHARE\x10\x03\x12!\n\x1dTRACK_TYPE_SCREEN_SHARE_AUDIO\x10\x04*\xcd\x01\n\x11ParticipantSource\x12)\n%PARTICIPANT_SOURCE_WEBRTC_UNSPECIFIED\x10\x00\x12\x1b\n\x17PARTICIPANT_SOURCE_RTMP\x10\x01\x12\x1b\n\x17PARTICIPANT_SOURCE_WHIP\x10\x02\x12\x1a\n\x16PARTICIPANT_SOURCE_SIP\x10\x03\x12\x1b\n\x17PARTICIPANT_SOURCE_RTSP\x10\x04\x12\x1a\n\x16PARTICIPANT_SOURCE_SRT\x10\x05*\xa7\x01\n\x13\x41udioBitrateProfile\x12\x34\n0AUDIO_BITRATE_PROFILE_VOICE_STANDARD_UNSPECIFIED\x10\x00\x12,\n(AUDIO_BITRATE_PROFILE_VOICE_HIGH_QUALITY\x10\x01\x12,\n(AUDIO_BITRATE_PROFILE_MUSIC_HIGH_QUALITY\x10\x02*\xea\x06\n\tErrorCode\x12\x1a\n\x16\x45RROR_CODE_UNSPECIFIED\x10\x00\x12&\n"ERROR_CODE_PUBLISH_TRACK_NOT_FOUND\x10\x64\x12&\n"ERROR_CODE_PUBLISH_TRACKS_MISMATCH\x10\x65\x12)\n%ERROR_CODE_PUBLISH_TRACK_OUT_OF_ORDER\x10\x66\x12\x32\n.ERROR_CODE_PUBLISH_TRACK_VIDEO_LAYER_NOT_FOUND\x10g\x12\x19\n\x15\x45RROR_CODE_LIVE_ENDED\x10h\x12%\n ERROR_CODE_PARTICIPANT_NOT_FOUND\x10\xc8\x01\x12)\n$ERROR_CODE_PARTICIPANT_MIGRATING_OUT\x10\xc9\x01\x12,\n\'ERROR_CODE_PARTICIPANT_MIGRATION_FAILED\x10\xca\x01\x12%\n ERROR_CODE_PARTICIPANT_MIGRATING\x10\xcb\x01\x12,\n\'ERROR_CODE_PARTICIPANT_RECONNECT_FAILED\x10\xcc\x01\x12\x33\n.ERROR_CODE_PARTICIPANT_MEDIA_TRANSPORT_FAILURE\x10\xcd\x01\x12\'\n"ERROR_CODE_PARTICIPANT_SIGNAL_LOST\x10\xce\x01\x12\x1e\n\x19\x45RROR_CODE_CALL_NOT_FOUND\x10\xac\x02\x12.\n)ERROR_CODE_CALL_PARTICIPANT_LIMIT_REACHED\x10\xad\x02\x12)\n$ERROR_CODE_REQUEST_VALIDATION_FAILED\x10\x90\x03\x12\x1f\n\x1a\x45RROR_CODE_UNAUTHENTICATED\x10\x91\x03\x12!\n\x1c\x45RROR_CODE_PERMISSION_DENIED\x10\x93\x03\x12!\n\x1c\x45RROR_CODE_TOO_MANY_REQUESTS\x10\xad\x03\x12%\n ERROR_CODE_INTERNAL_SERVER_ERROR\x10\xf4\x03\x12!\n\x1c\x45RROR_CODE_SFU_SHUTTING_DOWN\x10\xd8\x04\x12\x18\n\x13\x45RROR_CODE_SFU_FULL\x10\xbc\x05*\xea\x01\n\x07SdkType\x12\x18\n\x14SDK_TYPE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSDK_TYPE_REACT\x10\x01\x12\x14\n\x10SDK_TYPE_ANGULAR\x10\x02\x12\x14\n\x10SDK_TYPE_ANDROID\x10\x03\x12\x10\n\x0cSDK_TYPE_IOS\x10\x04\x12\x14\n\x10SDK_TYPE_FLUTTER\x10\x05\x12\x19\n\x15SDK_TYPE_REACT_NATIVE\x10\x06\x12\x12\n\x0eSDK_TYPE_UNITY\x10\x07\x12\x0f\n\x0bSDK_TYPE_GO\x10\x08\x12\x1d\n\x19SDK_TYPE_PLAIN_JAVASCRIPT\x10\t*\xbb\x01\n\x14TrackUnpublishReason\x12&\n"TRACK_UNPUBLISH_REASON_UNSPECIFIED\x10\x00\x12%\n!TRACK_UNPUBLISH_REASON_USER_MUTED\x10\x01\x12-\n)TRACK_UNPUBLISH_REASON_PERMISSION_REVOKED\x10\x02\x12%\n!TRACK_UNPUBLISH_REASON_MODERATION\x10\x03*n\n\x0cGoAwayReason\x12\x1e\n\x1aGO_AWAY_REASON_UNSPECIFIED\x10\x00\x12 \n\x1cGO_AWAY_REASON_SHUTTING_DOWN\x10\x01\x12\x1c\n\x18GO_AWAY_REASON_REBALANCE\x10\x02*\xb6\x01\n\x0f\x43\x61llEndedReason\x12!\n\x1d\x43\x41LL_ENDED_REASON_UNSPECIFIED\x10\x00\x12\x1b\n\x17\x43\x41LL_ENDED_REASON_ENDED\x10\x01\x12 \n\x1c\x43\x41LL_ENDED_REASON_LIVE_ENDED\x10\x02\x12\x1c\n\x18\x43\x41LL_ENDED_REASON_KICKED\x10\x03\x12#\n\x1f\x43\x41LL_ENDED_REASON_SESSION_ENDED\x10\x04*\xf1\x01\n\x1aWebsocketReconnectStrategy\x12,\n(WEBSOCKET_RECONNECT_STRATEGY_UNSPECIFIED\x10\x00\x12+\n\'WEBSOCKET_RECONNECT_STRATEGY_DISCONNECT\x10\x01\x12%\n!WEBSOCKET_RECONNECT_STRATEGY_FAST\x10\x02\x12\'\n#WEBSOCKET_RECONNECT_STRATEGY_REJOIN\x10\x03\x12(\n$WEBSOCKET_RECONNECT_STRATEGY_MIGRATE\x10\x04*\xb0\x02\n\x13\x41ndroidThermalState\x12%\n!ANDROID_THERMAL_STATE_UNSPECIFIED\x10\x00\x12\x1e\n\x1a\x41NDROID_THERMAL_STATE_NONE\x10\x01\x12\x1f\n\x1b\x41NDROID_THERMAL_STATE_LIGHT\x10\x02\x12"\n\x1e\x41NDROID_THERMAL_STATE_MODERATE\x10\x03\x12 \n\x1c\x41NDROID_THERMAL_STATE_SEVERE\x10\x04\x12"\n\x1e\x41NDROID_THERMAL_STATE_CRITICAL\x10\x05\x12#\n\x1f\x41NDROID_THERMAL_STATE_EMERGENCY\x10\x06\x12"\n\x1e\x41NDROID_THERMAL_STATE_SHUTDOWN\x10\x07*\xba\x01\n\x11\x41ppleThermalState\x12#\n\x1f\x41PPLE_THERMAL_STATE_UNSPECIFIED\x10\x00\x12\x1f\n\x1b\x41PPLE_THERMAL_STATE_NOMINAL\x10\x01\x12\x1c\n\x18\x41PPLE_THERMAL_STATE_FAIR\x10\x02\x12\x1f\n\x1b\x41PPLE_THERMAL_STATE_SERIOUS\x10\x03\x12 \n\x1c\x41PPLE_THERMAL_STATE_CRITICAL\x10\x04*c\n\x10\x43lientCapability\x12!\n\x1d\x43LIENT_CAPABILITY_UNSPECIFIED\x10\x00\x12,\n(CLIENT_CAPABILITY_SUBSCRIBER_VIDEO_PAUSE\x10\x01\x42\x65\x42\x0bSfuModelsV1P\x01Z7github.com/GetStream/protocol/protobuf/video/sfu/models\xaa\x02\x1aStream.Video.v1.Sfu.Modelsb\x06proto3' ) _globals = globals() @@ -37,32 +37,36 @@ _globals[ "DESCRIPTOR" ]._serialized_options = b"B\013SfuModelsV1P\001Z7github.com/GetStream/protocol/protobuf/video/sfu/models\252\002\032Stream.Video.v1.Sfu.Models" - _globals["_PEERTYPE"]._serialized_start = 3712 - _globals["_PEERTYPE"]._serialized_end = 3785 - _globals["_CONNECTIONQUALITY"]._serialized_start = 3788 - _globals["_CONNECTIONQUALITY"]._serialized_end = 3935 - _globals["_VIDEOQUALITY"]._serialized_start = 3937 - _globals["_VIDEOQUALITY"]._serialized_end = 4056 - _globals["_TRACKTYPE"]._serialized_start = 4059 - _globals["_TRACKTYPE"]._serialized_end = 4206 - _globals["_ERRORCODE"]._serialized_start = 4209 - _globals["_ERRORCODE"]._serialized_end = 5083 - _globals["_SDKTYPE"]._serialized_start = 5086 - _globals["_SDKTYPE"]._serialized_end = 5320 - _globals["_TRACKUNPUBLISHREASON"]._serialized_start = 5323 - _globals["_TRACKUNPUBLISHREASON"]._serialized_end = 5510 - _globals["_GOAWAYREASON"]._serialized_start = 5512 - _globals["_GOAWAYREASON"]._serialized_end = 5622 - _globals["_CALLENDEDREASON"]._serialized_start = 5625 - _globals["_CALLENDEDREASON"]._serialized_end = 5807 - _globals["_WEBSOCKETRECONNECTSTRATEGY"]._serialized_start = 5810 - _globals["_WEBSOCKETRECONNECTSTRATEGY"]._serialized_end = 6051 - _globals["_ANDROIDTHERMALSTATE"]._serialized_start = 6054 - _globals["_ANDROIDTHERMALSTATE"]._serialized_end = 6358 - _globals["_APPLETHERMALSTATE"]._serialized_start = 6361 - _globals["_APPLETHERMALSTATE"]._serialized_end = 6547 - _globals["_CLIENTCAPABILITY"]._serialized_start = 6549 - _globals["_CLIENTCAPABILITY"]._serialized_end = 6648 + _globals["_PEERTYPE"]._serialized_start = 3939 + _globals["_PEERTYPE"]._serialized_end = 4012 + _globals["_CONNECTIONQUALITY"]._serialized_start = 4015 + _globals["_CONNECTIONQUALITY"]._serialized_end = 4162 + _globals["_VIDEOQUALITY"]._serialized_start = 4164 + _globals["_VIDEOQUALITY"]._serialized_end = 4283 + _globals["_TRACKTYPE"]._serialized_start = 4286 + _globals["_TRACKTYPE"]._serialized_end = 4433 + _globals["_PARTICIPANTSOURCE"]._serialized_start = 4436 + _globals["_PARTICIPANTSOURCE"]._serialized_end = 4641 + _globals["_AUDIOBITRATEPROFILE"]._serialized_start = 4644 + _globals["_AUDIOBITRATEPROFILE"]._serialized_end = 4811 + _globals["_ERRORCODE"]._serialized_start = 4814 + _globals["_ERRORCODE"]._serialized_end = 5688 + _globals["_SDKTYPE"]._serialized_start = 5691 + _globals["_SDKTYPE"]._serialized_end = 5925 + _globals["_TRACKUNPUBLISHREASON"]._serialized_start = 5928 + _globals["_TRACKUNPUBLISHREASON"]._serialized_end = 6115 + _globals["_GOAWAYREASON"]._serialized_start = 6117 + _globals["_GOAWAYREASON"]._serialized_end = 6227 + _globals["_CALLENDEDREASON"]._serialized_start = 6230 + _globals["_CALLENDEDREASON"]._serialized_end = 6412 + _globals["_WEBSOCKETRECONNECTSTRATEGY"]._serialized_start = 6415 + _globals["_WEBSOCKETRECONNECTSTRATEGY"]._serialized_end = 6656 + _globals["_ANDROIDTHERMALSTATE"]._serialized_start = 6659 + _globals["_ANDROIDTHERMALSTATE"]._serialized_end = 6963 + _globals["_APPLETHERMALSTATE"]._serialized_start = 6966 + _globals["_APPLETHERMALSTATE"]._serialized_end = 7152 + _globals["_CLIENTCAPABILITY"]._serialized_start = 7154 + _globals["_CLIENTCAPABILITY"]._serialized_end = 7253 _globals["_CALLSTATE"]._serialized_start = 122 _globals["_CALLSTATE"]._serialized_end = 355 _globals["_PARTICIPANTCOUNT"]._serialized_start = 357 @@ -70,47 +74,49 @@ _globals["_PIN"]._serialized_start = 411 _globals["_PIN"]._serialized_end = 453 _globals["_PARTICIPANT"]._serialized_start = 456 - _globals["_PARTICIPANT"]._serialized_end = 872 - _globals["_STREAMQUALITY"]._serialized_start = 874 - _globals["_STREAMQUALITY"]._serialized_end = 968 - _globals["_VIDEODIMENSION"]._serialized_start = 970 - _globals["_VIDEODIMENSION"]._serialized_end = 1017 - _globals["_VIDEOLAYER"]._serialized_start = 1020 - _globals["_VIDEOLAYER"]._serialized_end = 1197 - _globals["_SUBSCRIBEOPTION"]._serialized_start = 1199 - _globals["_SUBSCRIBEOPTION"]._serialized_end = 1320 - _globals["_PUBLISHOPTION"]._serialized_start = 1323 - _globals["_PUBLISHOPTION"]._serialized_end = 1632 - _globals["_CODEC"]._serialized_start = 1634 - _globals["_CODEC"]._serialized_end = 1740 - _globals["_ICETRICKLE"]._serialized_start = 1742 - _globals["_ICETRICKLE"]._serialized_end = 1851 - _globals["_TRACKINFO"]._serialized_start = 1854 - _globals["_TRACKINFO"]._serialized_end = 2136 - _globals["_ERROR"]._serialized_start = 2138 - _globals["_ERROR"]._serialized_end = 2234 - _globals["_CLIENTDETAILS"]._serialized_start = 2237 - _globals["_CLIENTDETAILS"]._serialized_end = 2436 - _globals["_SDK"]._serialized_start = 2438 - _globals["_SDK"]._serialized_end = 2536 - _globals["_OS"]._serialized_start = 2538 - _globals["_OS"]._serialized_end = 2595 - _globals["_BROWSER"]._serialized_start = 2597 - _globals["_BROWSER"]._serialized_end = 2637 - _globals["_RTMPINGRESS"]._serialized_start = 2640 - _globals["_RTMPINGRESS"]._serialized_end = 2777 - _globals["_DEVICE"]._serialized_start = 2779 - _globals["_DEVICE"]._serialized_end = 2818 - _globals["_CALL"]._serialized_start = 2821 - _globals["_CALL"]._serialized_end = 3040 - _globals["_CALLGRANTS"]._serialized_start = 3042 - _globals["_CALLGRANTS"]._serialized_end = 3133 - _globals["_INPUTDEVICES"]._serialized_start = 3135 - _globals["_INPUTDEVICES"]._serialized_end = 3222 - _globals["_ANDROIDSTATE"]._serialized_start = 3224 - _globals["_ANDROIDSTATE"]._serialized_end = 3336 - _globals["_APPLESTATE"]._serialized_start = 3338 - _globals["_APPLESTATE"]._serialized_end = 3452 - _globals["_PERFORMANCESTATS"]._serialized_start = 3455 - _globals["_PERFORMANCESTATS"]._serialized_end = 3710 + _globals["_PARTICIPANT"]._serialized_end = 932 + _globals["_STREAMQUALITY"]._serialized_start = 934 + _globals["_STREAMQUALITY"]._serialized_end = 1028 + _globals["_VIDEODIMENSION"]._serialized_start = 1030 + _globals["_VIDEODIMENSION"]._serialized_end = 1077 + _globals["_VIDEOLAYER"]._serialized_start = 1080 + _globals["_VIDEOLAYER"]._serialized_end = 1257 + _globals["_SUBSCRIBEOPTION"]._serialized_start = 1259 + _globals["_SUBSCRIBEOPTION"]._serialized_end = 1380 + _globals["_PUBLISHOPTION"]._serialized_start = 1383 + _globals["_PUBLISHOPTION"]._serialized_end = 1763 + _globals["_CODEC"]._serialized_start = 1765 + _globals["_CODEC"]._serialized_end = 1871 + _globals["_ICETRICKLE"]._serialized_start = 1873 + _globals["_ICETRICKLE"]._serialized_end = 1982 + _globals["_AUDIOBITRATE"]._serialized_start = 1984 + _globals["_AUDIOBITRATE"]._serialized_end = 2078 + _globals["_TRACKINFO"]._serialized_start = 2081 + _globals["_TRACKINFO"]._serialized_end = 2363 + _globals["_ERROR"]._serialized_start = 2365 + _globals["_ERROR"]._serialized_end = 2461 + _globals["_CLIENTDETAILS"]._serialized_start = 2464 + _globals["_CLIENTDETAILS"]._serialized_end = 2663 + _globals["_SDK"]._serialized_start = 2665 + _globals["_SDK"]._serialized_end = 2763 + _globals["_OS"]._serialized_start = 2765 + _globals["_OS"]._serialized_end = 2822 + _globals["_BROWSER"]._serialized_start = 2824 + _globals["_BROWSER"]._serialized_end = 2864 + _globals["_RTMPINGRESS"]._serialized_start = 2867 + _globals["_RTMPINGRESS"]._serialized_end = 3004 + _globals["_DEVICE"]._serialized_start = 3006 + _globals["_DEVICE"]._serialized_end = 3045 + _globals["_CALL"]._serialized_start = 3048 + _globals["_CALL"]._serialized_end = 3267 + _globals["_CALLGRANTS"]._serialized_start = 3269 + _globals["_CALLGRANTS"]._serialized_end = 3360 + _globals["_INPUTDEVICES"]._serialized_start = 3362 + _globals["_INPUTDEVICES"]._serialized_end = 3449 + _globals["_ANDROIDSTATE"]._serialized_start = 3451 + _globals["_ANDROIDSTATE"]._serialized_end = 3563 + _globals["_APPLESTATE"]._serialized_start = 3565 + _globals["_APPLESTATE"]._serialized_end = 3679 + _globals["_PERFORMANCESTATS"]._serialized_start = 3682 + _globals["_PERFORMANCESTATS"]._serialized_end = 3937 # @@protoc_insertion_point(module_scope) diff --git a/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.pyi b/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.pyi index 956f70d0..4c109f35 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.pyi +++ b/getstream/video/rtc/pb/stream/video/sfu/models/models_pb2.pyi @@ -115,6 +115,63 @@ TRACK_TYPE_SCREEN_SHARE: TrackType.ValueType # 3 TRACK_TYPE_SCREEN_SHARE_AUDIO: TrackType.ValueType # 4 global___TrackType = TrackType +class _ParticipantSource: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ParticipantSourceEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + _ParticipantSource.ValueType + ], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PARTICIPANT_SOURCE_WEBRTC_UNSPECIFIED: _ParticipantSource.ValueType # 0 + PARTICIPANT_SOURCE_RTMP: _ParticipantSource.ValueType # 1 + PARTICIPANT_SOURCE_WHIP: _ParticipantSource.ValueType # 2 + PARTICIPANT_SOURCE_SIP: _ParticipantSource.ValueType # 3 + PARTICIPANT_SOURCE_RTSP: _ParticipantSource.ValueType # 4 + PARTICIPANT_SOURCE_SRT: _ParticipantSource.ValueType # 5 + +class ParticipantSource( + _ParticipantSource, metaclass=_ParticipantSourceEnumTypeWrapper +): + """must be aligned with kit""" + +PARTICIPANT_SOURCE_WEBRTC_UNSPECIFIED: ParticipantSource.ValueType # 0 +PARTICIPANT_SOURCE_RTMP: ParticipantSource.ValueType # 1 +PARTICIPANT_SOURCE_WHIP: ParticipantSource.ValueType # 2 +PARTICIPANT_SOURCE_SIP: ParticipantSource.ValueType # 3 +PARTICIPANT_SOURCE_RTSP: ParticipantSource.ValueType # 4 +PARTICIPANT_SOURCE_SRT: ParticipantSource.ValueType # 5 +global___ParticipantSource = ParticipantSource + +class _AudioBitrateProfile: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _AudioBitrateProfileEnumTypeWrapper( + google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ + _AudioBitrateProfile.ValueType + ], + builtins.type, +): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + AUDIO_BITRATE_PROFILE_VOICE_STANDARD_UNSPECIFIED: ( + _AudioBitrateProfile.ValueType + ) # 0 + AUDIO_BITRATE_PROFILE_VOICE_HIGH_QUALITY: _AudioBitrateProfile.ValueType # 1 + AUDIO_BITRATE_PROFILE_MUSIC_HIGH_QUALITY: _AudioBitrateProfile.ValueType # 2 + +class AudioBitrateProfile( + _AudioBitrateProfile, metaclass=_AudioBitrateProfileEnumTypeWrapper +): ... + +AUDIO_BITRATE_PROFILE_VOICE_STANDARD_UNSPECIFIED: AudioBitrateProfile.ValueType # 0 +AUDIO_BITRATE_PROFILE_VOICE_HIGH_QUALITY: AudioBitrateProfile.ValueType # 1 +AUDIO_BITRATE_PROFILE_MUSIC_HIGH_QUALITY: AudioBitrateProfile.ValueType # 2 +global___AudioBitrateProfile = AudioBitrateProfile + class _ErrorCode: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType @@ -587,6 +644,7 @@ class Participant(google.protobuf.message.Message): IMAGE_FIELD_NUMBER: builtins.int CUSTOM_FIELD_NUMBER: builtins.int ROLES_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int user_id: builtins.str session_id: builtins.str @property @@ -613,6 +671,7 @@ class Participant(google.protobuf.message.Message): ) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[ builtins.str ]: ... + source: global___ParticipantSource.ValueType def __init__( self, *, @@ -630,6 +689,7 @@ class Participant(google.protobuf.message.Message): image: builtins.str = ..., custom: google.protobuf.struct_pb2.Struct | None = ..., roles: collections.abc.Iterable[builtins.str] | None = ..., + source: global___ParticipantSource.ValueType = ..., ) -> None: ... def HasField( self, @@ -662,6 +722,8 @@ class Participant(google.protobuf.message.Message): b"roles", "session_id", b"session_id", + "source", + b"source", "track_lookup_prefix", b"track_lookup_prefix", "user_id", @@ -810,6 +872,7 @@ class PublishOption(google.protobuf.message.Message): VIDEO_DIMENSION_FIELD_NUMBER: builtins.int ID_FIELD_NUMBER: builtins.int USE_SINGLE_LAYER_FIELD_NUMBER: builtins.int + AUDIO_BITRATE_PROFILES_FIELD_NUMBER: builtins.int track_type: global___TrackType.ValueType """The type of the track being published (e.g., video, screenshare).""" @property @@ -860,6 +923,13 @@ class PublishOption(google.protobuf.message.Message): disabling all lower layers. This applies to simulcast encodings. For SVC codecs, prefer using the L1T3 (single spatial, 3 temporal layers) mode instead. """ + @property + def audio_bitrate_profiles( + self, + ) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[ + global___AudioBitrate + ]: + """Audio bitrate profiles for different audio quality profiles.""" def __init__( self, *, @@ -872,6 +942,8 @@ class PublishOption(google.protobuf.message.Message): video_dimension: global___VideoDimension | None = ..., id: builtins.int = ..., use_single_layer: builtins.bool = ..., + audio_bitrate_profiles: collections.abc.Iterable[global___AudioBitrate] + | None = ..., ) -> None: ... def HasField( self, @@ -882,6 +954,8 @@ class PublishOption(google.protobuf.message.Message): def ClearField( self, field_name: typing_extensions.Literal[ + "audio_bitrate_profiles", + b"audio_bitrate_profiles", "bitrate", b"bitrate", "codec", @@ -977,6 +1051,29 @@ class ICETrickle(google.protobuf.message.Message): global___ICETrickle = ICETrickle +@typing_extensions.final +class AudioBitrate(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PROFILE_FIELD_NUMBER: builtins.int + BITRATE_FIELD_NUMBER: builtins.int + profile: global___AudioBitrateProfile.ValueType + bitrate: builtins.int + def __init__( + self, + *, + profile: global___AudioBitrateProfile.ValueType = ..., + bitrate: builtins.int = ..., + ) -> None: ... + def ClearField( + self, + field_name: typing_extensions.Literal[ + "bitrate", b"bitrate", "profile", b"profile" + ], + ) -> None: ... + +global___AudioBitrate = AudioBitrate + @typing_extensions.final class TrackInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor diff --git a/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.py b/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.py index 382d57cc..347d171f 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.py +++ b/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_pb2.py @@ -2,7 +2,7 @@ # Generated by the protocol buffer compiler. DO NOT EDIT! # NO CHECKED-IN PROTOBUF GENCODE # source: video/sfu/signal_rpc/signal.proto -# Protobuf Python Version: 6.31.1 +# Protobuf Python Version: 5.28.2 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor @@ -12,7 +12,7 @@ from google.protobuf.internal import builder as _builder _runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, 6, 31, 1, "", "video/sfu/signal_rpc/signal.proto" + _runtime_version.Domain.PUBLIC, 5, 28, 2, "", "video/sfu/signal_rpc/signal.proto" ) # @@protoc_insertion_point(imports) diff --git a/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py b/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py index 227c076d..46d849e0 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py +++ b/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py @@ -2,17 +2,19 @@ # Generated by https://github.com/verloop/twirpy/protoc-gen-twirpy. DO NOT EDIT! # source: video/sfu/signal_rpc/signal.proto +import asyncio +import aiohttp +import json + from google.protobuf import symbol_database as _symbol_database from twirp.base import Endpoint from twirp.server import TwirpServer from twirp.client import TwirpClient +from typing import Optional -# Import the embedded AsyncTwirpClient using an absolute path -from getstream.video.rtc.twirp_async_client_embed import AsyncTwirpClient - -# We no longer need the try/except block, assume async is available via the embed -_async_available = True +from twirp import exceptions +from twirp import errors _sym_db = _symbol_database.Default() @@ -213,7 +215,55 @@ def StopNoiseCancellation( ) -# Use the embedded AsyncTwirpClient as the base class +class AsyncTwirpClient: + def __init__( + self, address: str, session: Optional[aiohttp.ClientSession] = None + ) -> None: + self._address = address + self._session = session + + async def _make_request( + self, *, url, ctx, request, response_obj, session=None, **kwargs + ): + headers = ctx.get_headers() + if "headers" in kwargs: + headers.update(kwargs["headers"]) + kwargs["headers"] = headers + kwargs["headers"]["Content-Type"] = "application/protobuf" + + if session is None: + session = self._session + if not isinstance(session, aiohttp.ClientSession): + raise TypeError(f"invalid session type '{type(session).__name__}'") + + try: + async with await session.post( + url=url, data=request.SerializeToString(), **kwargs + ) as resp: + if resp.status == 200: + response = response_obj() + response.ParseFromString(await resp.read()) + return response + try: + raise exceptions.TwirpServerException.from_json(await resp.json()) + except (aiohttp.ContentTypeError, json.JSONDecodeError): + raise exceptions.twirp_error_from_intermediary( + resp.status, resp.reason, resp.headers, await resp.text() + ) from None + except asyncio.TimeoutError as e: + raise exceptions.TwirpServerException( + code=errors.Errors.DeadlineExceeded, + message=str(e) or "request timeout", + meta={"original_exception": e}, + ) + except aiohttp.ServerConnectionError as e: + raise exceptions.TwirpServerException( + code=errors.Errors.Unavailable, + message=str(e), + meta={"original_exception": e}, + ) + + class AsyncSignalServerClient(AsyncTwirpClient): async def SetPublisher( self, *, ctx, request, server_path_prefix="/twirp", session=None, **kwargs @@ -306,9 +356,7 @@ async def SendStats( url=f"{server_path_prefix}/stream.video.sfu.signal.SignalServer/SendStats", ctx=ctx, request=request, - response_obj=_sym_db.GetSymbol( - "stream.video.sfu.signal.SendStatsResponse" - ), + response_obj=_sym_db.GetSymbol("stream.video.sfu.signal.SendStatsResponse"), session=session, **kwargs, ) diff --git a/getstream/video/rtc/twirp_async_client_embed.py b/getstream/video/rtc/twirp_async_client_embed.py deleted file mode 100644 index ce8780b3..00000000 --- a/getstream/video/rtc/twirp_async_client_embed.py +++ /dev/null @@ -1,82 +0,0 @@ -# -*- coding: utf-8 -*- -# Code copied directly from: -# https://github.com/verloop/twirpy/blob/main/twirp/async_client.py -# Embedded here as the library doesn't seem to ship it correctly. - -import asyncio -import json -from typing import Optional -from urllib.parse import urljoin # Import urljoin for cleaner URL combination - -import aiohttp - -# Assuming these modules exist within the installed twirp package -# or need to be embedded as well if they are also problematic. -# For now, assume they are available from the installed 'twirp' base. -from twirp import exceptions -from twirp import errors - - -class AsyncTwirpClient: - def __init__( - self, address: str, session: Optional[aiohttp.ClientSession] = None - ) -> None: - # Ensure address ends with a slash for urljoin to work correctly - if not address.endswith("/"): - address += "/" - self._address = address - self._session = session - - async def _make_request( - self, *, url, ctx, request, response_obj, session=None, **kwargs - ): - headers = ctx.get_headers() - if "headers" in kwargs: - headers.update(kwargs["headers"]) - kwargs["headers"] = headers - kwargs["headers"]["Content-Type"] = "application/protobuf" - - if session is None: - session = self._session - - _session_created = False - if not isinstance(session, aiohttp.ClientSession): - session = aiohttp.ClientSession() - _session_created = True - - # Construct the full URL by joining the base address and the path - # Remove leading slash from url path if present, as self._address ends with / - full_url = urljoin(self._address, url.lstrip("/")) - - try: - # Use the correctly constructed full_url - async with await session.post( - url=full_url, data=request.SerializeToString(), **kwargs - ) as resp: - if resp.status == 200: - response = response_obj() - response.ParseFromString(await resp.read()) - return response - try: - raise exceptions.TwirpServerException.from_json(await resp.json()) - except (aiohttp.ContentTypeError, json.JSONDecodeError): - raise exceptions.twirp_error_from_intermediary( - resp.status, resp.reason, resp.headers, await resp.text() - ) from None - except asyncio.TimeoutError as e: - raise exceptions.TwirpServerException( - code=errors.Errors.DeadlineExceeded, - message=str(e) or "request timeout", - meta={"original_exception": e}, - ) - except aiohttp.ServerConnectionError as e: - # This catches more connection-related errors like ClientConnectorError - raise exceptions.TwirpServerException( - code=errors.Errors.Unavailable, - message=str(e), - meta={"original_exception": e}, - ) - finally: - # Close the session only if we created it internally - if _session_created and session: - await session.close() diff --git a/getstream/video/rtc/twirp_client_wrapper.py b/getstream/video/rtc/twirp_client_wrapper.py index 726e7f20..94a82b03 100644 --- a/getstream/video/rtc/twirp_client_wrapper.py +++ b/getstream/video/rtc/twirp_client_wrapper.py @@ -12,7 +12,6 @@ from getstream.video.rtc.pb.stream.video.sfu.models import models_pb2 from getstream.video.rtc.pb.stream.video.sfu.signal_rpc.signal_twirp import ( AsyncSignalServerClient, - _async_available, ) logger = logging.getLogger(__name__) @@ -119,11 +118,6 @@ def _check_response_for_error(response, method_name, span=None): raise -# Check if async capabilities are available -if not _async_available: - raise ImportError("AsyncSignalServerClient requires 'aiohttp'. Please install it.") - - # Inherit but use __getattribute__ for dynamic wrapping class SignalClient(AsyncSignalServerClient): """An async TwirpClient wrapper that inherits from AsyncSignalServerClient diff --git a/pyproject.toml b/pyproject.toml index 062b7877..bbf89086 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,7 @@ dependencies = [ "pydantic>=2.10.6", "pyee>=13.0.0", "ijson>=3.4.0", + "twirp", ] [project.optional-dependencies] @@ -76,7 +77,6 @@ dev = [ "opentelemetry-exporter-otlp>=1.37.0", "opentelemetry-exporter-prometheus>=0.58b0", "prometheus-client>=0.23.1", - "twirp @ git+https://github.com/tbarbugli/twirpy@main", ] [tool.uv.workspace] diff --git a/uv.lock b/uv.lock index 762a9faa..76c4b365 100644 --- a/uv.lock +++ b/uv.lock @@ -737,6 +737,7 @@ dependencies = [ { name = "pyee" }, { name = "pyjwt" }, { name = "python-dateutil" }, + { name = "twirp" }, ] [package.optional-dependencies] @@ -779,7 +780,6 @@ dev = [ { name = "python-dateutil" }, { name = "python-dotenv" }, { name = "ruff" }, - { name = "twirp" }, ] [package.metadata] @@ -811,6 +811,7 @@ requires-dist = [ { name = "tenacity", marker = "extra == 'webrtc'", specifier = ">=9.1.2" }, { name = "torch", marker = "extra == 'webrtc'", specifier = ">=2.7.1" }, { name = "torchaudio", marker = "extra == 'webrtc'", specifier = ">=2.7.1" }, + { name = "twirp" }, { name = "twirp", marker = "extra == 'webrtc'" }, { name = "websocket-client", marker = "extra == 'webrtc'", specifier = ">=1.8.0" }, { name = "websockets", marker = "extra == 'webrtc'", specifier = ">=15.0.1" }, @@ -833,7 +834,6 @@ dev = [ { name = "python-dateutil", specifier = ">=2.8.2,<3" }, { name = "python-dotenv", specifier = ">=1.1.1,<2" }, { name = "ruff", specifier = ">=0.12.1" }, - { name = "twirp", git = "https://github.com/tbarbugli/twirpy?rev=main" }, ] [[package]] @@ -2964,13 +2964,17 @@ wheels = [ [[package]] name = "twirp" -version = "0.0.dev0" -source = { git = "https://github.com/tbarbugli/twirpy?rev=main#52d0b399c7861d9143b38935fdaa2c30441d4dc2" } +version = "0.0.7" +source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "protobuf" }, { name = "requests" }, { name = "structlog" }, ] +sdist = { url = "https://files.pythonhosted.org/packages/4c/e1/ebb06fa8f7164ff7f81d5cd78a74b768373480c417b2837d94a5943872b6/twirp-0.0.7.tar.gz", hash = "sha256:692b055d5da6e8ec29ded5dc93682c3ed5ec608bb9cb14529139a3b31cc2a44a", size = 11353 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f5/a8/12092a50daa826b692e79a199c3f55921ce5a9cd67bed8e454bcdbbf9fdb/twirp-0.0.7-py3-none-any.whl", hash = "sha256:978294787f07d9f5489e6e32ef050d4d8b619bdffcbe80b248797518b4f69871", size = 12437 }, +] [[package]] name = "types-protobuf" From 73e8a9c343727945c0bf3f2a6b974e09b907f6aa Mon Sep 17 00:00:00 2001 From: Tommaso Barbugli Date: Tue, 11 Nov 2025 16:21:14 +0100 Subject: [PATCH 3/4] remove dead test --- tests/test_twirp_client_wrapper.py | 145 ----------------------------- 1 file changed, 145 deletions(-) delete mode 100644 tests/test_twirp_client_wrapper.py diff --git a/tests/test_twirp_client_wrapper.py b/tests/test_twirp_client_wrapper.py deleted file mode 100644 index 911633ca..00000000 --- a/tests/test_twirp_client_wrapper.py +++ /dev/null @@ -1,145 +0,0 @@ -# -*- coding: utf-8 -*- -import unittest -from unittest.mock import AsyncMock, patch, MagicMock - -# Try importing the problematic module directly -# import twirp.async_client # <-- REMOVE THIS DEBUG IMPORT - -# Modules to test -from getstream.video.rtc.twirp_client_wrapper import SignalClient, SfuRpcError, Context - -# Protobufs needed for requests/responses and error codes -from getstream.video.rtc.pb.stream.video.sfu.signal_rpc import signal_pb2 -from getstream.video.rtc.pb.stream.video.sfu.models import models_pb2 - -# No longer need to patch the base class path -# ASYNC_CLIENT_PATH = "getstream.video.rtc.twirp_client_wrapper.AsyncSignalServerClient" - - -class TestSignalClientWrapper(unittest.IsolatedAsyncioTestCase): - async def test_rpc_success_no_error_field(self): - """Test RPC call succeeds when the response has no error field.""" - # Create a real instance of the wrapper - wrapper_client = SignalClient( - address="http://mock_address" - ) # Address needed for init - - # Create a MagicMock to simulate the response object - mock_response = MagicMock(spec=signal_pb2.SetPublisherResponse) - mock_response.sdp = "answer_sdp" - mock_response.session_id = "sid" - # Configure the HasField method mock on the mock response - mock_response.HasField.return_value = False - - # Patch the specific method *on the instance* to return the mock response - with patch.object( - wrapper_client, - "SetPublisher", - new_callable=AsyncMock, - return_value=mock_response, - ) as mock_method: - ctx = Context() - request = signal_pb2.SetPublisherRequest(sdp="offer_sdp", session_id="sid") - - try: - # Call the method (which is now patched) - response = await wrapper_client.SetPublisher(ctx=ctx, request=request) - self.assertEqual(response.sdp, "answer_sdp") - self.assertEqual(response.session_id, "sid") - # Assert the patched method was called - mock_method.assert_awaited_once_with(ctx=ctx, request=request) - # Assert HasField was called correctly on the mock response - # Note: This check might be less reliable now, as the wrapper's internal - # logic that calls _check_response_for_error is bypassed by patching - # the method directly. We rely on the other tests to cover error handling. - # mock_response.HasField.assert_called_once_with('error') - except SfuRpcError as e: - self.fail(f"SfuRpcError raised unexpectedly: {e}") - - async def test_rpc_success_unspecified_error_code(self): - """Test RPC call succeeds when error code is ERROR_CODE_UNSPECIFIED.""" - wrapper_client = SignalClient(address="http://mock_address") - - # Create a MagicMock for the response - mock_response = MagicMock(spec=signal_pb2.SendAnswerResponse) - # Create a MagicMock for the error attribute - mock_error_obj = MagicMock(spec=models_pb2.Error) - mock_error_obj.code = models_pb2.ERROR_CODE_UNSPECIFIED - mock_error_obj.message = "" - mock_response.error = mock_error_obj - mock_response.HasField.return_value = True - - # Patch the method on the instance - with patch.object( - wrapper_client, - "SendAnswer", - new_callable=AsyncMock, - return_value=mock_response, - ) as mock_method: - ctx = Context() - request = signal_pb2.SendAnswerRequest(sdp="answer_sdp", session_id="sid") - - try: - response = await wrapper_client.SendAnswer(ctx=ctx, request=request) - self.assertIsNotNone(response) - # Since we patched the method, the wrapper's __getattribute__ and - # _check_response_for_error might not be fully exercised here. - # The crucial part is that no SfuRpcError was raised. - # mock_response.HasField.assert_called_once_with('error') - self.assertEqual(response.error.code, models_pb2.ERROR_CODE_UNSPECIFIED) - mock_method.assert_awaited_once_with(ctx=ctx, request=request) - except SfuRpcError as e: - self.fail(f"SfuRpcError raised unexpectedly for UNSPECIFIED code: {e}") - - async def test_rpc_error_raises_sfu_rpc_error(self): - """Test RPC call raises SfuRpcError when a specific error code is present.""" - wrapper_client = SignalClient(address="http://mock_address") - - error_code = models_pb2.ERROR_CODE_REQUEST_VALIDATION_FAILED - error_message = "Invalid request parameters" - method_name = "UpdateSubscriptions" - - # Create a MagicMock for the response - mock_response = MagicMock(spec=signal_pb2.UpdateSubscriptionsResponse) - mock_error_obj = MagicMock(spec=models_pb2.Error) - mock_error_obj.code = error_code - mock_error_obj.message = error_message - mock_response.error = mock_error_obj - mock_response.HasField.return_value = True - - # We need to test the wrapper's __getattribute__ logic which calls - # _check_response_for_error. Patching the method itself bypasses this. - # Instead, we patch the *underlying* method that __getattribute__ calls via super() - # This requires knowing the structure, let's assume it calls super().UpdateSubscriptions - # The path is tricky, it needs to be the *actual* base class method. - # Let's patch the _make_request method in the embedded client, as that's - # where the actual call happens before error checking in the wrapper. - - # Path to the embedded client's _make_request - MAKE_REQUEST_PATH = "getstream.video.rtc.twirp_async_client_embed.AsyncTwirpClient._make_request" - - with patch( - MAKE_REQUEST_PATH, new_callable=AsyncMock, return_value=mock_response - ) as mock_make_request: - ctx = Context() - request = signal_pb2.UpdateSubscriptionsRequest(session_id="sid") - - # Now call the wrapper method - this should trigger __getattribute__, - # which calls the original method, which calls the *patched* _make_request - with self.assertRaises(SfuRpcError) as cm: - await wrapper_client.UpdateSubscriptions(ctx=ctx, request=request) - - self.assertEqual(cm.exception.code, error_code) - self.assertEqual(cm.exception.message, error_message) - # The method name comes from __getattribute__ using the attribute name - self.assertEqual(cm.exception.method_name, method_name) - - # Assert the underlying _make_request was called (indirectly via the original method) - mock_make_request.assert_awaited_once() - # We can't easily assert HasField was called here, as it happens inside - # _check_response_for_error which is called by the wrapper's logic. - - -# Example of how to run these tests (if the file is run directly) -if __name__ == "__main__": - unittest.main() From 20b8f1bbc0ddb249830513c1c5235f6c203858c2 Mon Sep 17 00:00:00 2001 From: Tommaso Barbugli Date: Tue, 11 Nov 2025 16:32:25 +0100 Subject: [PATCH 4/4] adjusted AsyncTwirpClient class --- generate_webrtc.sh | 2 +- .../video/sfu/signal_rpc/signal_twirp.py | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/generate_webrtc.sh b/generate_webrtc.sh index f77e87b2..357b29d8 100755 --- a/generate_webrtc.sh +++ b/generate_webrtc.sh @@ -56,7 +56,7 @@ if ! echo "$PATH" | grep -q "$(go env GOPATH)/bin"; then fi echo "Installing protoc-gen-twirpy..." -go install github.com/tbarbugli/twirpy/protoc-gen-twirpy@111deed +go install github.com/tbarbugli/twirpy/protoc-gen-twirpy@423caa6 # Get the path to protoc PROTOC_PATH=$(command -v protoc) diff --git a/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py b/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py index 46d849e0..1a6a4fb2 100644 --- a/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py +++ b/getstream/video/rtc/pb/stream/video/sfu/signal_rpc/signal_twirp.py @@ -16,6 +16,8 @@ from twirp import exceptions from twirp import errors +from urllib.parse import urljoin + _sym_db = _symbol_database.Default() @@ -219,6 +221,9 @@ class AsyncTwirpClient: def __init__( self, address: str, session: Optional[aiohttp.ClientSession] = None ) -> None: + # Ensure address ends with a slash for urljoin to work correctly + if not address.endswith("/"): + address += "/" self._address = address self._session = session @@ -233,12 +238,20 @@ async def _make_request( if session is None: session = self._session + + _session_created = False if not isinstance(session, aiohttp.ClientSession): - raise TypeError(f"invalid session type '{type(session).__name__}'") + session = aiohttp.ClientSession() + _session_created = True + + # Construct the full URL by joining the base address and the path + # Remove leading slash from url path if present, as self._address ends with / + full_url = urljoin(self._address, url.lstrip("/")) try: + # Use the correctly constructed full_url async with await session.post( - url=url, data=request.SerializeToString(), **kwargs + url=full_url, data=request.SerializeToString(), **kwargs ) as resp: if resp.status == 200: response = response_obj() @@ -257,11 +270,16 @@ async def _make_request( meta={"original_exception": e}, ) except aiohttp.ServerConnectionError as e: + # This catches more connection-related errors like ClientConnectorError raise exceptions.TwirpServerException( code=errors.Errors.Unavailable, message=str(e), meta={"original_exception": e}, ) + finally: + # Close the session only if we created it internally + if _session_created and session: + await session.close() class AsyncSignalServerClient(AsyncTwirpClient):