servo_media_gstreamer/
media_capture.rs

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