1 /* Ported to Vala from singledecodebin.py in Pitivi:
3 * Copyright (c) 2005, Edward Hervey <bilboed@bilboed.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
24 extern void qsort(void *p, size_t num, size_t size, GLib.CompareFunc func);
26 // Single-stream queue-less decodebin
28 // returns true if the caps are RAW
29 bool is_raw(Gst.Caps caps) {
30 string rep = caps.to_string();
31 string[] valid = { "video/x-raw", "audio/x-raw", "text/plain", "text/x-pango-markup" };
32 foreach (string val in valid)
33 if (rep.has_prefix(val))
38 // A variant of decodebin.
39 // Only outputs one stream; doesn't contain any internal queue.
40 class SingleDecodeBin : Gst.Bin {
45 ArrayList<Gst.Element> dynamics = new ArrayList<Gst.Element>();
46 ArrayList<Gst.Element> validelements = new ArrayList<Gst.Element>(); // added elements
47 Gst.ElementFactory[] factories;
49 const int64 QUEUE_SIZE = 1 * Gst.SECOND;
52 Gst.PadTemplate sink_pad = new Gst.PadTemplate("sinkpadtemplate", Gst.PadDirection.SINK,
53 Gst.PadPresence.ALWAYS, new Gst.Caps.any());
54 Gst.PadTemplate src_pad = new Gst.PadTemplate("srcpadtemplate", Gst.PadDirection.SINK,
55 Gst.PadPresence.ALWAYS, new Gst.Caps.any());
57 add_pad_template (src_pad);
58 add_pad_template (sink_pad);
61 public SingleDecodeBin(Gst.Caps? caps, string name, string? uri) throws Error {
62 this.caps = caps == null ? new Gst.Caps.any() : caps;
64 typefind = Gst.ElementFactory.make("typefind", "internal-typefind");
68 Gst.Element file_src = make_element("filesrc");
70 file_src.set("location", uri);
73 file_src.link(typefind);
75 Gst.GhostPad sinkpad = new Gst.GhostPad("sink", typefind.get_pad("sink"));
76 sinkpad.set_active(true);
79 Signal.connect(typefind, "have-type", (Callback) typefindHaveTypeCb, this);
81 factories = getSortedFactoryList();
86 void controlDynamicElement(Gst.Element element) {
87 dynamics.add(element);
88 element.pad_added.connect(dynamicPadAddedCb);
89 element.no_more_pads.connect(dynamicNoMorePadsCb);
92 static int factory_compare(Gst.ElementFactory** a, Gst.ElementFactory** b) {
93 Gst.PluginFeature* p1 = *(Gst.PluginFeature**)a;
94 Gst.PluginFeature* p2 = *(Gst.PluginFeature**)b;
95 return (int) (((Gst.PluginFeature) p2).get_rank() - ((Gst.PluginFeature) p1).get_rank());
98 // Returns the list of demuxers, decoders and parsers available, sorted by rank
99 Gst.ElementFactory[] getSortedFactoryList() {
100 Gst.Registry registry = Gst.Registry.get_default();
101 Gst.ElementFactory[] factories = new Gst.ElementFactory[0];
103 foreach (Gst.PluginFeature plugin_feature in
104 registry.get_feature_list(typeof(Gst.ElementFactory)) ) {
106 Gst.ElementFactory factory = plugin_feature as Gst.ElementFactory;
107 if (factory == null || factory.get_rank() < 64)
109 string klass = factory.get_klass();
110 if (klass.contains("Demuxer") || klass.contains("Decoder") || klass.contains("Parse"))
111 factories += factory;
114 qsort(factories, factories.length, sizeof(Gst.ElementFactory *),
115 (GLib.CompareFunc) factory_compare);
119 /* Returns a list of factories (sorted by rank) which can take caps as
120 * input. Returns empty list if none are compatible. */
121 Gee.ArrayList<Gst.ElementFactory> findCompatibleFactory(Gst.Caps caps) {
122 Gee.ArrayList<Gst.ElementFactory> res = new Gee.ArrayList<Gst.ElementFactory>();
124 foreach (Gst.ElementFactory factory in factories) {
125 weak GLib.List<Gst.StaticPadTemplate?> templates = factory.get_static_pad_templates();
126 foreach (Gst.StaticPadTemplate template in templates)
127 if (template.direction == Gst.PadDirection.SINK) {
128 Gst.Caps intersect = caps.intersect(template.static_caps.get());
129 if (!intersect.is_empty()) {
139 // Inspect element and try to connect something on the srcpads.
140 // If there are dynamic pads, set up a signal handler to
141 // continue autoplugging when they become available.
142 void closeLink(Gst.Element element) {
143 Gst.Pad[] to_connect = new Gst.Pad[0];
144 bool is_dynamic = false;
146 foreach (Gst.PadTemplate template in element.get_pad_template_list ()) {
147 if (template.direction != Gst.PadDirection.SRC)
149 if (template.presence == Gst.PadPresence.ALWAYS) {
150 Gst.Pad pad = element.get_pad(template.name_template);
152 } else if (template.presence == Gst.PadPresence.SOMETIMES) {
153 Gst.Pad pad = element.get_pad(template.name_template);
156 else is_dynamic = true;
161 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
162 "%s is a dynamic element".printf(element.get_name()));
163 controlDynamicElement(element);
166 foreach (Gst.Pad pad in to_connect)
167 closePadLink(element, pad, pad.get_caps());
170 bool isDemuxer(Gst.Element element) {
171 if (!element.get_factory().get_klass().contains("Demux"))
174 int potential_src_pads = 0;
175 foreach (Gst.PadTemplate template in element.get_pad_template_list()) {
176 if (template.direction != Gst.PadDirection.SRC)
179 if (template.presence == Gst.PadPresence.REQUEST ||
180 template.name_template.contains("%")) {
181 potential_src_pads += 2;
183 } else potential_src_pads += 1;
186 return potential_src_pads > 1;
189 Gst.Pad plugDecodingQueue(Gst.Pad pad) {
190 Gst.Element queue = Gst.ElementFactory.make("queue", null);
191 queue.set_property("max_size_time", QUEUE_SIZE);
193 queue.sync_state_with_parent();
194 pad.link(queue.get_pad("sink"));
195 return queue.get_pad("src");
198 // Tries to link one of the factories' element to the given pad.
199 // Returns the element that was successfully linked to the pad.
200 Gst.Element tryToLink1(Gst.Element source, Gst.Pad in_pad,
201 Gee.ArrayList<Gst.ElementFactory> factories) {
202 Gst.Pad? pad = in_pad;
203 if (isDemuxer(source))
204 pad = plugDecodingQueue(in_pad);
206 Gst.Element result = null;
207 foreach (Gst.ElementFactory factory in factories) {
208 Gst.Element element = factory.create(null);
209 if (element == null) {
210 warning("couldn't create element from factory");
214 Gst.Pad sinkpad = element.get_pad("sink");
219 element.set_state(Gst.State.READY);
220 if (pad.link(sinkpad) != Gst.PadLinkReturn.OK) {
221 element.set_state(Gst.State.NULL);
227 element.set_state(Gst.State.PAUSED);
236 // Finds the list of elements that could connect to the pad.
237 // If the pad has the desired caps, it will create a ghostpad.
238 // If no compatible elements could be found, the search will stop.
239 void closePadLink(Gst.Element element, Gst.Pad pad, Gst.Caps caps) {
240 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
241 "element:%s, pad:%s, caps:%s".printf(element.get_name(),
244 if (caps.is_empty()) {
245 emit(this, Facility.SINGLEDECODEBIN, Level.INFO, "unknown type");
249 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, "type is not known yet, waiting");
253 pad.get_direction ();
255 if (!caps.intersect(this.caps).is_empty()) {
256 // This is the desired caps
258 wrapUp(element, pad);
259 } else if (is_raw(caps)) {
260 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
261 "We hit a raw caps which isn't the wanted one");
262 // TODO : recursively remove everything until demux/typefind
265 if (caps.get_size() > 1) {
266 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
267 "many possible types, delaying");
270 Gee.ArrayList<Gst.ElementFactory> facts = findCompatibleFactory(caps);
271 if (facts.size == 0) {
272 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
276 tryToLink1(element, pad, facts);
280 // Ghost the given pad of element.
281 // Remove non-used elements.
282 void wrapUp(Gst.Element element, Gst.Pad pad) {
285 markValidElements(element);
286 removeUnusedElements(typefind);
287 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
288 "ghosting pad %s".printf(pad.get_name()));
289 srcpad = new Gst.GhostPad("src", pad);
290 if (caps.is_fixed()) {
291 srcpad.set_caps(caps);
293 srcpad.set_active(true);
296 post_message(new Gst.Message.state_dirty(this));
299 // Mark this element and upstreams as valid
300 void markValidElements(Gst.Element element) {
301 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
302 "element:%s".printf(element.get_name()));
303 if (element == typefind)
305 validelements.add(element);
307 // find upstream element
308 Gst.Pad pad = (Gst.Pad) element.sinkpads.first().data;
309 Gst.Element parent = pad.get_peer().get_parent_element();
310 markValidElements(parent);
313 // Remove unused elements connected to srcpad(s) of element
314 void removeUnusedElements(Gst.Element element) {
315 foreach (Gst.Pad pad in element.srcpads)
316 if (pad.is_linked()) {
317 Gst.Element peer = pad.get_peer().get_parent_element();
318 removeUnusedElements(peer);
319 if (!(peer in validelements)) {
320 emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE,
321 "removing %s".printf(peer.get_name()));
322 pad.unlink(pad.get_peer());
323 peer.set_state(Gst.State.NULL);
333 foreach (Gst.Element element in validelements) {
334 element.set_state(Gst.State.NULL);
337 validelements = new Gee.ArrayList<Gst.Element>();
342 public override Gst.StateChangeReturn change_state(Gst.StateChange transition) {
343 Gst.StateChangeReturn res = base.change_state(transition);
344 if (transition == Gst.StateChange.PAUSED_TO_READY)
351 static void typefindHaveTypeCb(Gst.Element t, int probability, Gst.Caps caps,
352 SingleDecodeBin data) {
353 emit(data, Facility.SINGLEDECODEBIN, Level.VERBOSE,
354 "probability:%d, caps:%s".printf(probability, caps.to_string()));
355 data.closePadLink(t, t.get_pad("src"), caps);
358 // Dynamic element Callbacks
360 void dynamicPadAddedCb(Gst.Element element, Gst.Pad pad) {
361 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "dynamicPadAddedCb");
363 closePadLink(element, pad, pad.get_caps());
366 void dynamicNoMorePadsCb(Gst.Element element) {
367 emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "dynamicNoMorePadsCb");