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