1use std::{sync::Arc, time::Duration};
2
3use futures::StreamExt;
4use gpui::KeyBinding;
5use live_kit_client2::{
6 LocalAudioTrack, LocalVideoTrack, RemoteAudioTrackUpdate, RemoteVideoTrackUpdate, Room,
7};
8use live_kit_server::token::{self, VideoGrant};
9use log::LevelFilter;
10use serde_derive::Deserialize;
11use simplelog::SimpleLogger;
12
13#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
14struct Quit;
15
16fn main() {
17 SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
18
19 gpui::App::production(Arc::new(())).run(|cx| {
20 #[cfg(any(test, feature = "test-support"))]
21 println!("USING TEST LIVEKIT");
22
23 #[cfg(not(any(test, feature = "test-support")))]
24 println!("USING REAL LIVEKIT");
25
26 cx.activate(true);
27
28 cx.on_action(quit);
29 cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
30
31 // todo!()
32 // cx.set_menus(vec![Menu {
33 // name: "Zed",
34 // items: vec![MenuItem::Action {
35 // name: "Quit",
36 // action: Box::new(Quit),
37 // os_action: None,
38 // }],
39 // }]);
40
41 let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap_or("http://localhost:7880".into());
42 let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap_or("devkey".into());
43 let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap_or("secret".into());
44
45 cx.spawn(|cx| async move {
46 let user_a_token = token::create(
47 &live_kit_key,
48 &live_kit_secret,
49 Some("test-participant-1"),
50 VideoGrant::to_join("test-room"),
51 )
52 .unwrap();
53 let room_a = Room::new();
54 room_a.connect(&live_kit_url, &user_a_token).await.unwrap();
55
56 let user2_token = token::create(
57 &live_kit_key,
58 &live_kit_secret,
59 Some("test-participant-2"),
60 VideoGrant::to_join("test-room"),
61 )
62 .unwrap();
63 let room_b = Room::new();
64 room_b.connect(&live_kit_url, &user2_token).await.unwrap();
65
66 let mut audio_track_updates = room_b.remote_audio_track_updates();
67 let audio_track = LocalAudioTrack::create();
68 let audio_track_publication = room_a.publish_audio_track(audio_track).await.unwrap();
69
70 if let RemoteAudioTrackUpdate::Subscribed(track, _) =
71 audio_track_updates.next().await.unwrap()
72 {
73 let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
74 assert_eq!(remote_tracks.len(), 1);
75 assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1");
76 assert_eq!(track.publisher_id(), "test-participant-1");
77 } else {
78 panic!("unexpected message");
79 }
80
81 audio_track_publication.set_mute(true).await.unwrap();
82
83 println!("waiting for mute changed!");
84 if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } =
85 audio_track_updates.next().await.unwrap()
86 {
87 let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
88 assert_eq!(remote_tracks[0].sid(), track_id);
89 assert_eq!(muted, true);
90 } else {
91 panic!("unexpected message");
92 }
93
94 audio_track_publication.set_mute(false).await.unwrap();
95
96 if let RemoteAudioTrackUpdate::MuteChanged { track_id, muted } =
97 audio_track_updates.next().await.unwrap()
98 {
99 let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
100 assert_eq!(remote_tracks[0].sid(), track_id);
101 assert_eq!(muted, false);
102 } else {
103 panic!("unexpected message");
104 }
105
106 println!("Pausing for 5 seconds to test audio, make some noise!");
107 let timer = cx.background_executor().timer(Duration::from_secs(5));
108 timer.await;
109 let remote_audio_track = room_b
110 .remote_audio_tracks("test-participant-1")
111 .pop()
112 .unwrap();
113 room_a.unpublish_track(audio_track_publication);
114
115 // Clear out any active speakers changed messages
116 let mut next = audio_track_updates.next().await.unwrap();
117 while let RemoteAudioTrackUpdate::ActiveSpeakersChanged { speakers } = next {
118 println!("Speakers changed: {:?}", speakers);
119 next = audio_track_updates.next().await.unwrap();
120 }
121
122 if let RemoteAudioTrackUpdate::Unsubscribed {
123 publisher_id,
124 track_id,
125 } = next
126 {
127 assert_eq!(publisher_id, "test-participant-1");
128 assert_eq!(remote_audio_track.sid(), track_id);
129 assert_eq!(room_b.remote_audio_tracks("test-participant-1").len(), 0);
130 } else {
131 panic!("unexpected message");
132 }
133
134 let mut video_track_updates = room_b.remote_video_track_updates();
135 let displays = room_a.display_sources().await.unwrap();
136 let display = displays.into_iter().next().unwrap();
137
138 let local_video_track = LocalVideoTrack::screen_share_for_display(&display);
139 let local_video_track_publication =
140 room_a.publish_video_track(local_video_track).await.unwrap();
141
142 if let RemoteVideoTrackUpdate::Subscribed(track) =
143 video_track_updates.next().await.unwrap()
144 {
145 let remote_video_tracks = room_b.remote_video_tracks("test-participant-1");
146 assert_eq!(remote_video_tracks.len(), 1);
147 assert_eq!(remote_video_tracks[0].publisher_id(), "test-participant-1");
148 assert_eq!(track.publisher_id(), "test-participant-1");
149 } else {
150 panic!("unexpected message");
151 }
152
153 let remote_video_track = room_b
154 .remote_video_tracks("test-participant-1")
155 .pop()
156 .unwrap();
157 room_a.unpublish_track(local_video_track_publication);
158 if let RemoteVideoTrackUpdate::Unsubscribed {
159 publisher_id,
160 track_id,
161 } = video_track_updates.next().await.unwrap()
162 {
163 assert_eq!(publisher_id, "test-participant-1");
164 assert_eq!(remote_video_track.sid(), track_id);
165 assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0);
166 } else {
167 panic!("unexpected message");
168 }
169
170 cx.update(|cx| cx.quit()).ok();
171 })
172 .detach();
173 });
174}
175
176fn quit(_: &Quit, cx: &mut gpui::AppContext) {
177 cx.quit();
178}