servo_media_gstreamer/
render.rs1use 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}