servo_media_gstreamer/
media_capture.rs1use gstreamer;
6use gstreamer::caps::NoFeature;
7use gstreamer::prelude::*;
8use servo_media_streams::MediaStreamType;
9use servo_media_streams::capture::*;
10use servo_media_streams::registry::MediaStreamId;
11
12use crate::media_stream::GStreamerMediaStream;
13
14trait AddToCaps {
15 type Bound;
16 fn add_to_caps(
17 &self,
18 name: &str,
19 min: Self::Bound,
20 max: Self::Bound,
21 builder: gstreamer::caps::Builder<NoFeature>,
22 ) -> Option<gstreamer::caps::Builder<NoFeature>>;
23}
24
25impl AddToCaps for Constrain<u32> {
26 type Bound = u32;
27 fn add_to_caps(
28 &self,
29 name: &str,
30 min: u32,
31 max: u32,
32 builder: gstreamer::caps::Builder<NoFeature>,
33 ) -> Option<gstreamer::caps::Builder<NoFeature>> {
34 match self {
35 Constrain::Value(v) => Some(builder.field(name, v)),
36 Constrain::Range(r) => {
37 let min = into_i32(r.min.unwrap_or(min));
38 let max = into_i32(r.max.unwrap_or(max));
39 let range = gstreamer::IntRange::<i32>::new(min, max);
40
41 Some(builder.field(name, range))
44 },
45 }
46 }
47}
48
49fn into_i32(x: u32) -> i32 {
50 if x > i32::MAX as u32 {
51 i32::MAX
52 } else {
53 x as i32
54 }
55}
56
57impl AddToCaps for Constrain<f64> {
58 type Bound = i32;
59 fn add_to_caps<'a>(
60 &self,
61 name: &str,
62 min: i32,
63 max: i32,
64 builder: gstreamer::caps::Builder<NoFeature>,
65 ) -> Option<gstreamer::caps::Builder<NoFeature>> {
66 match self {
67 Constrain::Value(v) => {
68 Some(builder.field("name", gstreamer::Fraction::approximate_f64(*v)?))
69 },
70 Constrain::Range(r) => {
71 let min = r
72 .min
73 .and_then(gstreamer::Fraction::approximate_f64)
74 .unwrap_or(gstreamer::Fraction::new(min, 1));
75 let max = r
76 .max
77 .and_then(gstreamer::Fraction::approximate_f64)
78 .unwrap_or(gstreamer::Fraction::new(max, 1));
79 let range = gstreamer::FractionRange::new(min, max);
80 Some(builder.field(name, range))
83 },
84 }
85 }
86}
87
88fn into_caps(set: MediaTrackConstraintSet, format: &str) -> Option<gstreamer::Caps> {
90 let mut builder = gstreamer::Caps::builder(format);
91 if let Some(w) = set.width {
92 builder = w.add_to_caps("width", 0, 1000000, builder)?;
93 }
94 if let Some(h) = set.height {
95 builder = h.add_to_caps("height", 0, 1000000, builder)?;
96 }
97 if let Some(aspect) = set.aspect {
98 builder = aspect.add_to_caps("pixel-aspect-ratio", 0, 1000000, builder)?;
99 }
100 if let Some(fr) = set.frame_rate {
101 builder = fr.add_to_caps("framerate", 0, 1000000, builder)?;
102 }
103 if let Some(sr) = set.sample_rate {
104 builder = sr.add_to_caps("rate", 0, 1000000, builder)?;
105 }
106 Some(builder.build())
107}
108
109struct GstMediaDevices {
110 monitor: gstreamer::DeviceMonitor,
111}
112
113impl GstMediaDevices {
114 pub fn new() -> Self {
115 Self {
116 monitor: gstreamer::DeviceMonitor::new(),
117 }
118 }
119
120 pub fn get_track(
121 &self,
122 video: bool,
123 constraints: MediaTrackConstraintSet,
124 ) -> Option<GstMediaTrack> {
125 let (format, filter) = if video {
126 ("video/x-raw", "Video/Source")
127 } else {
128 ("audio/x-raw", "Audio/Source")
129 };
130 let caps = into_caps(constraints, format)?;
131 let f = self.monitor.add_filter(Some(filter), Some(&caps));
132 let devices = self.monitor.devices();
133 if let Some(f) = f {
134 let _ = self.monitor.remove_filter(f);
135 }
136 match devices.front() {
137 Some(d) => {
138 let element = d.create_element(None).ok()?;
139 Some(GstMediaTrack { element })
140 },
141 _ => None,
142 }
143 }
144}
145
146pub struct GstMediaTrack {
147 element: gstreamer::Element,
148}
149
150fn create_input_stream(
151 stream_type: MediaStreamType,
152 constraint_set: MediaTrackConstraintSet,
153) -> Option<MediaStreamId> {
154 let devices = GstMediaDevices::new();
155 devices
156 .get_track(stream_type == MediaStreamType::Video, constraint_set)
157 .map(|track| {
158 let f = match stream_type {
159 MediaStreamType::Audio => GStreamerMediaStream::create_audio_from,
160 MediaStreamType::Video => GStreamerMediaStream::create_video_from,
161 };
162 f(track.element)
163 })
164}
165
166pub fn create_audioinput_stream(constraint_set: MediaTrackConstraintSet) -> Option<MediaStreamId> {
167 create_input_stream(MediaStreamType::Audio, constraint_set)
168}
169
170pub fn create_videoinput_stream(constraint_set: MediaTrackConstraintSet) -> Option<MediaStreamId> {
171 create_input_stream(MediaStreamType::Video, constraint_set)
172}