1use crate::{
2 AudioStream, ConnectionQuality, LocalAudioTrack, LocalTrackPublication, LocalVideoTrack,
3 Participant, ParticipantIdentity, RemoteTrack, RemoteTrackPublication, TrackSid,
4 test::{Room, WeakRoom},
5};
6use anyhow::Result;
7use collections::HashMap;
8use gpui::{
9 AsyncApp, DevicePixels, ScreenCaptureSource, ScreenCaptureStream, SourceMetadata, size,
10};
11use std::sync::{Arc, atomic::AtomicU64};
12
13#[derive(Clone, Debug)]
14pub struct LocalParticipant {
15 pub(crate) identity: ParticipantIdentity,
16 pub(crate) room: Room,
17}
18
19#[derive(Clone, Debug)]
20pub struct RemoteParticipant {
21 pub(crate) identity: ParticipantIdentity,
22 pub(crate) room: WeakRoom,
23}
24
25impl Participant {
26 pub fn identity(&self) -> ParticipantIdentity {
27 match self {
28 Participant::Local(participant) => participant.identity.clone(),
29 Participant::Remote(participant) => participant.identity.clone(),
30 }
31 }
32
33 pub fn connection_quality(&self) -> ConnectionQuality {
34 match self {
35 Participant::Local(p) => p.connection_quality(),
36 Participant::Remote(p) => p.connection_quality(),
37 }
38 }
39
40 pub fn audio_level(&self) -> f32 {
41 match self {
42 Participant::Local(p) => p.audio_level(),
43 Participant::Remote(p) => p.audio_level(),
44 }
45 }
46}
47
48impl LocalParticipant {
49 pub fn connection_quality(&self) -> ConnectionQuality {
50 ConnectionQuality::Excellent
51 }
52
53 pub fn audio_level(&self) -> f32 {
54 0.0
55 }
56
57 pub async fn unpublish_track(&self, track: TrackSid, _cx: &AsyncApp) -> Result<()> {
58 self.room
59 .test_server()
60 .unpublish_track(self.room.token(), &track)
61 .await
62 }
63
64 pub(crate) async fn publish_microphone_track(
65 &self,
66 _cx: &AsyncApp,
67 ) -> Result<(LocalTrackPublication, AudioStream, Arc<AtomicU64>)> {
68 let this = self.clone();
69 let server = this.room.test_server();
70 let sid = server
71 .publish_audio_track(this.room.token(), &LocalAudioTrack {})
72 .await?;
73
74 Ok((
75 LocalTrackPublication {
76 room: self.room.downgrade(),
77 sid,
78 },
79 AudioStream {},
80 Arc::new(AtomicU64::new(0)),
81 ))
82 }
83
84 pub async fn publish_screenshare_track(
85 &self,
86 _source: &dyn ScreenCaptureSource,
87 _cx: &mut AsyncApp,
88 ) -> Result<(LocalTrackPublication, Box<dyn ScreenCaptureStream>)> {
89 let this = self.clone();
90 let server = this.room.test_server();
91 let sid = server
92 .publish_video_track(this.room.token(), LocalVideoTrack {})
93 .await?;
94 Ok((
95 LocalTrackPublication {
96 room: self.room.downgrade(),
97 sid,
98 },
99 Box::new(TestScreenCaptureStream {}),
100 ))
101 }
102
103 #[cfg(target_os = "linux")]
104 pub async fn publish_screenshare_track_wayland(
105 &self,
106 _cx: &mut AsyncApp,
107 ) -> Result<(
108 LocalTrackPublication,
109 Box<dyn ScreenCaptureStream>,
110 futures::channel::oneshot::Receiver<()>,
111 )> {
112 let (_failure_tx, failure_rx) = futures::channel::oneshot::channel();
113 let this = self.clone();
114 let server = this.room.test_server();
115 let sid = server
116 .publish_video_track(this.room.token(), LocalVideoTrack {})
117 .await?;
118 Ok((
119 LocalTrackPublication {
120 room: self.room.downgrade(),
121 sid,
122 },
123 Box::new(TestWaylandScreenCaptureStream::new()),
124 failure_rx,
125 ))
126 }
127}
128
129impl RemoteParticipant {
130 pub fn connection_quality(&self) -> ConnectionQuality {
131 ConnectionQuality::Excellent
132 }
133
134 pub fn audio_level(&self) -> f32 {
135 0.0
136 }
137
138 pub fn track_publications(&self) -> HashMap<TrackSid, RemoteTrackPublication> {
139 if let Some(room) = self.room.upgrade() {
140 let server = room.test_server();
141 let audio = server
142 .audio_tracks(room.token())
143 .unwrap()
144 .into_iter()
145 .filter(|track| track.publisher_id() == self.identity)
146 .map(|track| {
147 (
148 track.sid(),
149 RemoteTrackPublication {
150 sid: track.sid(),
151 room: self.room.clone(),
152 track: RemoteTrack::Audio(track),
153 },
154 )
155 });
156 let video = server
157 .video_tracks(room.token())
158 .unwrap()
159 .into_iter()
160 .filter(|track| track.publisher_id() == self.identity)
161 .map(|track| {
162 (
163 track.sid(),
164 RemoteTrackPublication {
165 sid: track.sid(),
166 room: self.room.clone(),
167 track: RemoteTrack::Video(track),
168 },
169 )
170 });
171 audio.chain(video).collect()
172 } else {
173 HashMap::default()
174 }
175 }
176
177 pub fn identity(&self) -> ParticipantIdentity {
178 self.identity.clone()
179 }
180}
181
182struct TestScreenCaptureStream;
183
184impl ScreenCaptureStream for TestScreenCaptureStream {
185 fn metadata(&self) -> Result<SourceMetadata> {
186 Ok(SourceMetadata {
187 id: 0,
188 is_main: None,
189 label: None,
190 resolution: size(DevicePixels(1), DevicePixels(1)),
191 })
192 }
193}
194
195#[cfg(target_os = "linux")]
196static NEXT_TEST_WAYLAND_SHARE_ID: AtomicU64 = AtomicU64::new(1);
197
198#[cfg(target_os = "linux")]
199struct TestWaylandScreenCaptureStream {
200 id: u64,
201}
202
203#[cfg(target_os = "linux")]
204impl TestWaylandScreenCaptureStream {
205 fn new() -> Self {
206 Self {
207 id: NEXT_TEST_WAYLAND_SHARE_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
208 }
209 }
210}
211
212#[cfg(target_os = "linux")]
213impl ScreenCaptureStream for TestWaylandScreenCaptureStream {
214 fn metadata(&self) -> Result<SourceMetadata> {
215 Ok(SourceMetadata {
216 id: self.id,
217 is_main: None,
218 label: None,
219 resolution: size(DevicePixels(1), DevicePixels(1)),
220 })
221 }
222}