remote_video_track_view.rs

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