remote_video_track_view.rs

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