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}