remote_video_track_view.rs

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