servo_media_gstreamer/
render.rs

1use glib::prelude::*;
2
3use std::sync::Arc;
4
5use servo_media_gstreamer_render::Render;
6use servo_media_player::context::PlayerGLContext;
7use servo_media_player::video::{Buffer, VideoFrame, VideoFrameData};
8use servo_media_player::PlayerError;
9
10#[cfg(any(
11    target_os = "linux",
12    target_os = "dragonfly",
13    target_os = "freebsd",
14    target_os = "netbsd",
15    target_os = "openbsd"
16))]
17mod platform {
18    extern crate servo_media_gstreamer_render_unix;
19    pub use self::servo_media_gstreamer_render_unix::RenderUnix as Render;
20
21    use super::*;
22
23    pub fn create_render(gl_context: Box<dyn PlayerGLContext>) -> Option<Render> {
24        Render::new(gl_context)
25    }
26}
27
28#[cfg(target_os = "android")]
29mod platform {
30    extern crate servo_media_gstreamer_render_android;
31    pub use self::servo_media_gstreamer_render_android::RenderAndroid as Render;
32
33    use super::*;
34
35    pub fn create_render(gl_context: Box<dyn PlayerGLContext>) -> Option<Render> {
36        Render::new(gl_context)
37    }
38}
39
40#[cfg(not(any(
41    target_os = "linux",
42    target_os = "dragonfly",
43    target_os = "freebsd",
44    target_os = "netbsd",
45    target_os = "openbsd",
46    target_os = "android",
47)))]
48mod platform {
49    use servo_media_gstreamer_render::Render as RenderTrait;
50    use servo_media_player::context::PlayerGLContext;
51    use servo_media_player::video::VideoFrame;
52    use servo_media_player::PlayerError;
53
54    pub struct RenderDummy();
55    pub type Render = RenderDummy;
56
57    pub fn create_render(_: Box<dyn PlayerGLContext>) -> Option<RenderDummy> {
58        None
59    }
60
61    impl RenderTrait for RenderDummy {
62        fn is_gl(&self) -> bool {
63            false
64        }
65
66        fn build_frame(&self, _: gst::Sample) -> Result<VideoFrame, ()> {
67            Err(())
68        }
69
70        fn build_video_sink(&self, _: &gst::Element, _: &gst::Element) -> Result<(), PlayerError> {
71            Err(PlayerError::Backend(
72                "Not available videosink decorator".to_owned(),
73            ))
74        }
75    }
76}
77
78struct GStreamerBuffer {
79    frame: gst_video::VideoFrame<gst_video::video_frame::Readable>,
80}
81
82impl Buffer for GStreamerBuffer {
83    fn to_vec(&self) -> Result<VideoFrameData, ()> {
84        let data = self.frame.plane_data(0).map_err(|_| ())?;
85        Ok(VideoFrameData::Raw(Arc::new(data.to_vec())))
86    }
87}
88
89pub struct GStreamerRender {
90    render: Option<platform::Render>,
91}
92
93impl GStreamerRender {
94    pub fn new(gl_context: Box<dyn PlayerGLContext>) -> Self {
95        GStreamerRender {
96            render: platform::create_render(gl_context),
97        }
98    }
99
100    pub fn is_gl(&self) -> bool {
101        if let Some(render) = self.render.as_ref() {
102            render.is_gl()
103        } else {
104            false
105        }
106    }
107
108    pub fn get_frame_from_sample(&self, sample: gst::Sample) -> Result<VideoFrame, ()> {
109        if let Some(render) = self.render.as_ref() {
110            render.build_frame(sample)
111        } else {
112            let buffer = sample.buffer_owned().ok_or(())?;
113            let caps = sample.caps().ok_or(())?;
114            let info = gst_video::VideoInfo::from_caps(caps).map_err(|_| ())?;
115
116            let frame =
117                gst_video::VideoFrame::from_buffer_readable(buffer, &info).map_err(|_| ())?;
118
119            VideoFrame::new(
120                info.width() as i32,
121                info.height() as i32,
122                Arc::new(GStreamerBuffer { frame }),
123            )
124        }
125    }
126
127    pub fn setup_video_sink(
128        &self,
129        pipeline: &gst::Element,
130    ) -> Result<gst_app::AppSink, PlayerError> {
131        let appsink = gst::ElementFactory::make("appsink")
132            .build()
133            .map_err(|error| PlayerError::Backend(format!("appsink creation failed: {error:?}")))?
134            .downcast::<gst_app::AppSink>()
135            .unwrap();
136
137        if let Some(render) = self.render.as_ref() {
138            render.build_video_sink(appsink.upcast_ref::<gst::Element>(), pipeline)?
139        } else {
140            let caps = gst::Caps::builder("video/x-raw")
141                .field("format", gst_video::VideoFormat::Bgra.to_str())
142                .field("pixel-aspect-ratio", gst::Fraction::from((1, 1)))
143                .build();
144
145            appsink.set_caps(Some(&caps));
146            pipeline.set_property("video-sink", &appsink);
147        };
148
149        Ok(appsink)
150    }
151}