Skip to main content

servo_media_gstreamer/
media_capture.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 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                // TODO: Include the ideal caps value in the caps, needs a refactor
42                //       of the AddToCaps trait
43                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                // TODO: Include the ideal caps value in the caps, needs a refactor
81                //       of the AddToCaps trait
82                Some(builder.field(name, range))
83            },
84        }
85    }
86}
87
88// TODO(Manishearth): Should support a set of constraints
89fn 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}