diff --git a/.gitmodules b/.gitmodules index cbbd96d2..dc7ce931 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "client-sdk-rust"] - path = client-sdk-rust + path = rust-sdks url = https://github.com/livekit/client-sdk-rust diff --git a/client-sdk-rust b/client-sdk-rust deleted file mode 160000 index b968d3bb..00000000 --- a/client-sdk-rust +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b968d3bb1edd30ad3331a7f174c958d4d0346077 diff --git a/examples/basic_room.py b/examples/basic_room.py index 3c071407..bac887fe 100644 --- a/examples/basic_room.py +++ b/examples/basic_room.py @@ -5,8 +5,8 @@ import livekit -URL = 'ws://localhost:7880' -TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY' # noqa +URL = 'wss://nativesdk.livekit.cloud' +TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDQ2NzU5OTAsImlzcyI6IkFQSXM4eUdIS0FLZWYyWCIsIm5iZiI6MTY5NTY3NTk5MCwic3ViIjoiY2xpZW50IiwidmlkZW8iOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuUHVibGlzaERhdGEiOnRydWUsImNhblN1YnNjcmliZSI6dHJ1ZSwicm9vbSI6Im9haS1kZWJ1ZyIsInJvb21Kb2luIjp0cnVlfX0.Q7DYepLE1dIkl-ZAYwtFwnBFoInYUX9iuiNRZpiT1tE' async def main(room: livekit.Room) -> None: @@ -118,7 +118,8 @@ def on_reconnected() -> None: logging.info("participants: %s", room.participants) await asyncio.sleep(2) - await room.local_participant.publish_data("hello world") + await room.disconnect() + logging.info("disconnected") if __name__ == "__main__": diff --git a/examples/e2ee.py b/examples/e2ee.py index a5f0dc62..9e65d870 100644 --- a/examples/e2ee.py +++ b/examples/e2ee.py @@ -6,11 +6,11 @@ import livekit -URL = 'ws://localhost:7880' -TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY' # noqa +URL = 'wss://nativesdk.livekit.cloud' +TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MDQ2NzU5OTAsImlzcyI6IkFQSXM4eUdIS0FLZWYyWCIsIm5iZiI6MTY5NTY3NTk5MCwic3ViIjoiY2xpZW50IiwidmlkZW8iOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuUHVibGlzaERhdGEiOnRydWUsImNhblN1YnNjcmliZSI6dHJ1ZSwicm9vbSI6Im9haS1kZWJ1ZyIsInJvb21Kb2luIjp0cnVlfX0.Q7DYepLE1dIkl-ZAYwtFwnBFoInYUX9iuiNRZpiT1tE' # ("livekitrocks") this is our shared key, it must match the one used by your clients -SHARED_KEY = b"livekitrocks" +SHARED_KEY = b"password" async def draw_cube(source: livekit.VideoSource): diff --git a/examples/publish_wave.py b/examples/publish_wave.py index 598c9679..3d131cef 100644 --- a/examples/publish_wave.py +++ b/examples/publish_wave.py @@ -6,14 +6,15 @@ import livekit -URL = 'ws://localhost:7880' -TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY' # noqa +URL = 'wss://nativesdk.livekit.cloud' +TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTU2OTcyODYsImlzcyI6IkFQSXM4eUdIS0FLZWYyWCIsIm5iZiI6MTY5NTY3NTY4Niwic3ViIjoicHl0aG8gIiwidmlkZW8iOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuUHVibGlzaERhdGEiOnRydWUsImNhblN1YnNjcmliZSI6dHJ1ZSwicm9vbSI6Im9haS1kZWJ1ZyIsInJvb21Kb2luIjp0cnVlfX0.MRtWbEzwVd0gjGj-agU4n3OZyfIDuxe9MKu6zbx7M_M' SAMPLE_RATE = 48000 NUM_CHANNELS = 1 -async def publish_frames(source: livekit.AudioSource, frequency: int): +async def publish_sine(source: livekit.AudioSource): + frequency = 440 amplitude = 32767 # for 16-bit audio samples_per_channel = 480 # 10ms at 48kHz time = np.arange(samples_per_channel) / SAMPLE_RATE @@ -36,19 +37,62 @@ async def publish_frames(source: livekit.AudioSource, frequency: int): total_samples += samples_per_channel -async def main(room: livekit.Room) -> None: +async def publish_pulse(source: livekit.AudioSource): + frequency = 440 + modulation_frequency = 1.0 # 1Hz for beep once every second + beep_duration = 0.5 # Beep for half a second, then silence for half a second + amplitude = 32767 # for 16-bit audio + samples_per_channel = 480 # 10ms at 48kHz + total_samples = 0 + + audio_frame = livekit.AudioFrame.create( + SAMPLE_RATE, NUM_CHANNELS, samples_per_channel) + + audio_data = np.ctypeslib.as_array(audio_frame.data) + + while True: + time = (total_samples + np.arange(samples_per_channel)) / SAMPLE_RATE + + sine_wave = amplitude * np.sin(2 * np.pi * frequency * time) + + # Square wave for clear beep effect + square_wave = ( + np.sign(np.sin(2 * np.pi * modulation_frequency * time)) + 1) / 2 + # Zero out the sine wave where the square wave is zero (during the "off" portion of the beep) + beeping_sine = sine_wave * square_wave + + # Limit beep duration + beeping_sine[time % (1/modulation_frequency) > beep_duration] = 0 - @room.on("participant_disconnected") - def on_participant_disconnect(participant: livekit.Participant, *_): - logging.info("participant disconnected: %s", participant.identity) + np.copyto(audio_data, beeping_sine.astype(np.int16)) + await source.capture_frame(audio_frame) + + total_samples += samples_per_channel + + +async def main(room: livekit.Room) -> None: logging.info("connecting to %s", URL) try: e2ee_options = livekit.E2EEOptions() - e2ee_options.key_provider_options.shared_key = b"password" + e2ee_options.key_provider_options.shared_key = b'password' + + async def handle_disconnect(): + logging.info("disconnecting") + await room.disconnect() + logging.info("disconnected") + + @room.on('participant_connected') + def on_participant_connected(participant): + logging.info("participant connected: %s", participant.identity) + + @room.on('participant_disconnected') + def on_participant_disconnected(participant): + logging.info("participant disconnected: %s", participant.identity) + # Exit on participant_disconnect + asyncio.ensure_future(handle_disconnect()) await room.connect(URL, TOKEN, options=livekit.RoomOptions( - auto_subscribe=True, e2ee=e2ee_options )) logging.info("connected to room %s", room.name) @@ -56,15 +100,32 @@ def on_participant_disconnect(participant: livekit.Participant, *_): logging.error("failed to connect to the room: %s", e) return + @room.on('e2ee_state_changed') + def e2ee_changed(participant, state): + logging.info("decryption changed %s: %s", participant.identity, state) + # publish a track source = livekit.AudioSource(SAMPLE_RATE, NUM_CHANNELS) track = livekit.LocalAudioTrack.create_audio_track("sinewave", source) - options = livekit.TrackPublishOptions() - options.source = livekit.TrackSource.SOURCE_MICROPHONE + options = livekit.TrackPublishOptions( + source=livekit.TrackSource.SOURCE_MICROPHONE, + ) publication = await room.local_participant.publish_track(track, options) logging.info("published track %s", publication.sid) + asyncio.ensure_future(publish_sine(source)) + + # await asyncio.sleep(1) + + source2 = livekit.AudioSource(SAMPLE_RATE, NUM_CHANNELS) + track2 = livekit.LocalAudioTrack.create_audio_track("sinewave2", source2) + publication = await room.local_participant.publish_track(track2, options) + logging.info("published track 2 %s", publication.sid) + asyncio.ensure_future(publish_sine(source2)) - asyncio.ensure_future(publish_frames(source, 440)) + await asyncio.sleep(10) + + await room.disconnect() + print("disconnected") if __name__ == "__main__": @@ -88,3 +149,70 @@ async def cleanup(): loop.run_forever() finally: loop.close() + + +# theomonnom@Theos-MacBook-Pro ~/l/client-sdk-python (main)> python3 examples/publish_wave.py +# [2023-09-26T00:11:48Z INFO livekit_ffi::server::requests] initializing ffi server v0.3.6 +# INFO:root:connecting to wss://nativesdk.livekit.cloud +# [2023-09-26T00:11:48Z DEBUG livekit::rtc_engine::lk_runtime] LkRuntime::new() +# [2023-09-26T00:11:48Z INFO livekit_api::signal_client::signal_stream] connecting to SignalClient: wss://nativesdk.livekit.cloud/rtc?sdk=rust&access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTU2OTcyODYsImlzcyI6IkFQSXM4eUdIS0FLZWYyWCIsIm5iZiI6MTY5NTY3NTY4Niwic3ViIjoicHl0aG8gIiwidmlkZW8iOnsiY2FuUHVibGlzaCI6dHJ1ZSwiY2FuUHVibGlzaERhdGEiOnRydWUsImNhblN1YnNjcmliZSI6dHJ1ZSwicm9vbSI6Im9haS1kZWJ1ZyIsInJvb21Kb2luIjp0cnVlfX0.MRtWbEzwVd0gjGj-agU4n3OZyfIDuxe9MKu6zbx7M_M&protocol=8&auto_subscribe=1&adaptive_stream=0 +# [2023-09-26T00:11:49Z INFO livekit::rtc_engine::rtc_session] received JoinResponse: JoinResponse { room: Some(Room { sid: "RM_HFp6RQsQpK2i", name: "oai-debug", empty_timeout: 300, max_participants: 0, creation_time: 1695686940, turn_password: "BY1ZV931j+Y7xpRS++8GB14bIm4=", enabled_codecs: [Codec { mime: "video/H264", fmtp_line: "" }, Codec { mime: "video/VP8", fmtp_line: "" }, Codec { mime: "audio/red", fmtp_line: "" }, Codec { mime: "audio/opus", fmtp_line: "" }], metadata: "", num_participants: 1, num_publishers: 1, active_recording: false, playout_delay: None }), participant: Some(ParticipantInfo { sid: "PA_978ZP7HJudtd", identity: "pytho ", state: Joining, tracks: [], metadata: "", joined_at: 1695687109, name: "", version: 0, permission: Some(ParticipantPermission { can_subscribe: true, can_publish: true, can_publish_data: true, can_publish_sources: [], hidden: false, recorder: false, can_update_metadata: false }), region: "sfo3", is_publisher: false }), other_participants: [], server_version: "1.4.5", ice_servers: [IceServer { urls: ["turn:ip-143-198-238-188.host.livekit.cloud:3478?transport=udp", "turn:143.198.238.188:3478?transport=udp", "turns:sfo3.turn.livekit.cloud:443?transport=tcp"], username: "kRHZ1pES3AlW4cTOfKIo4DLZMrslCaJkOKPcmLJoCC", credential: "WhvqkdRZcP9TtOdSmxwIuGhvefFqIx6GYPmVI4HBYSo" }], subscriber_primary: true, alternative_url: "", client_configuration: None, server_region: "US West", ping_timeout: 20, ping_interval: 10, server_info: Some(ServerInfo { edition: Cloud, version: "1.4.5", protocol: 10, region: "US West", node_id: "NM_SFO3_arbXR8UBDNUt", debug_info: "" }), sif_trailer: [174, 61, 236, 196, 169, 5, 26, 218] } +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] received subscriber offer: SessionDescription { r#type: "offer", sdp: "v=0\r\no=- 5993781426435561238 1695687109 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 40:73:41:22:E6:73:23:0F:7F:BC:F2:DD:E1:9F:A4:DF:9C:6D:D7:7A:89:70:06:FA:C0:BA:AC:55:D2:64:FC:95\r\na=ice-lite\r\na=extmap-allow-mixed\r\na=group:BUNDLE 0\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=setup:actpass\r\na=mid:0\r\na=sendrecv\r\na=sctp-port:5000\r\na=ice-ufrag:ifGXNytRcYANcNpK\r\na=ice-pwd:kDiiAVNWKOpSIHkyOemirVRRGvdgtSWP\r\n" } +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:3264501797 1 udp 2130706431 143.198.238.188 59325 typ host generation 0" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:2137467333 1 tcp 1671430143 143.198.238.188 7881 typ host tcptype passive generation 0" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] connection change, Connecting Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:3317103142 1 udp 2122260223 192.168.4.174 54684 typ host generation 0 ufrag dMHY network-id 1 network-cost 50" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:4074455543 1 tcp 1671430143 2604:a880:4:1d0::5d9:f000 7881 typ host tcptype passive generation 0" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:1328033815 1 udp 2130706431 2604:a880:4:1d0::5d9:f000 58169 typ host generation 0" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:3300296704 1 udp 1686052607 99.24.128.217 54684 typ srflx raddr 192.168.4.174 rport 54684 generation 0 ufrag dMHY network-id 1 network-cost 50" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] connection change, Connected Subscriber +# INFO:root:connected to room oai-debug +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] received data channel: DataChannel { label: "_reliable", state: Open } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] received data channel: DataChannel { label: "_lossy", state: Open } Subscriber +# [2023-09-26T00:11:49Z INFO livekit::room] local track published: TR_AMjQtb8JmU9URK +# [2023-09-26T00:11:49Z INFO livekit_ffi::server::room] waiting for the PublishTrack callback to be sent +# INFO:root:published track TR_AMjQtb8JmU9URK +# [2023-09-26T00:11:49Z INFO livekit::room] local track published: TR_AM79x2khwzorTT +# INFO:root:published track 2 TR_AM79x2khwzorTT +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] negotiating the publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] sending publisher offer: SessionDescription { sdp_type: Offer } +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:4077834233 1 udp 2122260223 192.168.4.174 50393 typ host generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:4077834233 1 udp 2122260223 192.168.4.174 50496 typ host generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:4077834233 1 udp 2122260223 192.168.4.174 51797 typ host generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:553798262 1 udp 1686052607 99.24.128.217 50496 typ srflx raddr 192.168.4.174 rport 50496 generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:553798262 1 udp 1686052607 99.24.128.217 50393 typ srflx raddr 192.168.4.174 rport 50393 generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:553798262 1 udp 1686052607 99.24.128.217 51797 typ srflx raddr 192.168.4.174 rport 51797 generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] received publisher answer: SessionDescription { r#type: "answer", sdp: "v=0\r\no=- 131966927897248563 1695687109 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 BF:BF:9B:DB:29:EC:87:E4:5A:85:6E:A3:41:18:DA:71:3D:BF:9E:69:EF:CF:6A:03:59:A3:FE:E3:AF:9C:4D:B9\r\na=ice-lite\r\na=extmap-allow-mixed\r\na=group:BUNDLE 0 1 2\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63\r\nc=IN IP4 0.0.0.0\r\na=setup:active\r\na=mid:0\r\na=ice-ufrag:GrFrpjzKgAHGYiem\r\na=ice-pwd:vWsrubFbVhiuugtZQPKqCGqttfSXlPUO\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtcp-fb:111 transport-cc \r\na=rtcp-fb:111 nack \r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=recvonly\r\nm=audio 9 UDP/TLS/RTP/SAVPF 111 63\r\nc=IN IP4 0.0.0.0\r\na=setup:active\r\na=mid:1\r\na=ice-ufrag:GrFrpjzKgAHGYiem\r\na=ice-pwd:vWsrubFbVhiuugtZQPKqCGqttfSXlPUO\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime=10;useinbandfec=1\r\na=rtcp-fb:111 transport-cc \r\na=rtcp-fb:111 nack \r\na=rtpmap:63 red/48000/2\r\na=fmtp:63 111/111\r\na=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid\r\na=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\na=recvonly\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=setup:active\r\na=mid:2\r\na=sendrecv\r\na=sctp-port:5000\r\na=ice-ufrag:GrFrpjzKgAHGYiem\r\na=ice-pwd:vWsrubFbVhiuugtZQPKqCGqttfSXlPUO\r\n" } +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:3264501797 1 udp 2130706431 143.198.238.188 53189 typ host generation 0" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:2137467333 1 tcp 1671430143 143.198.238.188 7881 typ host tcptype passive generation 0" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] connection change, Connecting Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:1328033815 1 udp 2130706431 2604:a880:4:1d0::5d9:f000 56960 typ host generation 0" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] remote ice_candidate IceCandidate { candidate: "candidate:4074455543 1 tcp 1671430143 2604:a880:4:1d0::5d9:f000 7881 typ host tcptype passive generation 0" } Publisher +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:3145275582 1 tcp 1518280447 192.168.4.174 51424 typ host tcptype passive generation 0 ufrag dMHY network-id 1 network-cost 50" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:3145275582 1 tcp 1518280447 192.168.4.174 51426 typ host tcptype passive generation 0 ufrag dMHY network-id 1 network-cost 50" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:3145275582 1 tcp 1518280447 192.168.4.174 51428 typ host tcptype passive generation 0 ufrag dMHY network-id 1 network-cost 50" } Subscriber +# [2023-09-26T00:11:49Z DEBUG livekit::rtc_engine::rtc_session] local ice_candidate IceCandidate { candidate: "candidate:228858733 1 tcp 1518280447 192.168.4.174 51430 typ host tcptype passive generation 0 ufrag CZzs network-id 1 network-cost 50" } Publisher +# [2023-09-26T00:11:50Z DEBUG livekit::rtc_engine::rtc_session] connection change, Connected Publisher +# [2023-09-26T00:11:50Z INFO livekit::room] e2ee state changed for pytho : Ok +# [2023-09-26T00:11:50Z INFO livekit::room] e2ee state changed for pytho : Ok +# INFO:root:decryption changed pytho : 1 +# INFO:root:decryption changed pytho : 1 +# [2023-09-26T00:11:50Z INFO livekit::room] participant update: [ParticipantInfo { sid: "PA_978ZP7HJudtd", identity: "pytho ", state: Active, tracks: [TrackInfo { sid: "TR_AMjQtb8JmU9URK", r#type: Audio, name: "sinewave", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Microphone, layers: [], mime_type: "audio/opus", mid: "0", codecs: [], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }, TrackInfo { sid: "TR_AM79x2khwzorTT", r#type: Audio, name: "sinewave2", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Microphone, layers: [], mime_type: "audio/opus", mid: "1", codecs: [], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }], metadata: "", joined_at: 1695687109, name: "", version: 3, permission: Some(ParticipantPermission { can_subscribe: true, can_publish: true, can_publish_data: true, can_publish_sources: [], hidden: false, recorder: false, can_update_metadata: false }), region: "sfo3", is_publisher: true }] +# [2023-09-26T00:11:50Z INFO livekit::room] participant update: [ParticipantInfo { sid: "PA_978ZP7HJudtd", identity: "pytho ", state: Active, tracks: [TrackInfo { sid: "TR_AM79x2khwzorTT", r#type: Audio, name: "sinewave2", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Microphone, layers: [], mime_type: "audio/opus", mid: "1", codecs: [], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }, TrackInfo { sid: "TR_AMjQtb8JmU9URK", r#type: Audio, name: "sinewave", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Microphone, layers: [], mime_type: "audio/opus", mid: "0", codecs: [], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }], metadata: "", joined_at: 1695687109, name: "", version: 3, permission: Some(ParticipantPermission { can_subscribe: true, can_publish: true, can_publish_data: true, can_publish_sources: [], hidden: false, recorder: false, can_update_metadata: false }), region: "sfo3", is_publisher: true }] +# [2023-09-26T00:11:50Z INFO livekit::room] participant update: [ParticipantInfo { sid: "PA_978ZP7HJudtd", identity: "pytho ", state: Active, tracks: [TrackInfo { sid: "TR_AM79x2khwzorTT", r#type: Audio, name: "sinewave2", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Microphone, layers: [], mime_type: "audio/opus", mid: "1", codecs: [], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }, TrackInfo { sid: "TR_AMjQtb8JmU9URK", r#type: Audio, name: "sinewave", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Microphone, layers: [], mime_type: "audio/opus", mid: "0", codecs: [], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }], metadata: "", joined_at: 1695687109, name: "", version: 3, permission: Some(ParticipantPermission { can_subscribe: true, can_publish: true, can_publish_data: true, can_publish_sources: [], hidden: false, recorder: false, can_update_metadata: false }), region: "sfo3", is_publisher: true }] +# [2023-09-26T00:11:50Z INFO livekit::room] participant update: [ParticipantInfo { sid: "PA_QZfHXKyd76rt", identity: "client", state: Active, tracks: [TrackInfo { sid: "TR_VC38LEKmaZYXAd", r#type: Video, name: "cube", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Camera, layers: [VideoLayer { quality: Low, width: 0, height: 0, bitrate: 80000, ssrc: 0 }], mime_type: "video/VP8", mid: "0", codecs: [SimulcastCodecInfo { mime_type: "video/VP8", mid: "0", cid: "", layers: [VideoLayer { quality: Low, width: 0, height: 0, bitrate: 80000, ssrc: 0 }] }], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }], metadata: "", joined_at: 1695687109, name: "", version: 3, permission: Some(ParticipantPermission { can_subscribe: true, can_publish: true, can_publish_data: true, can_publish_sources: [], hidden: false, recorder: false, can_update_metadata: false }), region: "sfo3", is_publisher: true }] +# [2023-09-26T00:11:50Z INFO livekit::room] new participant: PA_QZfHXKyd76rt +# ERROR:root:error running user callback for track_published: 'PA_QZfHXKyd76rt' +# INFO:root:participant connected: client +# [2023-09-26T00:11:50Z INFO livekit::room] participant update: [ParticipantInfo { sid: "PA_QZfHXKyd76rt", identity: "client", state: Active, tracks: [TrackInfo { sid: "TR_VC38LEKmaZYXAd", r#type: Video, name: "cube", muted: false, width: 0, height: 0, simulcast: false, disable_dtx: true, source: Camera, layers: [VideoLayer { quality: Low, width: 0, height: 0, bitrate: 80000, ssrc: 0 }], mime_type: "video/VP8", mid: "0", codecs: [SimulcastCodecInfo { mime_type: "video/VP8", mid: "0", cid: "", layers: [VideoLayer { quality: Low, width: 0, height: 0, bitrate: 80000, ssrc: 0 }] }], stereo: false, disable_red: true, encryption: Gcm, stream: "camera" }], metadata: "", joined_at: 1695687109, name: "", version: 3, permission: Some(ParticipantPermission { can_subscribe: true, can_publish: true, can_publish_data: true, can_publish_sources: [], hidden: false, recorder: false, can_update_metadata: false }), region: "sfo3", is_publisher: true }] +# [2023-09-26T00:11:50Z DEBUG livekit::rtc_engine::rtc_session] received subscriber offer: SessionDescription { r#type: "offer", sdp: "v=0\r\no=- 5993781426435561238 1695687110 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\na=fingerprint:sha-256 40:73:41:22:E6:73:23:0F:7F:BC:F2:DD:E1:9F:A4:DF:9C:6D:D7:7A:89:70:06:FA:C0:BA:AC:55:D2:64:FC:95\r\na=ice-lite\r\na=extmap-allow-mixed\r\na=group:BUNDLE 0 1\r\nm=application 9 UDP/DTLS/SCTP webrtc-datachannel\r\nc=IN IP4 0.0.0.0\r\na=setup:actpass\r\na=mid:0\r\na=sendrecv\r\na=sctp-port:5000\r\na=ice-ufrag:ifGXNytRcYANcNpK\r\na=ice-pwd:kDiiAVNWKOpSIHkyOemirVRRGvdgtSWP\r\na=candidate:3264501797 1 udp 2130706431 143.198.238.188 59325 typ host\r\na=candidate:3264501797 2 udp 2130706431 143.198.238.188 59325 typ host\r\na=candidate:2137467333 1 tcp 1671430143 143.198.238.188 7881 typ host tcptype passive\r\na=candidate:2137467333 2 tcp 1671430143 143.198.238.188 7881 typ host tcptype passive\r\na=candidate:4074455543 1 tcp 1671430143 2604:a880:4:1d0::5d9:f000 7881 typ host tcptype passive\r\na=candidate:4074455543 2 tcp 1671430143 2604:a880:4:1d0::5d9:f000 7881 typ host tcptype passive\r\na=candidate:1328033815 1 udp 2130706431 2604:a880:4:1d0::5d9:f000 58169 typ host\r\na=candidate:1328033815 2 udp 2130706431 2604:a880:4:1d0::5d9:f000 58169 typ host\r\na=end-of-candidates\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 125 108\r\nc=IN IP4 0.0.0.0\r\na=setup:actpass\r\na=mid:1\r\na=ice-ufrag:ifGXNytRcYANcNpK\r\na=ice-pwd:kDiiAVNWKOpSIHkyOemirVRRGvdgtSWP\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 VP8/90000\r\na=rtcp-fb:96 ccm fir\r\na=rtcp-fb:96 nack \r\na=rtcp-fb:96 nack pli\r\na=rtcp-fb:96 goog-remb \r\na=rtpmap:125 H264/90000\r\na=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtcp-fb:125 ccm fir\r\na=rtcp-fb:125 nack \r\na=rtcp-fb:125 nack pli\r\na=rtcp-fb:125 goog-remb \r\na=rtpmap:108 H264/90000\r\na=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f\r\na=rtcp-fb:108 ccm fir\r\na=rtcp-fb:108 nack \r\na=rtcp-fb:108 nack pli\r\na=rtcp-fb:108 goog-remb \r\na=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\r\na=extmap:1 https://aomediacodec.github.io/av1-rtp-spec/#dependency-descriptor-rtp-header-extension\r\na=ssrc:2468787415 cname:PA_QZfHXKyd76rt|TR_VC38LEKmaZYXAd\r\na=ssrc:2468787415 msid:PA_QZfHXKyd76rt|TR_VC38LEKmaZYXAd TR_VC38LEKmaZYXAd\r\na=ssrc:2468787415 mslabel:PA_QZfHXKyd76rt|TR_VC38LEKmaZYXAd\r\na=ssrc:2468787415 label:TR_VC38LEKmaZYXAd\r\na=msid:PA_QZfHXKyd76rt|TR_VC38LEKmaZYXAd TR_VC38LEKmaZYXAd\r\na=sendrecv\r\n" } +# [2023-09-26T00:11:50Z INFO livekit::room] track subscribed: TR_VC38LEKmaZYXAd +# ERROR:root:error running user callback for track_subscribed: 'TR_VC38LEKmaZYXAd' +# [2023-09-26T00:11:50Z INFO livekit::room] e2ee state changed for client: Ok +# INFO:root:decryption changed client: 1 +# [2023-09-26T00:11:59Z DEBUG livekit::rtc_engine::rtc_session] rtc_session_task closed +# [2023-09-26T00:11:59Z DEBUG livekit::rtc_engine::rtc_session] closing signal_task +# [2023-09-26T00:11:59Z DEBUG livekit_api::signal_client::signal_stream] server closed the connection: None +# [2023-09-26T00:11:59Z DEBUG livekit::rtc_engine] engine task closed +# [2023-09-26T00:11:59Z DEBUG livekit::room] room_task closed +# disconnected diff --git a/generate_proto.sh b/generate_proto.sh index 29a02fad..342bc2ec 100755 --- a/generate_proto.sh +++ b/generate_proto.sh @@ -16,7 +16,7 @@ # This script requires protobuf-compiler and https://github.com/nipunn1313/mypy-protobuf -FFI_PROTOCOL=./client-sdk-rust/livekit-ffi/protocol +FFI_PROTOCOL=./rust-sdks/livekit-ffi/protocol OUT_PYTHON=./livekit/_proto protoc \ diff --git a/livekit/participant.py b/livekit/participant.py index feb64925..7ef4b2be 100644 --- a/livekit/participant.py +++ b/livekit/participant.py @@ -103,17 +103,43 @@ async def publish_data(self, req.publish_data.destination_sids.extend(sids) try: - queue = self._room_queue.subscribe() + queue = ffi_client.queue.subscribe() resp = ffi_client.request(req) cb = await queue.wait_for(lambda e: e.publish_data.async_id == resp.publish_data.async_id) - queue.task_done() finally: - self._room_queue.unsubscribe(queue) + ffi_client.queue.unsubscribe(queue) if cb.publish_data.error: raise PublishDataError(cb.publish_data.error) + async def update_metadata(self, metadata: str) -> None: + req = proto_ffi.FfiRequest() + req.update_local_metadata.local_participant_handle = self._ffi_handle.handle + req.update_local_metadata.metadata = metadata + + try: + queue = ffi_client.queue.subscribe() + resp = ffi_client.request(req) + cb = await queue.wait_for(lambda e: e.update_local_metadata.async_id == + resp.update_local_metadata.async_id) + finally: + ffi_client.queue.unsubscribe(queue) + + async def update_name(self, name: str) -> None: + req = proto_ffi.FfiRequest() + req.update_local_name.local_participant_handle = self._ffi_handle.handle + req.update_local_name.name = name + + try: + queue = ffi_client.queue.subscribe() + resp = ffi_client.request(req) + cb = await queue.wait_for(lambda e: e.update_local_name.async_id == + resp.update_local_name.async_id) + finally: + ffi_client.queue.unsubscribe(queue) + + async def publish_track(self, track: Track, options: TrackPublishOptions) \ -> TrackPublication: if not isinstance(track, LocalAudioTrack) \ diff --git a/livekit/room.py b/livekit/room.py index fd7962b0..2f93becc 100644 --- a/livekit/room.py +++ b/livekit/room.py @@ -35,9 +35,9 @@ @dataclass class RtcConfiguration: - ice_transport_type: proto_room.IceTransportType = \ + ice_transport_type: proto_room.IceTransportType.ValueType = \ proto_room.IceTransportType.TRANSPORT_ALL - continual_gathering_policy: proto_room.ContinualGatheringPolicy = \ + continual_gathering_policy: proto_room.ContinualGatheringPolicy.ValueType = \ proto_room.ContinualGatheringPolicy.GATHER_CONTINUALLY ice_servers: list[proto_room.IceServer] = field(default_factory=list) @@ -274,12 +274,29 @@ def _on_room_event(self, event: proto_room.RoomEvent): speakers.append(self._retrieve_participant(sid)) self.emit('active_speakers_changed', speakers) + elif which == 'room_metadata_changed': + old_metadata = self.metadata + self._info.metadata = event.room_metadata_changed.metadata + self.emit('room_metadata_changed', old_metadata, self.metadata) + elif which == 'participant_metadata_changed': + sid = event.participant_metadata_changed.participant_sid + participant = self._retrieve_participant(sid) + old_metadata = participant.metadata + participant._info.metadata = event.participant_metadata_changed.metadata + self.emit('participant_metadata_changed', + participant, old_metadata, participant.metadata) + elif which == 'participant_name_changed': + sid = event.participant_name_changed.participant_sid + participant = self._retrieve_participant(sid) + old_name = participant.name + participant._info.name = event.participant_name_changed.name + self.emit('participant_name_changed', + participant, old_name, participant.name) elif which == 'connection_quality_changed': sid = event.connection_quality_changed.participant_sid - p = self._retrieve_participant(sid) - + participant = self._retrieve_participant(sid) self.emit('connection_quality_changed', - p, event.connection_quality_changed.quality) + participant, event.connection_quality_changed.quality) elif which == 'data_received': rparticipant = self.participants[event.data_received.participant_sid] owned_buffer_info = event.data_received.data diff --git a/rust-sdks b/rust-sdks new file mode 160000 index 00000000..d7ec6fbd --- /dev/null +++ b/rust-sdks @@ -0,0 +1 @@ +Subproject commit d7ec6fbd4209adae1b10cc33feb5f013acb880e4 diff --git a/setup.py b/setup.py index 974be95b..b1831c1c 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ class BuildPyCommand(setuptools.command.build_py.build_py): def run(self): - download_script = here / 'client-sdk-rust' / 'download_ffi.py' + download_script = here / 'rust-sdks' / 'download_ffi.py' cmd = ['python3', download_script.absolute(), '--output', 'livekit/resources']