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