servo_media_gstreamer/
registry_scanner.rs

1use once_cell::sync::Lazy;
2use std::collections::HashSet;
3use std::str::FromStr;
4
5// The GStreamer registry holds the metadata of the set of plugins available in the host.
6// This scanner is used to lazily analyze the registry and to provide information about
7// the set of supported mime types and codecs that the backend is able to deal with.
8pub static GSTREAMER_REGISTRY_SCANNER: Lazy<GStreamerRegistryScanner> =
9    Lazy::new(|| GStreamerRegistryScanner::new());
10
11pub struct GStreamerRegistryScanner {
12    supported_mime_types: HashSet<&'static str>,
13    supported_codecs: HashSet<&'static str>,
14}
15
16impl GStreamerRegistryScanner {
17    fn new() -> GStreamerRegistryScanner {
18        let mut registry_scanner = GStreamerRegistryScanner {
19            supported_mime_types: HashSet::new(),
20            supported_codecs: HashSet::new(),
21        };
22        registry_scanner.initialize();
23        registry_scanner
24    }
25
26    pub fn is_container_type_supported(&self, container_type: &str) -> bool {
27        self.supported_mime_types.contains(container_type)
28    }
29
30    fn is_codec_supported(&self, codec: &str) -> bool {
31        self.supported_codecs.contains(codec)
32    }
33
34    pub fn are_all_codecs_supported(&self, codecs: &Vec<&str>) -> bool {
35        codecs.iter().all(|&codec| self.is_codec_supported(codec))
36    }
37
38    fn initialize(&mut self) {
39        let audio_decoder_factories = gst::ElementFactory::factories_with_type(
40            gst::ElementFactoryType::DECODER | gst::ElementFactoryType::MEDIA_AUDIO,
41            gst::Rank::MARGINAL,
42        );
43        let audio_parser_factories = gst::ElementFactory::factories_with_type(
44            gst::ElementFactoryType::PARSER | gst::ElementFactoryType::MEDIA_AUDIO,
45            gst::Rank::NONE,
46        );
47        let video_decoder_factories = gst::ElementFactory::factories_with_type(
48            gst::ElementFactoryType::DECODER | gst::ElementFactoryType::MEDIA_VIDEO,
49            gst::Rank::MARGINAL,
50        );
51        let video_parser_factories = gst::ElementFactory::factories_with_type(
52            gst::ElementFactoryType::PARSER | gst::ElementFactoryType::MEDIA_VIDEO,
53            gst::Rank::MARGINAL,
54        );
55        let demux_factories = gst::ElementFactory::factories_with_type(
56            gst::ElementFactoryType::DEMUXER,
57            gst::Rank::MARGINAL,
58        );
59
60        if has_element_for_media_type(&audio_decoder_factories, "audio/mpeg, mpegversion=(int)4") {
61            self.supported_mime_types.insert("audio/aac");
62            self.supported_mime_types.insert("audio/mp4");
63            self.supported_mime_types.insert("audio/x-m4a");
64            self.supported_codecs.insert("mpeg");
65            self.supported_codecs.insert("mp4a*");
66        }
67
68        let is_opus_supported =
69            has_element_for_media_type(&audio_decoder_factories, "audio/x-opus");
70        if is_opus_supported && has_element_for_media_type(&audio_parser_factories, "audio/x-opus")
71        {
72            self.supported_mime_types.insert("audio/opus");
73            self.supported_codecs.insert("opus");
74            self.supported_codecs.insert("x-opus");
75        }
76
77        let is_vorbis_supported =
78            has_element_for_media_type(&audio_decoder_factories, "audio/x-vorbis");
79        if is_vorbis_supported
80            && has_element_for_media_type(&audio_parser_factories, "audio/x-vorbis")
81        {
82            self.supported_codecs.insert("vorbis");
83            self.supported_codecs.insert("x-vorbis");
84        }
85
86        if has_element_for_media_type(&demux_factories, "video/x-matroska") {
87            let is_vp8_decoder_available =
88                has_element_for_media_type(&video_decoder_factories, "video/x-vp8");
89            let is_vp9_decoder_available =
90                has_element_for_media_type(&video_decoder_factories, "video/x-vp9");
91
92            if is_vp8_decoder_available || is_vp9_decoder_available {
93                self.supported_mime_types.insert("video/webm");
94            }
95
96            if is_vp8_decoder_available {
97                self.supported_codecs.insert("vp8");
98                self.supported_codecs.insert("x-vp8");
99                self.supported_codecs.insert("vp8.0");
100            }
101
102            if is_vp9_decoder_available {
103                self.supported_codecs.insert("vp9");
104                self.supported_codecs.insert("x-vp9");
105                self.supported_codecs.insert("vp9.0");
106            }
107
108            if is_opus_supported {
109                self.supported_mime_types.insert("audio/webm");
110            }
111        }
112
113        let is_h264_decoder_available = has_element_for_media_type(
114            &video_decoder_factories,
115            "video/x-h264, profile=(string){ constrained-baseline, baseline, high }",
116        );
117        if is_h264_decoder_available
118            && has_element_for_media_type(&video_parser_factories, "video/x-h264")
119        {
120            self.supported_mime_types.insert("video/mp4");
121            self.supported_mime_types.insert("video/x-m4v");
122            self.supported_codecs.insert("x-h264");
123            self.supported_codecs.insert("avc*");
124            self.supported_codecs.insert("mp4v*");
125        }
126
127        if has_element_for_media_type(&audio_decoder_factories, "audio/midi") {
128            self.supported_mime_types.insert("audio/midi");
129            self.supported_mime_types.insert("audio/riff-midi");
130        }
131
132        if has_element_for_media_type(&audio_decoder_factories, "audio/x-ac3") {
133            self.supported_mime_types.insert("audio/x-ac3");
134        }
135
136        if has_element_for_media_type(&audio_decoder_factories, "audio/x-flac") {
137            self.supported_mime_types.insert("audio/flac");
138            self.supported_mime_types.insert("audio/x-flac");
139        }
140
141        if has_element_for_media_type(&audio_decoder_factories, "audio/x-speex") {
142            self.supported_mime_types.insert("audio/speex");
143            self.supported_mime_types.insert("audio/x-speex");
144        }
145
146        if has_element_for_media_type(&audio_decoder_factories, "audio/x-wavpack") {
147            self.supported_mime_types.insert("audio/x-wavpack");
148        }
149
150        if has_element_for_media_type(
151            &video_decoder_factories,
152            "video/mpeg, mpegversion=(int){1,2}, systemstream=(boolean)false",
153        ) {
154            self.supported_mime_types.insert("video/mpeg");
155            self.supported_codecs.insert("mpeg");
156        }
157
158        if has_element_for_media_type(&video_decoder_factories, "video/x-flash-video") {
159            self.supported_mime_types.insert("video/flv");
160            self.supported_mime_types.insert("video/x-flv");
161        }
162
163        if has_element_for_media_type(&video_decoder_factories, "video/x-msvideocodec") {
164            self.supported_mime_types.insert("video/x-msvideo");
165        }
166
167        if has_element_for_media_type(&demux_factories, "application/x-hls") {
168            self.supported_mime_types
169                .insert("application/vnd.apple.mpegurl");
170            self.supported_mime_types.insert("application/x-mpegurl");
171        }
172
173        if has_element_for_media_type(&demux_factories, "application/x-wav")
174            || has_element_for_media_type(&demux_factories, "audio/x-wav")
175        {
176            self.supported_mime_types.insert("audio/wav");
177            self.supported_mime_types.insert("audio/vnd.wav");
178            self.supported_mime_types.insert("audio/x-wav");
179            self.supported_codecs.insert("1");
180        }
181
182        if has_element_for_media_type(&demux_factories, "video/quicktime, variant=(string)3gpp") {
183            self.supported_mime_types.insert("video/3gpp");
184        }
185
186        if has_element_for_media_type(&demux_factories, "application/ogg") {
187            self.supported_mime_types.insert("application/ogg");
188
189            if is_vorbis_supported {
190                self.supported_mime_types.insert("audio/ogg");
191                self.supported_mime_types.insert("audio/x-vorbis+ogg");
192            }
193
194            if has_element_for_media_type(&audio_decoder_factories, "audio/x-speex") {
195                self.supported_mime_types.insert("audio/ogg");
196                self.supported_codecs.insert("speex");
197            }
198
199            if has_element_for_media_type(&video_decoder_factories, "video/x-theora") {
200                self.supported_mime_types.insert("video/ogg");
201                self.supported_codecs.insert("theora");
202            }
203        }
204
205        let mut is_audio_mpeg_supported = false;
206        if has_element_for_media_type(
207            &audio_decoder_factories,
208            "audio/mpeg, mpegversion=(int)1, layer=(int)[1, 3]",
209        ) {
210            is_audio_mpeg_supported = true;
211            self.supported_mime_types.insert("audio/mp1");
212            self.supported_mime_types.insert("audio/mp3");
213            self.supported_mime_types.insert("audio/x-mp3");
214            self.supported_codecs.insert("audio/mp3");
215        }
216
217        if has_element_for_media_type(&audio_decoder_factories, "audio/mpeg, mpegversion=(int)2") {
218            is_audio_mpeg_supported = true;
219            self.supported_mime_types.insert("audio/mp2");
220        }
221
222        is_audio_mpeg_supported |= self.is_container_type_supported("video/mp4");
223        if is_audio_mpeg_supported {
224            self.supported_mime_types.insert("audio/mpeg");
225            self.supported_mime_types.insert("audio/x-mpeg");
226        }
227
228        let is_matroska_supported =
229            has_element_for_media_type(&demux_factories, "video/x-matroska");
230        if is_matroska_supported {
231            self.supported_mime_types.insert("video/x-matroska");
232
233            if has_element_for_media_type(&video_decoder_factories, "video/x-vp10") {
234                self.supported_mime_types.insert("video/webm");
235            }
236        }
237
238        if (is_matroska_supported || self.is_container_type_supported("video/mp4"))
239            && has_element_for_media_type(&video_decoder_factories, "video/x-av1")
240        {
241            self.supported_codecs.insert("av01*");
242        }
243    }
244}
245
246fn has_element_for_media_type(
247    factories: &glib::List<gst::ElementFactory>,
248    media_type: &str,
249) -> bool {
250    match gst::caps::Caps::from_str(media_type) {
251        Ok(caps) => {
252            for factory in factories {
253                if factory.can_sink_all_caps(&caps) {
254                    return true;
255                }
256            }
257            false
258        }
259        _ => false,
260    }
261}