test_app.rs

  1use futures::StreamExt;
  2use gpui::{
  3    actions,
  4    elements::{Canvas, *},
  5    keymap::Binding,
  6    platform::current::Surface,
  7    Menu, MenuItem, ViewContext,
  8};
  9use live_kit_client::{LocalVideoTrack, RemoteVideoTrackUpdate, Room};
 10use live_kit_server::token::{self, VideoGrant};
 11use log::LevelFilter;
 12use media::core_video::CVImageBuffer;
 13use postage::watch;
 14use simplelog::SimpleLogger;
 15use std::sync::Arc;
 16
 17actions!(capture, [Quit]);
 18
 19fn main() {
 20    SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 21
 22    gpui::App::new(()).unwrap().run(|cx| {
 23        cx.platform().activate(true);
 24        cx.add_global_action(quit);
 25
 26        cx.add_bindings([Binding::new("cmd-q", Quit, None)]);
 27        cx.set_menus(vec![Menu {
 28            name: "Zed",
 29            items: vec![MenuItem::Action {
 30                name: "Quit",
 31                action: Box::new(Quit),
 32            }],
 33        }]);
 34
 35        let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap_or("http://localhost:7880".into());
 36        let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap_or("devkey".into());
 37        let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap_or("secret".into());
 38
 39        cx.spawn(|cx| async move {
 40            let user_a_token = token::create(
 41                &live_kit_key,
 42                &live_kit_secret,
 43                Some("test-participant-1"),
 44                VideoGrant::to_join("test-room"),
 45            )
 46            .unwrap();
 47            let room_a = Room::new();
 48            room_a.connect(&live_kit_url, &user_a_token).await.unwrap();
 49
 50            let user2_token = token::create(
 51                &live_kit_key,
 52                &live_kit_secret,
 53                Some("test-participant-2"),
 54                VideoGrant::to_join("test-room"),
 55            )
 56            .unwrap();
 57            let room_b = Room::new();
 58            room_b.connect(&live_kit_url, &user2_token).await.unwrap();
 59
 60            let mut track_changes = room_b.remote_video_track_updates();
 61
 62            let displays = live_kit_client::display_sources().await.unwrap();
 63            let display = displays.into_iter().next().unwrap();
 64
 65            let track_a = LocalVideoTrack::screen_share_for_display(&display);
 66            let track_a_publication = room_a.publish_video_track(&track_a).await.unwrap();
 67
 68            if let RemoteVideoTrackUpdate::Subscribed(track) = track_changes.next().await.unwrap() {
 69                let remote_tracks = room_b.remote_video_tracks("test-participant-1");
 70                assert_eq!(remote_tracks.len(), 1);
 71                assert_eq!(remote_tracks[0].publisher_id(), "test-participant-1");
 72                assert_eq!(track.publisher_id(), "test-participant-1");
 73            } else {
 74                panic!("unexpected message");
 75            }
 76
 77            let remote_track = room_b
 78                .remote_video_tracks("test-participant-1")
 79                .pop()
 80                .unwrap();
 81            room_a.unpublish_track(track_a_publication);
 82            if let RemoteVideoTrackUpdate::Unsubscribed {
 83                publisher_id,
 84                track_id,
 85            } = track_changes.next().await.unwrap()
 86            {
 87                assert_eq!(publisher_id, "test-participant-1");
 88                assert_eq!(remote_track.sid(), track_id);
 89                assert_eq!(room_b.remote_video_tracks("test-participant-1").len(), 0);
 90            } else {
 91                panic!("unexpected message");
 92            }
 93
 94            cx.platform().quit();
 95        })
 96        .detach();
 97    });
 98}
 99
100struct ScreenCaptureView {
101    image_buffer: Option<CVImageBuffer>,
102    _room: Arc<Room>,
103}
104
105impl gpui::Entity for ScreenCaptureView {
106    type Event = ();
107}
108
109impl ScreenCaptureView {
110    pub fn new(room: Arc<Room>, cx: &mut ViewContext<Self>) -> Self {
111        let mut remote_video_tracks = room.remote_video_track_updates();
112        cx.spawn_weak(|this, mut cx| async move {
113            if let Some(video_track) = remote_video_tracks.next().await {
114                let (mut frames_tx, mut frames_rx) = watch::channel_with(None);
115                // video_track.add_renderer(move |frame| *frames_tx.borrow_mut() = Some(frame));
116
117                while let Some(frame) = frames_rx.next().await {
118                    if let Some(this) = this.upgrade(&cx) {
119                        this.update(&mut cx, |this, cx| {
120                            this.image_buffer = frame;
121                            cx.notify();
122                        });
123                    } else {
124                        break;
125                    }
126                }
127            }
128        })
129        .detach();
130
131        Self {
132            image_buffer: None,
133            _room: room,
134        }
135    }
136}
137
138impl gpui::View for ScreenCaptureView {
139    fn ui_name() -> &'static str {
140        "View"
141    }
142
143    fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
144        let image_buffer = self.image_buffer.clone();
145        let canvas = Canvas::new(move |bounds, _, cx| {
146            if let Some(image_buffer) = image_buffer.clone() {
147                cx.scene.push_surface(Surface {
148                    bounds,
149                    image_buffer,
150                });
151            }
152        });
153
154        if let Some(image_buffer) = self.image_buffer.as_ref() {
155            canvas
156                .constrained()
157                .with_width(image_buffer.width() as f32)
158                .with_height(image_buffer.height() as f32)
159                .aligned()
160                .boxed()
161        } else {
162            canvas.boxed()
163        }
164    }
165}
166
167fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
168    cx.platform().quit();
169}