1#![deny(unsafe_code)]
6#![allow(clippy::type_complexity)]
7
8mod media_thread;
9
10use std::sync::{Arc, Mutex};
11
12use compositing_traits::{
13 ExternalImageSource, WebrenderExternalImageApi, WebrenderExternalImageHandlers,
14 WebrenderExternalImageRegistry, 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(
146 external_image_handlers: &mut WebrenderExternalImageHandlers,
147 external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
148 ) {
149 if !pref!(media_glvideo_enabled) {
150 return;
151 }
152
153 let mut window_gl_context = WINDOW_GL_CONTEXT.lock().unwrap();
154 if window_gl_context.glplayer_thread_sender.is_some() {
155 warn!("Not going to initialize GL accelerated media playback more than once.");
156 return;
157 }
158
159 if matches!(window_gl_context.display, NativeDisplay::Unknown) ||
160 matches!(window_gl_context.context, GlContext::Unknown)
161 {
162 return;
163 }
164
165 let thread_sender = GLPlayerThread::start(external_images);
166 let image_handler = Box::new(GLPlayerExternalImages::new(thread_sender.clone()));
167 external_image_handlers.set_handler(image_handler, WebrenderImageHandlerType::Media);
168 window_gl_context.glplayer_thread_sender = Some(thread_sender);
169 }
170}
171
172impl PlayerGLContext for WindowGLContext {
173 fn get_gl_context(&self) -> GlContext {
174 match self.glplayer_thread_sender {
175 Some(..) => self.context.clone(),
176 None => GlContext::Unknown,
177 }
178 }
179
180 fn get_native_display(&self) -> NativeDisplay {
181 match self.glplayer_thread_sender {
182 Some(..) => self.display.clone(),
183 None => NativeDisplay::Unknown,
184 }
185 }
186
187 fn get_gl_api(&self) -> GlApi {
188 self.api.clone()
189 }
190}
191
192struct GLPlayerExternalImages {
195 glplayer_channel: IpcSender<GLPlayerMsg>,
199 lock_channel: (
202 IpcSender<(u32, Size2D<i32>, usize)>,
203 IpcReceiver<(u32, Size2D<i32>, usize)>,
204 ),
205}
206
207impl GLPlayerExternalImages {
208 fn new(sender: IpcSender<GLPlayerMsg>) -> Self {
209 Self {
210 glplayer_channel: sender,
211 lock_channel: channel().unwrap(),
212 }
213 }
214}
215
216impl WebrenderExternalImageApi for GLPlayerExternalImages {
217 fn lock(&mut self, id: u64) -> (ExternalImageSource<'_>, Size2D<i32>) {
218 self.glplayer_channel
221 .send(GLPlayerMsg::Lock(id, self.lock_channel.0.clone()))
222 .unwrap();
223 let (image_id, size, _gl_sync) = self.lock_channel.1.recv().unwrap();
224 (ExternalImageSource::NativeTexture(image_id), size)
232 }
233
234 fn unlock(&mut self, id: u64) {
235 self.glplayer_channel.send(GLPlayerMsg::Unlock(id)).unwrap();
236 }
237}