Skip to main content

servo_media_gstreamer/
render.rs

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