1use super::*;
20
21use std::alloc::{self, Layout};
22use std::ffi::CString;
23use std::marker::PhantomData;
24use std::mem;
25use std::os::raw as ctype;
26use std::panic;
27use std::panic::RefUnwindSafe;
28use std::ptr::{self, NonNull};
29use std::slice;
30use std::sync::{atomic::Ordering, Mutex};
31
32impl Mpv {
33 pub fn create_protocol_context<T, U>(&self) -> ProtocolContext<T, U>
38 where
39 T: RefUnwindSafe,
40 U: RefUnwindSafe,
41 {
42 match self.protocols_guard.compare_exchange(
43 false,
44 true,
45 Ordering::AcqRel,
46 Ordering::Acquire,
47 ) {
48 Ok(_) => ProtocolContext::new(self.ctx, PhantomData::<&Self>),
49 Err(_) => panic!("A protocol context already exists"),
50 }
51 }
52}
53
54pub type StreamOpen<T, U> = fn(&mut U, &str) -> T;
56pub type StreamClose<T> = fn(Box<T>);
58pub type StreamSeek<T> = fn(&mut T, i64) -> i64;
61pub type StreamRead<T> = fn(&mut T, &mut [ctype::c_char]) -> i64;
64pub type StreamSize<T> = fn(&mut T) -> i64;
66
67unsafe extern "C" fn open_wrapper<T, U>(
68 user_data: *mut ctype::c_void,
69 uri: *mut ctype::c_char,
70 info: *mut libmpv_sys::mpv_stream_cb_info,
71) -> ctype::c_int
72where
73 T: RefUnwindSafe,
74 U: RefUnwindSafe,
75{
76 let data = user_data as *mut ProtocolData<T, U>;
77
78 (*info).cookie = user_data;
79 (*info).read_fn = Some(read_wrapper::<T, U>);
80 (*info).seek_fn = Some(seek_wrapper::<T, U>);
81 (*info).size_fn = Some(size_wrapper::<T, U>);
82 (*info).close_fn = Some(close_wrapper::<T, U>);
83
84 let ret = panic::catch_unwind(|| {
85 let uri = mpv_cstr_to_str!(uri as *const _).unwrap();
86 ptr::write(
87 (*data).cookie,
88 ((*data).open_fn)(&mut (*data).user_data, uri),
89 );
90 });
91
92 if ret.is_ok() {
93 0
94 } else {
95 mpv_error::Generic as _
96 }
97}
98
99unsafe extern "C" fn read_wrapper<T, U>(
100 cookie: *mut ctype::c_void,
101 buf: *mut ctype::c_char,
102 nbytes: u64,
103) -> i64
104where
105 T: RefUnwindSafe,
106 U: RefUnwindSafe,
107{
108 let data = cookie as *mut ProtocolData<T, U>;
109
110 let ret = panic::catch_unwind(|| {
111 let slice = slice::from_raw_parts_mut(buf, nbytes as _);
112 ((*data).read_fn)(&mut *(*data).cookie, slice)
113 });
114 if let Ok(ret) = ret {
115 ret
116 } else {
117 -1
118 }
119}
120
121unsafe extern "C" fn seek_wrapper<T, U>(cookie: *mut ctype::c_void, offset: i64) -> i64
122where
123 T: RefUnwindSafe,
124 U: RefUnwindSafe,
125{
126 let data = cookie as *mut ProtocolData<T, U>;
127
128 if (*data).seek_fn.is_none() {
129 return mpv_error::Unsupported as _;
130 }
131
132 let ret =
133 panic::catch_unwind(|| (*(*data).seek_fn.as_ref().unwrap())(&mut *(*data).cookie, offset));
134 if let Ok(ret) = ret {
135 ret
136 } else {
137 mpv_error::Generic as _
138 }
139}
140
141unsafe extern "C" fn size_wrapper<T, U>(cookie: *mut ctype::c_void) -> i64
142where
143 T: RefUnwindSafe,
144 U: RefUnwindSafe,
145{
146 let data = cookie as *mut ProtocolData<T, U>;
147
148 if (*data).size_fn.is_none() {
149 return mpv_error::Unsupported as _;
150 }
151
152 let ret = panic::catch_unwind(|| (*(*data).size_fn.as_ref().unwrap())(&mut *(*data).cookie));
153 if let Ok(ret) = ret {
154 ret
155 } else {
156 mpv_error::Unsupported as _
157 }
158}
159
160#[allow(unused_must_use)]
161unsafe extern "C" fn close_wrapper<T, U>(cookie: *mut ctype::c_void)
162where
163 T: RefUnwindSafe,
164 U: RefUnwindSafe,
165{
166 let data = Box::from_raw(cookie as *mut ProtocolData<T, U>);
167
168 panic::catch_unwind(|| ((*data).close_fn)(Box::from_raw((*data).cookie)));
169}
170
171struct ProtocolData<T, U> {
172 cookie: *mut T,
173 user_data: U,
174
175 open_fn: StreamOpen<T, U>,
176 close_fn: StreamClose<T>,
177 read_fn: StreamRead<T>,
178 seek_fn: Option<StreamSeek<T>>,
179 size_fn: Option<StreamSize<T>>,
180}
181
182pub struct ProtocolContext<'parent, T: RefUnwindSafe, U: RefUnwindSafe> {
185 ctx: NonNull<libmpv_sys::mpv_handle>,
186 protocols: Mutex<Vec<Protocol<T, U>>>,
187 _does_not_outlive: PhantomData<&'parent Mpv>,
188}
189
190unsafe impl<'parent, T: RefUnwindSafe, U: RefUnwindSafe> Send for ProtocolContext<'parent, T, U> {}
191unsafe impl<'parent, T: RefUnwindSafe, U: RefUnwindSafe> Sync for ProtocolContext<'parent, T, U> {}
192
193impl<'parent, T: RefUnwindSafe, U: RefUnwindSafe> ProtocolContext<'parent, T, U> {
194 fn new(
195 ctx: NonNull<libmpv_sys::mpv_handle>,
196 marker: PhantomData<&'parent Mpv>,
197 ) -> ProtocolContext<'parent, T, U> {
198 ProtocolContext {
199 ctx,
200 protocols: Mutex::new(Vec::new()),
201 _does_not_outlive: marker,
202 }
203 }
204
205 pub fn register(&self, protocol: Protocol<T, U>) -> Result<()> {
211 let mut protocols = self.protocols.lock().unwrap();
212 protocol.register(self.ctx.as_ptr())?;
213 protocols.push(protocol);
214 Ok(())
215 }
216}
217
218pub struct Protocol<T: Sized + RefUnwindSafe, U: RefUnwindSafe> {
220 name: String,
221 data: *mut ProtocolData<T, U>,
222}
223
224impl<T: RefUnwindSafe, U: RefUnwindSafe> Protocol<T, U> {
225 pub unsafe fn new(
233 name: String,
234 user_data: U,
235 open_fn: StreamOpen<T, U>,
236 close_fn: StreamClose<T>,
237 read_fn: StreamRead<T>,
238 seek_fn: Option<StreamSeek<T>>,
239 size_fn: Option<StreamSize<T>>,
240 ) -> Protocol<T, U> {
241 let c_layout = Layout::from_size_align(mem::size_of::<T>(), mem::align_of::<T>()).unwrap();
242 let cookie = alloc::alloc(c_layout) as *mut T;
243 let data = Box::into_raw(Box::new(ProtocolData {
244 cookie,
245 user_data,
246
247 open_fn,
248 close_fn,
249 read_fn,
250 seek_fn,
251 size_fn,
252 }));
253
254 Protocol { name, data }
255 }
256
257 fn register(&self, ctx: *mut libmpv_sys::mpv_handle) -> Result<()> {
258 let name = CString::new(&self.name[..])?;
259 unsafe {
260 mpv_err(
261 (),
262 libmpv_sys::mpv_stream_cb_add_ro(
263 ctx,
264 name.as_ptr(),
265 self.data as *mut _,
266 Some(open_wrapper::<T, U>),
267 ),
268 )
269 }
270 }
271}