X-Git-Url: http://git.maemo.org/git/?p=fillmore;a=blobdiff_plain;f=src%2Fmarina%2Fsingledecodebin.vala;fp=src%2Fmarina%2Fsingledecodebin.vala;h=adf9153a9c04de098a310c158c5341d6b2ff31ff;hp=0000000000000000000000000000000000000000;hb=a712cd772f4f3db8bed7037bb95c4de94767b230;hpb=2f0296582bf5d3f51db40d299f434fc8240ca6a5 diff --git a/src/marina/singledecodebin.vala b/src/marina/singledecodebin.vala new file mode 100644 index 0000000..adf9153 --- /dev/null +++ b/src/marina/singledecodebin.vala @@ -0,0 +1,370 @@ +/* Ported to Vala from singledecodebin.py in Pitivi: + * + * Copyright (c) 2005, Edward Hervey + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +using Gee; +using Logging; + +extern void qsort(void *p, size_t num, size_t size, GLib.CompareFunc func); + +// Single-stream queue-less decodebin + +// returns true if the caps are RAW +bool is_raw(Gst.Caps caps) { + string rep = caps.to_string(); + string[] valid = { "video/x-raw", "audio/x-raw", "text/plain", "text/x-pango-markup" }; + foreach (string val in valid) + if (rep.has_prefix(val)) + return true; + return false; +} + +// A variant of decodebin. +// Only outputs one stream; doesn't contain any internal queue. +class SingleDecodeBin : Gst.Bin { + Gst.Caps caps; + Gst.Element typefind; + Gst.GhostPad srcpad; + + ArrayList dynamics = new ArrayList(); + ArrayList validelements = new ArrayList(); // added elements + Gst.ElementFactory[] factories; + + const int64 QUEUE_SIZE = 1 * Gst.SECOND; + + class construct { + Gst.PadTemplate sink_pad = new Gst.PadTemplate("sinkpadtemplate", Gst.PadDirection.SINK, + Gst.PadPresence.ALWAYS, new Gst.Caps.any()); + Gst.PadTemplate src_pad = new Gst.PadTemplate("srcpadtemplate", Gst.PadDirection.SINK, + Gst.PadPresence.ALWAYS, new Gst.Caps.any()); + + add_pad_template (src_pad); + add_pad_template (sink_pad); + } + + public SingleDecodeBin(Gst.Caps? caps, string name, string? uri) throws Error { + this.caps = caps == null ? new Gst.Caps.any() : caps; + + typefind = Gst.ElementFactory.make("typefind", "internal-typefind"); + add(typefind); + + if (uri != null) { + Gst.Element file_src = make_element("filesrc"); + + file_src.set("location", uri); + add(file_src); + + file_src.link(typefind); + } else { + Gst.GhostPad sinkpad = new Gst.GhostPad("sink", typefind.get_pad("sink")); + sinkpad.set_active(true); + add_pad(sinkpad); + } + Signal.connect(typefind, "have-type", (Callback) typefindHaveTypeCb, this); + + factories = getSortedFactoryList(); + } + + // internal methods + + void controlDynamicElement(Gst.Element element) { + dynamics.add(element); + element.pad_added.connect(dynamicPadAddedCb); + element.no_more_pads.connect(dynamicNoMorePadsCb); + } + + static int factory_compare(Gst.ElementFactory** a, Gst.ElementFactory** b) { + Gst.PluginFeature* p1 = *(Gst.PluginFeature**)a; + Gst.PluginFeature* p2 = *(Gst.PluginFeature**)b; + return (int) (((Gst.PluginFeature) p2).get_rank() - ((Gst.PluginFeature) p1).get_rank()); + } + + // Returns the list of demuxers, decoders and parsers available, sorted by rank + Gst.ElementFactory[] getSortedFactoryList() { + Gst.Registry registry = Gst.Registry.get_default(); + Gst.ElementFactory[] factories = new Gst.ElementFactory[0]; + + foreach (Gst.PluginFeature plugin_feature in + registry.get_feature_list(typeof(Gst.ElementFactory)) ) { + + Gst.ElementFactory factory = plugin_feature as Gst.ElementFactory; + if (factory == null || factory.get_rank() < 64) + continue; + string klass = factory.get_klass(); + if (klass.contains("Demuxer") || klass.contains("Decoder") || klass.contains("Parse")) + factories += factory; + } + + qsort(factories, factories.length, sizeof(Gst.ElementFactory *), + (GLib.CompareFunc) factory_compare); + return factories; + } + + /* Returns a list of factories (sorted by rank) which can take caps as + * input. Returns empty list if none are compatible. */ + Gee.ArrayList findCompatibleFactory(Gst.Caps caps) { + Gee.ArrayList res = new Gee.ArrayList(); + + foreach (Gst.ElementFactory factory in factories) { + weak GLib.List templates = factory.get_static_pad_templates(); + foreach (Gst.StaticPadTemplate template in templates) + if (template.direction == Gst.PadDirection.SINK) { + Gst.Caps intersect = caps.intersect(template.static_caps.get()); + if (!intersect.is_empty()) { + res.add(factory); + break; + } + } + } + + return res; + } + + // Inspect element and try to connect something on the srcpads. + // If there are dynamic pads, set up a signal handler to + // continue autoplugging when they become available. + void closeLink(Gst.Element element) { + Gst.Pad[] to_connect = new Gst.Pad[0]; + bool is_dynamic = false; + + foreach (Gst.PadTemplate template in element.get_pad_template_list ()) { + if (template.direction != Gst.PadDirection.SRC) + continue; + if (template.presence == Gst.PadPresence.ALWAYS) { + Gst.Pad pad = element.get_pad(template.name_template); + to_connect += pad; + } else if (template.presence == Gst.PadPresence.SOMETIMES) { + Gst.Pad pad = element.get_pad(template.name_template); + if (pad != null) + to_connect += pad; + else is_dynamic = true; + } + } + + if (is_dynamic) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "%s is a dynamic element".printf(element.get_name())); + controlDynamicElement(element); + } + + foreach (Gst.Pad pad in to_connect) + closePadLink(element, pad, pad.get_caps()); + } + + bool isDemuxer(Gst.Element element) { + if (!element.get_factory().get_klass().contains("Demux")) + return false; + + int potential_src_pads = 0; + foreach (Gst.PadTemplate template in element.get_pad_template_list()) { + if (template.direction != Gst.PadDirection.SRC) + continue; + + if (template.presence == Gst.PadPresence.REQUEST || + template.name_template.contains("%")) { + potential_src_pads += 2; + break; + } else potential_src_pads += 1; + } + + return potential_src_pads > 1; + } + + Gst.Pad plugDecodingQueue(Gst.Pad pad) { + Gst.Element queue = Gst.ElementFactory.make("queue", null); + queue.set_property("max_size_time", QUEUE_SIZE); + add(queue); + queue.sync_state_with_parent(); + pad.link(queue.get_pad("sink")); + return queue.get_pad("src"); + } + + // Tries to link one of the factories' element to the given pad. + // Returns the element that was successfully linked to the pad. + Gst.Element tryToLink1(Gst.Element source, Gst.Pad in_pad, + Gee.ArrayList factories) { + Gst.Pad? pad = in_pad; + if (isDemuxer(source)) + pad = plugDecodingQueue(in_pad); + + Gst.Element result = null; + foreach (Gst.ElementFactory factory in factories) { + Gst.Element element = factory.create(null); + if (element == null) { + warning("couldn't create element from factory"); + continue; + } + + Gst.Pad sinkpad = element.get_pad("sink"); + if (sinkpad == null) + continue; + + add(element); + element.set_state(Gst.State.READY); + if (pad.link(sinkpad) != Gst.PadLinkReturn.OK) { + element.set_state(Gst.State.NULL); + remove(element); + continue; + } + + closeLink(element); + element.set_state(Gst.State.PAUSED); + + result = element; + break; + } + + return result; + } + + // Finds the list of elements that could connect to the pad. + // If the pad has the desired caps, it will create a ghostpad. + // If no compatible elements could be found, the search will stop. + void closePadLink(Gst.Element element, Gst.Pad pad, Gst.Caps caps) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "element:%s, pad:%s, caps:%s".printf(element.get_name(), + pad.get_name(), + caps.to_string())); + if (caps.is_empty()) { + emit(this, Facility.SINGLEDECODEBIN, Level.INFO, "unknown type"); + return; + } + if (caps.is_any()) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, "type is not known yet, waiting"); + return; + } + + pad.get_direction (); + + if (!caps.intersect(this.caps).is_empty()) { + // This is the desired caps + if (srcpad == null) + wrapUp(element, pad); + } else if (is_raw(caps)) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "We hit a raw caps which isn't the wanted one"); + // TODO : recursively remove everything until demux/typefind + } else { + // Find something + if (caps.get_size() > 1) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "many possible types, delaying"); + return; + } + Gee.ArrayList facts = findCompatibleFactory(caps); + if (facts.size == 0) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "unknown type"); + return; + } + tryToLink1(element, pad, facts); + } + } + + // Ghost the given pad of element. + // Remove non-used elements. + void wrapUp(Gst.Element element, Gst.Pad pad) { + if (srcpad != null) + return; + markValidElements(element); + removeUnusedElements(typefind); + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "ghosting pad %s".printf(pad.get_name())); + srcpad = new Gst.GhostPad("src", pad); + if (caps.is_fixed()) { + srcpad.set_caps(caps); + } + srcpad.set_active(true); + + add_pad(srcpad); + post_message(new Gst.Message.state_dirty(this)); + } + + // Mark this element and upstreams as valid + void markValidElements(Gst.Element element) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "element:%s".printf(element.get_name())); + if (element == typefind) + return; + validelements.add(element); + + // find upstream element + Gst.Pad pad = (Gst.Pad) element.sinkpads.first().data; + Gst.Element parent = pad.get_peer().get_parent_element(); + markValidElements(parent); + } + + // Remove unused elements connected to srcpad(s) of element + void removeUnusedElements(Gst.Element element) { + foreach (Gst.Pad pad in element.srcpads) + if (pad.is_linked()) { + Gst.Element peer = pad.get_peer().get_parent_element(); + removeUnusedElements(peer); + if (!(peer in validelements)) { + emit(this, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "removing %s".printf(peer.get_name())); + pad.unlink(pad.get_peer()); + peer.set_state(Gst.State.NULL); + remove(peer); + } + } + } + + void cleanUp() { + if (srcpad != null) + remove_pad(srcpad); + srcpad = null; + foreach (Gst.Element element in validelements) { + element.set_state(Gst.State.NULL); + remove(element); + } + validelements = new Gee.ArrayList(); + } + + // Overrides + + public override Gst.StateChangeReturn change_state(Gst.StateChange transition) { + Gst.StateChangeReturn res = base.change_state(transition); + if (transition == Gst.StateChange.PAUSED_TO_READY) + cleanUp(); + return res; + } + + // Signal callbacks + + static void typefindHaveTypeCb(Gst.Element t, int probability, Gst.Caps caps, + SingleDecodeBin data) { + emit(data, Facility.SINGLEDECODEBIN, Level.VERBOSE, + "probability:%d, caps:%s".printf(probability, caps.to_string())); + data.closePadLink(t, t.get_pad("src"), caps); + } + + // Dynamic element Callbacks + + void dynamicPadAddedCb(Gst.Element element, Gst.Pad pad) { + emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "dynamicPadAddedCb"); + if (srcpad == null) + closePadLink(element, pad, pad.get_caps()); + } + + void dynamicNoMorePadsCb(Gst.Element element) { + emit(this, Facility.SIGNAL_HANDLERS, Level.INFO, "dynamicNoMorePadsCb"); + } +} +