1#![deny(unsafe_code)]
6#![allow(clippy::type_complexity)]
7
8mod media_thread;
9
10use std::sync::Mutex;
11
12use euclid::default::Size2D;
13use ipc_channel::ipc::{IpcReceiver, IpcSender, channel};
14use log::warn;
15use malloc_size_of_derive::MallocSizeOf;
16use paint_api::{
17 ExternalImageSource, WebRenderExternalImageApi, WebRenderExternalImageHandlers,
18 WebRenderImageHandlerType,
19};
20use serde::{Deserialize, Serialize};
21use servo_config::pref;
22pub use servo_media::player::context::{GlApi, GlContext, NativeDisplay, PlayerGLContext};
23
24use crate::media_thread::GLPlayerThread;
25
26static WINDOW_GL_CONTEXT: Mutex<WindowGLContext> = Mutex::new(WindowGLContext::inactive());
32
33#[derive(Debug, Deserialize, Serialize)]
36pub enum GLPlayerMsgForward {
37 PlayerId(u64),
38 Lock(IpcSender<(u32, Size2D<i32>, usize)>),
39 Unlock(),
40}
41
42#[derive(Debug, Deserialize, Serialize)]
48pub enum GLPlayerMsg {
49 RegisterPlayer(IpcSender<GLPlayerMsgForward>),
51 UnregisterPlayer(u64),
53 Lock(u64, IpcSender<(u32, Size2D<i32>, usize)>),
66 Unlock(u64),
75 Exit,
77}
78
79#[derive(Clone, Debug, Deserialize, Serialize, MallocSizeOf)]
83pub struct WindowGLContext {
84 pub context: GlContext,
86 pub api: GlApi,
88 pub display: NativeDisplay,
90 pub glplayer_thread_sender: Option<IpcSender<GLPlayerMsg>>,
92}
93
94impl WindowGLContext {
95 pub const fn inactive() -> Self {
97 WindowGLContext {
98 context: GlContext::Unknown,
99 api: GlApi::None,
100 display: NativeDisplay::Unknown,
101 glplayer_thread_sender: None,
102 }
103 }
104
105 pub fn register(context: Self) {
106 *WINDOW_GL_CONTEXT.lock().unwrap() = context;
107 }
108
109 pub fn get() -> Self {
110 WINDOW_GL_CONTEXT.lock().unwrap().clone()
111 }
112
113 pub fn exit(&self) {
115 self.send(GLPlayerMsg::Exit);
116 }
117
118 #[inline]
119 pub fn send(&self, message: GLPlayerMsg) {
120 let Some(sender) = self.glplayer_thread_sender.as_ref() else {
122 return;
123 };
124
125 if let Err(error) = sender.send(message) {
126 warn!("Could no longer communicate with GL accelerated media threads: {error}")
127 }
128 }
129
130 pub fn initialize(display: NativeDisplay, api: GlApi, context: GlContext) {
131 if matches!(display, NativeDisplay::Unknown) || matches!(context, GlContext::Unknown) {
132 return;
133 }
134
135 let mut window_gl_context = WINDOW_GL_CONTEXT.lock().unwrap();
136 if window_gl_context.glplayer_thread_sender.is_some() {
137 warn!("Not going to initialize GL accelerated media playback more than once.");
138 return;
139 }
140
141 window_gl_context.context = context;
142 window_gl_context.display = display;
143 window_gl_context.api = api;
144 }
145
146 pub fn initialize_image_handler(external_image_handlers: &mut WebRenderExternalImageHandlers) {
147 if !pref!(media_glvideo_enabled) {
148 return;
149 }
150
151 let mut window_gl_context = WINDOW_GL_CONTEXT.lock().unwrap();
152 if window_gl_context.glplayer_thread_sender.is_some() {
153 warn!("Not going to initialize GL accelerated media playback more than once.");
154 return;
155 }
156
157 if matches!(window_gl_context.display, NativeDisplay::Unknown) ||
158 matches!(window_gl_context.context, GlContext::Unknown)
159 {
160 return;
161 }
162
163 let thread_sender = GLPlayerThread::start(external_image_handlers.id_manager());
164 let image_handler = Box::new(GLPlayerExternalImages::new(thread_sender.clone()));
165 external_image_handlers.set_handler(image_handler, WebRenderImageHandlerType::Media);
166 window_gl_context.glplayer_thread_sender = Some(thread_sender);
167 }
168}
169
170impl PlayerGLContext for WindowGLContext {
171 fn get_gl_context(&self) -> GlContext {
172 match self.glplayer_thread_sender {
173 Some(..) => self.context.clone(),
174 None => GlContext::Unknown,
175 }
176 }
177
178 fn get_native_display(&self) -> NativeDisplay {
179 match self.glplayer_thread_sender {
180 Some(..) => self.display.clone(),
181 None => NativeDisplay::Unknown,
182 }
183 }
184
185 fn get_gl_api(&self) -> GlApi {
186 self.api.clone()
187 }
188}
189
190struct GLPlayerExternalImages {
193 glplayer_channel: IpcSender<GLPlayerMsg>,
197 lock_channel: (
200 IpcSender<(u32, Size2D<i32>, usize)>,
201 IpcReceiver<(u32, Size2D<i32>, usize)>,
202 ),
203}
204
205impl GLPlayerExternalImages {
206 fn new(sender: IpcSender<GLPlayerMsg>) -> Self {
207 Self {
208 glplayer_channel: sender,
209 lock_channel: channel().unwrap(),
210 }
211 }
212}
213
214impl WebRenderExternalImageApi for GLPlayerExternalImages {
215 fn lock(&mut self, id: u64) -> (ExternalImageSource<'_>, Size2D<i32>) {
216 self.glplayer_channel
219 .send(GLPlayerMsg::Lock(id, self.lock_channel.0.clone()))
220 .unwrap();
221 let (image_id, size, _gl_sync) = self.lock_channel.1.recv().unwrap();
222 (ExternalImageSource::NativeTexture(image_id), size)
230 }
231
232 fn unlock(&mut self, id: u64) {
233 self.glplayer_channel.send(GLPlayerMsg::Unlock(id)).unwrap();
234 }
235}