mimick/library/video_player/
pipeline.rs1use std::sync::Arc;
2
3use gstreamer as gst;
4use gstreamer_play as gst_play;
5use gtk::prelude::*;
6use parking_lot::Mutex;
7
8pub(super) fn build_play(
9 volume: f64,
10 muted: bool,
11 api_key: Arc<Mutex<Option<String>>>,
12) -> Result<(gst_play::Play, gtk::gdk::Paintable), String> {
13 let (sink, paintable) = video_sink()?;
14 let play = gst_play::Play::new(None::<gst_play::PlayVideoRenderer>);
15 let mut config = play.config();
16 config.set_position_update_interval(250);
17 config.set_seek_accurate(true);
18 play.set_config(config).map_err(|e| e.to_string())?;
19 let pipeline = play.pipeline();
20 pipeline.set_property("video-sink", &sink);
21 if let Ok(tempo) = gst::ElementFactory::make("scaletempo").build() {
22 pipeline.set_property("audio-filter", &tempo);
23 }
24 connect_api_key_header(&pipeline, api_key);
25 play.set_volume(volume);
26 play.set_mute(muted);
27 Ok((play, paintable))
28}
29
30pub(super) fn frame_duration(numer: i32, denom: i32) -> Option<gst::ClockTime> {
31 if numer <= 0 || denom <= 0 {
32 return None;
33 }
34 Some(gst::ClockTime::from_nseconds(
35 1_000_000_000 * denom as u64 / numer as u64,
36 ))
37}
38
39fn video_sink() -> Result<(gst::Element, gtk::gdk::Paintable), String> {
40 let gtk_sink = gst::ElementFactory::make("gtk4paintablesink")
41 .build()
42 .map_err(|_| "The GStreamer GTK 4 video sink (gst-plugin-gtk4) is not installed.")?;
43 let paintable = gtk_sink.property::<gtk::gdk::Paintable>("paintable");
44 if !paintable.has_property("gl-context")
45 || paintable
46 .property::<Option<gtk::gdk::GLContext>>("gl-context")
47 .is_none()
48 {
49 return Ok((gtk_sink, paintable));
50 }
51 let gl_sink = gst::ElementFactory::make("glsinkbin")
52 .property("sink", >k_sink)
53 .build()
54 .map_err(|e| e.to_string())?;
55 Ok((gl_sink, paintable))
56}
57
58fn connect_api_key_header(pipeline: &gst::Element, api_key: Arc<Mutex<Option<String>>>) {
59 pipeline.connect("source-setup", false, move |args| {
60 let source = args[1].get::<gst::Element>().ok()?;
61 let key = api_key.lock().clone()?;
62 if source.has_property("extra-headers") {
63 let headers = gst::Structure::builder("extra-headers")
64 .field("x-api-key", key)
65 .build();
66 source.set_property("extra-headers", headers);
67 }
68 None
69 });
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn frame_duration_handles_ntsc_and_unknown_rates() {
78 assert_eq!(
79 frame_duration(30, 1),
80 Some(gst::ClockTime::from_nseconds(33_333_333))
81 );
82 assert_eq!(
83 frame_duration(30000, 1001),
84 Some(gst::ClockTime::from_nseconds(33_366_666))
85 );
86 assert_eq!(frame_duration(0, 1), None);
87 }
88}