sciter/
video.rs

1/*! Sciter video rendering.
2
3Host application can render custom video streams using `<video>` infrastructure.
4
5*/
6
7
8use capi::sctypes::{UINT, LPCBYTE, LPCSTR};
9use capi::scom::som_passport_t;
10
11/// A type alias for Sciter functions that return `bool`.
12pub type Result<T> = ::std::result::Result<T, ()>;
13
14
15/// Color space for video frame.
16#[repr(C)]
17pub enum COLOR_SPACE {
18	Unknown,
19
20	Yv12,
21	/// I420
22	Iyuv,
23	Nv12,
24	Yuy2,
25
26	Rgb24,
27	Rgb555,
28	Rgb565,
29	Rgb32,
30}
31
32macro_rules! cppcall {
33	// self.func()
34	($this:ident . $func:ident ()) => {
35		unsafe {
36			((*$this.vtbl).$func)($this as *mut _)
37		}
38	};
39  (const $this:ident . $func:ident ()) => {
40    unsafe {
41      ((*$this.vtbl).$func)($this as *const _)
42    }
43  };
44
45	// self.func(args...)
46	($this:ident . $func:ident ( $( $arg:expr ),* )) => {
47		unsafe {
48			((*$this.vtbl).$func)($this as *mut _, $($arg),* )
49		}
50	};
51  (const $this:ident . $func:ident ( $( $arg:expr ),* )) => {
52    unsafe {
53      ((*$this.vtbl).$func)($this as *const _, $($arg),* )
54    }
55  };
56}
57
58macro_rules! cppresult {
59	( $( $t:tt )* ) => {
60		if cppcall!( $($t)* ) {
61			Ok(())
62		} else {
63			Err(())
64		}
65	}
66}
67
68#[doc(hidden)]
69pub trait NamedInterface {
70	fn get_interface_name() -> &'static [u8];
71
72	fn query_interface(from: &mut iasset) -> Option<* mut iasset> {
73		let mut out: *mut iasset = ::std::ptr::null_mut();
74		from.get_interface(Self::get_interface_name().as_ptr() as LPCSTR, &mut out as *mut _);
75		if !out.is_null() {
76			Some(out)
77		} else {
78			None
79		}
80	}
81}
82
83impl NamedInterface for video_source {
84	fn get_interface_name() -> &'static [u8] {
85		b"source.video.sciter.com\0"
86	}
87}
88
89impl NamedInterface for video_destination {
90	fn get_interface_name() -> &'static [u8] {
91		b"destination.video.sciter.com\0"
92	}
93}
94
95impl NamedInterface for fragmented_video_destination {
96	fn get_interface_name() -> &'static [u8] {
97		b"fragmented.destination.video.sciter.com\0"
98	}
99}
100
101
102/// COM `IUnknown` alike thing.
103#[repr(C)]
104struct iasset_vtbl {
105	/// Increments the reference count for an interface on an object.
106	pub add_ref: extern "C" fn(this: *mut iasset) -> i32,
107
108	/// Decrements the reference count for an interface on an object.
109	pub release: extern "C" fn(this: *mut iasset) -> i32,
110
111	/// Retrieves pointers to the supported interfaces on an object.
112	pub get_interface: extern "C" fn(this: *mut iasset, name: LPCSTR, out: *mut *mut iasset) -> bool,
113
114	/// Retrieves a pointer to the passport declaration of an object.
115	pub get_passport: extern "C" fn(thing: *mut iasset) -> *const som_passport_t,
116}
117
118/// COM `IUnknown` alike thing.
119#[repr(C)]
120pub struct iasset {
121	vtbl: *const iasset_vtbl,
122}
123
124impl iasset {
125	/// Increments the reference count for an interface on an object.
126	fn add_ref(&mut self) -> i32 {
127		cppcall!(self.add_ref())
128	}
129
130	/// Decrements the reference count for an interface on an object.
131	fn release(&mut self) -> i32 {
132		cppcall!(self.release())
133	}
134
135	/// Retrieves pointers to the supported interfaces on an object.
136	pub fn get_interface(&mut self, name: LPCSTR, out: *mut *mut iasset) -> bool {
137		cppcall!(self.get_interface(name, out))
138	}
139}
140
141
142/// Video source interface, used by engine to query video state.
143#[repr(C)]
144struct video_source_vtbl {
145	// region: iasset
146	/// Increments the reference count for an interface on an object.
147	pub add_ref: extern "C" fn(this: *mut video_source) -> i32,
148
149	/// Decrements the reference count for an interface on an object.
150	pub release: extern "C" fn(this: *mut video_source) -> i32,
151
152	/// Retrieves pointers to the supported interfaces on an object.
153	pub get_interface: extern "C" fn(this: *mut video_source, name: *const u8, out: *mut *mut iasset) -> bool,
154
155	/// Retrieves a pointer to the passport declaration of an object.
156	pub get_passport: extern "C" fn(thing: *mut iasset) -> *const som_passport_t,
157	// endregion
158
159	// region: video_source
160	pub play: extern "C" fn(this: *mut video_source) -> bool,
161	pub pause: extern "C" fn(this: *mut video_source) -> bool,
162	pub stop: extern "C" fn(this: *mut video_source) -> bool,
163
164	pub get_is_ended: extern "C" fn(this: *const video_source, is_end: *mut bool) -> bool,
165
166	pub get_position: extern "C" fn(this: *const video_source, seconds: *mut f64) -> bool,
167	pub set_position: extern "C" fn(this: *mut video_source, seconds: f64) -> bool,
168
169	pub get_duration: extern "C" fn(this: *const video_source, seconds: *mut f64) -> bool,
170
171	pub get_volume: extern "C" fn(this: *const video_source, volume: *mut f64) -> bool,
172	pub set_volume: extern "C" fn(this: *mut video_source, volume: f64) -> bool,
173
174	pub get_balance: extern "C" fn(this: *const video_source, balance: *mut f64) -> bool,
175	pub set_balance: extern "C" fn(this: *mut video_source, balance: f64) -> bool,
176	// endregion
177}
178
179/// Video source interface to query video state.
180#[repr(C)]
181pub struct video_source {
182	vtbl: *const video_source_vtbl,
183}
184
185impl video_source {
186	/// Starts playback from the current position.
187	pub fn play(&mut self) -> Result<()> {
188		cppresult!(self.play())
189	}
190
191	/// Pauses playback.
192	pub fn pause(&mut self) -> Result<()> {
193		cppresult!(self.pause())
194	}
195
196	/// Stops playback.
197	pub fn stop(&mut self) -> Result<()> {
198		cppresult!(self.stop())
199	}
200
201	/// Whether playback has reached the end of the video.
202	pub fn is_ended(&self) -> Result<bool> {
203		let mut r = false;
204		cppresult!(const self.get_is_ended(&mut r as *mut _)).map(|_| r)
205	}
206
207	/// Reports the current playback position.
208	pub fn get_position(&self) -> Result<f64> {
209		let mut r = 0f64;
210		cppresult!(const self.get_position(&mut r as *mut _)).map(|_| r)
211	}
212
213	/// Sets the current playback position.
214	pub fn set_position(&mut self, seconds: f64) -> Result<()> {
215		cppresult!(self.set_position(seconds))
216	}
217
218	/// Reports the duration of the video in seconds.
219	///
220	/// If duration is not available, returns `0`.
221	pub fn get_duration(&self) -> Result<f64> {
222		let mut r = 0f64;
223		cppresult!(const self.get_duration(&mut r as *mut _)).map(|_| r)
224	}
225
226	/// Reports the current volume level of an audio track of the movie.
227	///
228	/// `1.0` corresponds to `0db`, `0.0` (mute) to `-100db`.
229	pub fn get_volume(&self) -> Result<f64> {
230		let mut r = 0f64;
231		cppresult!(const self.get_volume(&mut r as *mut _)).map(|_| r)
232	}
233
234	/// Sets the current volume level between `0.0` (mute) and `1.0` (`0db`).
235	pub fn set_volume(&mut self, volume: f64) -> Result<()> {
236		cppresult!(self.set_volume(volume))
237	}
238
239	/// Reports the current stereo balance.
240	pub fn get_balance(&self) -> Result<f64> {
241		let mut r = 0f64;
242		cppresult!(const self.get_balance(&mut r as *mut _)).map(|_| r)
243	}
244
245	/// Sets a new value of the stereo balance.
246	pub fn set_balance(&mut self, balance: f64) -> Result<()> {
247		cppresult!(self.set_balance(balance))
248	}
249}
250
251
252/// Video destination interface, represents video rendering site.
253#[repr(C)]
254struct video_destination_vtbl {
255	// region: iasset:
256	/// Increments the reference count for an interface on an object.
257	pub add_ref: extern "C" fn(this: *mut video_destination) -> i32,
258
259	/// Decrements the reference count for an interface on an object.
260	pub release: extern "C" fn(this: *mut video_destination) -> i32,
261
262	/// Retrieves pointers to the supported interfaces on an object.
263	pub get_interface: extern "C" fn(this: *mut video_destination, name: *const u8, out: *mut *mut iasset) -> bool,
264
265	/// Retrieves a pointer to the passport declaration of an object.
266	pub get_passport: extern "C" fn(thing: *mut iasset) -> *const som_passport_t,
267	// endregion
268
269	// region: video_destination
270	/// Whether this instance of `video_renderer` is attached to a DOM element and is capable of playing.
271	pub is_alive: extern "C" fn(this: *const video_destination) -> bool,
272
273	/// Start streaming/rendering.
274	pub start_streaming: extern "C" fn(this: *mut video_destination, frame_width: i32, frame_height: i32, color_space: COLOR_SPACE, src: *const video_source) -> bool,
275
276	/// Stop streaming.
277	pub stop_streaming: extern "C" fn(this: *mut video_destination) -> bool,
278
279	/// Render the next frame.
280	pub render_frame: extern "C" fn(this: *mut video_destination, data: LPCBYTE, size: UINT) -> bool,
281
282	/// Render the next frame with the given stride.
283	pub render_frame_with_stride: extern "C" fn(this: *mut video_destination, data: LPCBYTE, size: UINT, stride: UINT) -> bool,
284	// endregion
285}
286
287/// Video destination interface, represents video rendering site.
288#[repr(C)]
289pub struct video_destination {
290	vtbl: *const video_destination_vtbl,
291}
292
293impl video_destination {
294
295	/// Whether this instance of `video_renderer` is attached to a DOM element and is capable of playing.
296	pub fn is_alive(&self) -> bool {
297		cppcall!(const self.is_alive())
298	}
299
300	/// Start streaming/rendering.
301	///
302	/// * `frame_size` - the width and the height of the video frame.
303	/// * `color_space` - the color space format of the video frame.
304	/// * `src` - an optional custom [`video_source`](struct.video_source.html) interface implementation, provided by the application.
305	pub fn start_streaming(&mut self, frame_size: (i32, i32), color_space: COLOR_SPACE, src: Option<&video_source>) -> Result<()> {
306		let src_ptr = if let Some(ptr) = src { ptr as *const _ } else { ::std::ptr::null() };
307		cppresult!(self.start_streaming(frame_size.0, frame_size.1, color_space, src_ptr))
308	}
309
310	/// Stop streaming.
311	pub fn stop_streaming(&mut self) -> Result<()> {
312		cppresult!(self.stop_streaming())
313	}
314
315	/// Render the next frame.
316	pub fn render_frame(&mut self, data: &[u8]) -> Result<()> {
317		cppresult!(self.render_frame(data.as_ptr(), data.len() as UINT))
318	}
319
320	/// Render the next frame with the given stride.
321	pub fn render_frame_with_stride(&mut self, data: &[u8], stride: u32) -> Result<()> {
322		cppresult!(self.render_frame_with_stride(data.as_ptr(), data.len() as UINT, stride))
323	}
324}
325
326
327/// Fragmented destination interface, used for partial updates.
328#[repr(C)]
329struct fragmented_video_destination_vtbl {
330	// region: iasset:
331	/// Increments the reference count for an interface on an object.
332	pub add_ref: extern "C" fn(this: *mut fragmented_video_destination) -> i32,
333
334	/// Decrements the reference count for an interface on an object.
335	pub release: extern "C" fn(this: *mut fragmented_video_destination) -> i32,
336
337	/// Retrieves pointers to the supported interfaces on an object.
338	pub get_interface: extern "C" fn(this: *mut fragmented_video_destination, name: *const u8, out: *mut *mut iasset) -> bool,
339
340	/// Retrieves a pointer to the passport declaration of an object.
341	pub get_passport: extern "C" fn(thing: *mut iasset) -> *const som_passport_t,
342	// endregion
343
344	// region: video_destination
345	/// Whether this instance of `video_renderer` is attached to a DOM element and is capable of playing.
346	pub is_alive: extern "C" fn(this: *const fragmented_video_destination) -> bool,
347
348	/// Start streaming/rendering.
349	pub start_streaming: extern "C" fn(this: *mut fragmented_video_destination, frame_width: i32, frame_height: i32, color_space: COLOR_SPACE, src: *const video_source) -> bool,
350
351	/// Stop streaming.
352	pub stop_streaming: extern "C" fn(this: *mut fragmented_video_destination) -> bool,
353
354	/// Render the next frame.
355	pub render_frame: extern "C" fn(this: *mut fragmented_video_destination, data: LPCBYTE, size: UINT) -> bool,
356
357	/// Render the next frame with the given stride.
358	pub render_frame_with_stride: extern "C" fn(this: *mut fragmented_video_destination, data: LPCBYTE, size: UINT, stride: UINT) -> bool,
359	// endregion
360
361	// region: fragmented_video_destination
362	/// Render the specified part of the current frame.
363	pub render_frame_part: extern "C" fn(this: *mut fragmented_video_destination, data: LPCBYTE, size: UINT, x: i32, y: i32, width: i32, height: i32) -> bool,
364	// endregion
365}
366
367/// Fragmented destination interface, used for partial updates.
368#[repr(C)]
369pub struct fragmented_video_destination {
370	vtbl: *const fragmented_video_destination_vtbl,
371}
372
373impl fragmented_video_destination {
374
375	/// Whether this instance of `video_renderer` is attached to a DOM element and is capable of playing.
376	pub fn is_alive(&self) -> bool {
377		cppcall!(const self.is_alive())
378	}
379
380	/// Start streaming/rendering.
381	///
382	/// * `frame_size` - the width and the height of the video frame.
383	/// * `color_space` - the color space format of the video frame.
384	/// * `src` - an optional custom [`video_source`](struct.video_source.html) interface implementation, provided by the application.
385	pub fn start_streaming(&mut self, frame_size: (i32, i32), color_space: COLOR_SPACE, src: Option<&video_source>) -> Result<()> {
386		let src_ptr = if let Some(ptr) = src { ptr as *const _ } else { ::std::ptr::null() };
387		cppresult!(self.start_streaming(frame_size.0, frame_size.1, color_space, src_ptr))
388	}
389
390	/// Stop streaming.
391	pub fn stop_streaming(&mut self) -> Result<()> {
392		cppresult!(self.stop_streaming())
393	}
394
395	/// Render the next frame.
396	pub fn render_frame(&mut self, data: &[u8]) -> Result<()> {
397		cppresult!(self.render_frame(data.as_ptr(), data.len() as UINT))
398	}
399
400	/// Render the next frame with the given stride.
401	pub fn render_frame_with_stride(&mut self, data: &[u8], stride: u32) -> Result<()> {
402		cppresult!(self.render_frame_with_stride(data.as_ptr(), data.len() as UINT, stride))
403	}
404
405	/// Render the specified part of the current frame.
406	///
407	/// * `update_point` - X and Y coordinates of the update portion.
408	/// * `update_size` - width and height of the update portion.
409	pub fn render_frame_part(&mut self, data: &[u8], update_point: (i32, i32), update_size: (i32, i32)) -> Result<()> {
410		cppresult!(self.render_frame_part(data.as_ptr(), data.len() as UINT, update_point.0, update_point.1, update_size.0, update_size.1))
411	}
412}
413
414/// A managed `iasset` pointer.
415pub struct AssetPtr<T> {
416	ptr: *mut T,
417}
418
419/// It's okay to transfer video pointers between threads.
420unsafe impl<T> Send for AssetPtr<T> {}
421
422use ::std::ops::{Deref, DerefMut};
423
424impl Deref for AssetPtr<video_destination> {
425	type Target = video_destination;
426
427	fn deref(&self) -> &Self::Target {
428		unsafe { &*self.ptr }
429	}
430}
431
432impl DerefMut for AssetPtr<video_destination> {
433	fn deref_mut(&mut self) -> &mut Self::Target {
434		unsafe { &mut *self.ptr }
435	}
436}
437
438impl Deref for AssetPtr<fragmented_video_destination> {
439	type Target = fragmented_video_destination;
440
441	fn deref(&self) -> &Self::Target {
442		unsafe { &*self.ptr }
443	}
444}
445
446impl DerefMut for AssetPtr<fragmented_video_destination> {
447	fn deref_mut(&mut self) -> &mut Self::Target {
448		unsafe { &mut *self.ptr }
449	}
450}
451
452/// Decrements the reference count of a managed pointer.
453impl<T> Drop for AssetPtr<T> {
454	fn drop(&mut self) {
455		self.get().release();
456	}
457}
458
459impl<T> AssetPtr<T> {
460	/// Attach to an existing `iasset` pointer without reference increment.
461	fn attach(lp: *mut T) -> Self {
462		assert!(!lp.is_null());
463		Self {
464			ptr: lp
465		}
466	}
467
468	/// Attach to an `iasset` pointer and increment its reference count.
469	pub fn adopt(lp: *mut T) -> Self {
470		let mut me = Self::attach(lp);
471		me.get().add_ref();
472		me
473	}
474
475	/// Get as an `iasset` type.
476	fn get(&mut self) -> &mut iasset {
477		let ptr = self.ptr as *mut iasset;
478		unsafe { &mut *ptr }
479	}
480}
481
482
483/// Attach to an `iasset` pointer.
484impl<T> From<*mut T> for AssetPtr<T> {
485	/// Attach to a pointer and increment its reference count.
486	fn from(lp: *mut T) -> Self {
487		AssetPtr::adopt(lp)
488	}
489}
490
491
492/// Attempt to construct `Self` via a conversion.
493impl<T: NamedInterface> AssetPtr<T> {
494
495	/// Retrieve a supported interface of the managed pointer.
496	///
497	/// Example:
498	///
499	/// ```rust,no_run
500	/// # use sciter::video::{AssetPtr, iasset, video_source};
501	/// # let external_ptr: *mut iasset = ::std::ptr::null_mut();
502	/// let mut site = AssetPtr::adopt(external_ptr);
503	/// let source = AssetPtr::<video_source>::try_from(&mut site);
504	/// assert!(source.is_ok());
505	/// ```
506	pub fn try_from<U>(other: &mut AssetPtr<U>) -> Result<Self> {
507		let me = T::query_interface(other.get());
508		me.map(|p| AssetPtr::adopt(p as *mut T)).ok_or(())
509	}
510}