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