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}