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