remote_video_track_view.rs

  1use crate::track::RemoteVideoTrack;
  2use anyhow::Result;
  3use futures::StreamExt as _;
  4use gpui::{AppContext, Context, Empty, Entity, EventEmitter, IntoElement, Render, Task, Window};
  5
  6pub struct RemoteVideoTrackView {
  7    track: RemoteVideoTrack,
  8    latest_frame: Option<crate::RemoteVideoFrame>,
  9    #[cfg(not(target_os = "macos"))]
 10    current_rendered_frame: Option<crate::RemoteVideoFrame>,
 11    #[cfg(not(target_os = "macos"))]
 12    previous_rendered_frame: Option<crate::RemoteVideoFrame>,
 13    _maintain_frame: Task<Result<()>>,
 14}
 15
 16#[derive(Debug)]
 17pub enum RemoteVideoTrackViewEvent {
 18    Close,
 19}
 20
 21impl RemoteVideoTrackView {
 22    pub fn new(track: RemoteVideoTrack, window: &mut Window, cx: &mut Context<Self>) -> Self {
 23        cx.focus_handle();
 24        let frames = super::play_remote_video_track(&track);
 25        let _window_handle = window.window_handle();
 26
 27        Self {
 28            track,
 29            latest_frame: None,
 30            _maintain_frame: cx.spawn_in(window, |this, mut cx| async move {
 31                futures::pin_mut!(frames);
 32                while let Some(frame) = frames.next().await {
 33                    this.update(&mut cx, |this, cx| {
 34                        this.latest_frame = Some(frame);
 35                        cx.notify();
 36                    })?;
 37                }
 38                this.update(&mut cx, |_this, cx| {
 39                    #[cfg(not(target_os = "macos"))]
 40                    {
 41                        use util::ResultExt as _;
 42                        if let Some(frame) = _this.previous_rendered_frame.take() {
 43                            _window_handle
 44                                .update(cx, |_, window, _cx| window.drop_image(frame).log_err())
 45                                .ok();
 46                        }
 47                        // TODO(mgsloan): This might leak the last image of the screenshare if
 48                        // render is called after the screenshare ends.
 49                        if let Some(frame) = _this.current_rendered_frame.take() {
 50                            _window_handle
 51                                .update(cx, |_, window, _cx| window.drop_image(frame).log_err())
 52                                .ok();
 53                        }
 54                    }
 55                    cx.emit(RemoteVideoTrackViewEvent::Close)
 56                })?;
 57                Ok(())
 58            }),
 59            #[cfg(not(target_os = "macos"))]
 60            current_rendered_frame: None,
 61            #[cfg(not(target_os = "macos"))]
 62            previous_rendered_frame: None,
 63        }
 64    }
 65
 66    pub fn clone(&self, window: &mut Window, cx: &mut Context<Self>) -> Entity<Self> {
 67        cx.new(|cx| Self::new(self.track.clone(), window, cx))
 68    }
 69}
 70
 71impl EventEmitter<RemoteVideoTrackViewEvent> for RemoteVideoTrackView {}
 72
 73impl Render for RemoteVideoTrackView {
 74    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
 75        #[cfg(target_os = "macos")]
 76        if let Some(latest_frame) = &self.latest_frame {
 77            use gpui::Styled as _;
 78            return gpui::surface(latest_frame.clone())
 79                .size_full()
 80                .into_any_element();
 81        }
 82
 83        #[cfg(not(target_os = "macos"))]
 84        if let Some(latest_frame) = &self.latest_frame {
 85            use gpui::Styled as _;
 86            if let Some(current_rendered_frame) = self.current_rendered_frame.take() {
 87                if let Some(frame) = self.previous_rendered_frame.take() {
 88                    // Only drop the frame if it's not also the current frame.
 89                    if frame.id != current_rendered_frame.id {
 90                        use util::ResultExt as _;
 91                        _window.drop_image(frame).log_err();
 92                    }
 93                }
 94                self.previous_rendered_frame = Some(current_rendered_frame)
 95            }
 96            self.current_rendered_frame = Some(latest_frame.clone());
 97            return gpui::img(latest_frame.clone())
 98                .size_full()
 99                .into_any_element();
100        }
101
102        Empty.into_any_element()
103    }
104}