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}