Skip to main content

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