/** * Base class for the GStreamer object hierarchy * * <p>GstObject provides a root for the object hierarchy tree filed in by the GStreamer library. It * is currently a thin wrapper on top of {@link GObject}. It is an abstract class that is not very * usable on its own. */ public class GstObject extends GObject { private static Logger logger = Logger.getLogger(GstObject.class.getName()); private static final GstObjectAPI gst = GstNative.load(GstObjectAPI.class); /** * Wraps an underlying C GstObject with a Java proxy * * @param init Initialization data */ public GstObject(Initializer init) { super(init); } protected static Initializer initializer(Pointer ptr) { return initializer(ptr, true, true); } protected static Initializer initializer(Pointer ptr, boolean needRef) { return initializer(ptr, needRef, true); } /** * Steal the native peer from another GstObject. After calling this, the victim object is * disconnected from the native object and any attempt to use it will throw an exception. * * @param victim The GstObject to takeover. * @return An Initializer that can be passed to {@link #GstObject} */ protected static Initializer steal(GstObject victim) { Initializer init = new Initializer(victim.handle(), false, true); victim.invalidate(); return init; } /** * Sets the name of this object, or gives this object a guaranteed unique name (if name is null). * * <p>Returns: TRUE if the name could be set. Since Objects that have a parent cannot be renamed, * this function returns FALSE in those cases. * * <p>MT safe. * * @param name new name of object * @return true if the name was set. Since Objects that have a parent cannot be renamed, this * function returns false in those cases. */ public boolean setName(String name) { logger.entering("GstObject", "setName", name); return gst.gst_object_set_name(this, name); } /** * Returns a copy of the name of this object. * * <p>For a nameless object, this returns null. * * @return the name of this object. */ public String getName() { logger.entering("GstObject", "getName"); return gst.gst_object_get_name(this); } @Override public String toString() { return String.format("%s: [%s]", getClass().getSimpleName(), getName()); } protected void ref() { gst.gst_object_ref(this); } protected void unref() { gst.gst_object_unref(this); } @Override protected void sink() { gst.gst_object_ref_sink(this); } public GstObject getParent() { return gst.gst_object_get_parent(this); } /** * Adds an {@link EventListenerProxy} on this object. This is used by subclasses that wish to map * between java style event listeners and gstreamer signals. * * @param listenerClass Class of the listener being added. * @param listener The listener being added. * @param proxy Proxy for the event listener. */ protected synchronized void addListenerProxy( Class<? extends EventListener> listenerClass, EventListener listener, EventListenerProxy proxy) { Map<EventListener, EventListenerProxy> map = getListenerMap().get(listenerClass); /* * Create the map for this class if it doesn't exist */ if (map == null) { map = new HashMap<EventListener, EventListenerProxy>(); getListenerMap().put(listenerClass, map); } map.put(listener, proxy); } /** * Removes an {@link EventListenerProxy} from this object. This is used by subclasses that wish to * map between java style event listeners and gstreamer signals. * * @param listenerClass The class of listener the proxy was added for. * @param listener The listener the proxy was added for. * @return The proxy that was removed, or null if no proxy was found. */ protected synchronized EventListenerProxy removeListenerProxy( Class<? extends EventListener> listenerClass, EventListener listener) { Map<EventListener, EventListenerProxy> map = getListenerMap().get(listenerClass); if (map == null) { return null; } EventListenerProxy proxy = map.remove(listener); /* * Reclaim memory if this listener class is no longer used */ if (map.isEmpty()) { listenerMap.remove(listenerClass); if (listenerMap.isEmpty()) { listenerMap = null; } } return proxy; } private Map<Class<? extends EventListener>, Map<EventListener, EventListenerProxy>> getListenerMap() { if (listenerMap == null) { listenerMap = new HashMap<Class<? extends EventListener>, Map<EventListener, EventListenerProxy>>(); } return listenerMap; } private Map<Class<? extends EventListener>, Map<EventListener, EventListenerProxy>> listenerMap; }
/** Used to query the total duration of a stream. */ public class DurationQuery extends Query { private static interface API extends com.sun.jna.Library { Pointer ptr_gst_query_new_duration(Format format); void gst_query_set_duration(Query query, Format format, /* gint64 */ long duration); void gst_query_parse_duration(Query query, Format[] format, /* gint64 * */ long[] duration); } private static final API gst = GstNative.load(API.class); public DurationQuery(Initializer init) { super(init); } /** * Constructs a new stream duration query object to query in the given format. A duration query * will give the total length of the stream. * * @param format the {@link Format} for this duration query. */ public DurationQuery(Format format) { super(initializer(gst.ptr_gst_query_new_duration(format))); } /** * Answers a duration query by setting the requested value in the given format. * * @param format the {@link Format} for the duration * @param duration the duration of the stream */ public void setDuration(Format format, long duration) { gst.gst_query_set_duration(this, format, duration); } /** * Gets the format of this duration query. * * @return The {@link Format} of the duration value. */ public Format getFormat() { Format[] fmt = new Format[1]; gst.gst_query_parse_duration(this, fmt, null); return fmt[0]; } /** * Gets the duration answer for this duration query. * * @return The total duration. */ public long getDuration() { long[] duration = new long[1]; gst.gst_query_parse_duration(this, null, duration); return duration[0]; } /** * Gets the duration as a user-readable string. * * @return A string representing the duration. */ @Override public String toString() { return String.format("duration: [format=%s, duration=%d]", getFormat(), getDuration()); } }
/** * ElementFactory is used to create instances of elements. * * <p>Use the {@link #find} and {@link #create} methods to create element instances or use {@link * #make} as a convenient shortcut. */ public class ElementFactory extends PluginFeature { private static Logger logger = Logger.getLogger(ElementFactory.class.getName()); private static Level DEBUG = Level.FINE; private static final Map<String, Class<? extends Element>> typeMap = new HashMap<String, Class<? extends Element>>(); public static final String GTYPE_NAME = "GstElementFactory"; private static interface API extends GstElementFactoryAPI, GstCapsAPI, GstPadTemplateAPI, GstPluginAPI {} private static final API gst = GstNative.load(API.class); private static final GlibAPI glib = GlibAPI.GLIB_API; /** Register a new class into the typeMap. */ public static void registerElement(Class<? extends Element> klass, String name) { typeMap.put(name, klass); } /** * Retrieve an instance of a factory that can produce {@link Element}s * * @param name The type of {@link Element} to produce. * @return An ElementFactory that will produce {@link Element}s of the desired type. */ public static ElementFactory find(String name) { logger.entering("ElementFactory", "find", name); ElementFactory factory = gst.gst_element_factory_find(name); if (factory == null) { throw new IllegalArgumentException("No such Gstreamer factory: " + name); } return factory; } /** * Creates a new Element from the specified factory. * * @param factoryName The name of the factory to use to produce the Element * @param name The name to assign to the created Element * @return A new GstElemElement */ public static Element make(String factoryName, String name) { logger.entering("ElementFactory", "make", new Object[] {factoryName, name}); return elementFor(makeRawElement(factoryName, name), factoryName); } /** * Get a list of factories that match the given type. Only elements with a rank greater or equal * to minrank will be returned. The list of factories is returned by decreasing rank. * * @param type a {@link ElementFactoryListType} * @param minrank Minimum rank * @return a List of ElementFactory elements. */ public static List<ElementFactory> listGetElement(ElementFactoryListType type, Rank minrank) { GList glist = gst.gst_element_factory_list_get_elements(type.getValue(), minrank.getValue()); List<ElementFactory> list = new ArrayList<ElementFactory>(); GList next = glist; while (next != null) { if (next.data != null) { ElementFactory fact = new ElementFactory(initializer(next.data, true, true)); list.add(fact); } next = next.next(); } gst.gst_plugin_list_free(glist); return list; } private static List<ElementFactory> lister( GList glist, Caps caps, PadDirection direction, boolean subsetonly) { List<ElementFactory> filterList = new ArrayList<ElementFactory>(); GList gFilterList = gst.gst_element_factory_list_filter(glist, caps, direction, subsetonly); GList next = gFilterList; while (next != null) { if (next.data != null) { ElementFactory fact = new ElementFactory(initializer(next.data, true, true)); filterList.add(fact); } next = next.next(); } gst.gst_plugin_list_free(glist); gst.gst_plugin_list_free(gFilterList); return filterList; } /** * Filter out all the elementfactories in list that can handle caps in the given direction. * * <p>If subsetonly is true, then only the elements whose pads templates are a complete superset * of caps will be returned. Else any element whose pad templates caps can intersect with caps * will be returned. * * @param list a {@link List} of {@link ElementFactory} to filter * @param caps a {@link Caps} * @param direction a {@link PadDirection} to filter on * @param subsetonly whether to filter on caps subsets or not. * @return a {@link List} of {@link ElementFactory} elements that match the given requisits. */ public static List<ElementFactory> listFilter( List<ElementFactory> list, Caps caps, PadDirection direction, boolean subsetonly) { GList glist = null; for (ElementFactory fact : list) { fact.ref(); glist = glib.g_list_append(glist, fact.handle()); } return lister(glist, caps, direction, subsetonly); } /** * Get a list of factories that match the given parameter. * * <p>It is a combination of listGetElement and listFilter passing all the results of the first * call to the second. * * <p>This method improves performance because there is no need to map to java list the elements * returned by the first call. * * @param type a {@link ElementFactoryListType} * @param minrank Minimum rank * @param caps a {@link Caps} * @param direction a {@link PadDirection} to filter on * @param subsetonly whether to filter on caps subsets or not. * @return a {@link List} of {@link ElementFactory} elements that match the given requisits. */ public static List<ElementFactory> listGetElementFilter( ElementFactoryListType type, Rank minrank, Caps caps, PadDirection direction, boolean subsetonly) { GList glist = gst.gst_element_factory_list_get_elements(type.getValue(), minrank.getValue()); return lister(glist, caps, direction, subsetonly); } public static Pointer makeRawElement(String factoryName, String name) { logger.entering("ElementFactory", "makeRawElement", new Object[] {factoryName, name}); Pointer elem = gst.ptr_gst_element_factory_make(factoryName, name); logger.log(DEBUG, "Return from gst_element_factory_make=" + elem); if (elem == null) { throw new IllegalArgumentException("No such Gstreamer factory: " + factoryName); } return elem; } @SuppressWarnings("unchecked") private static Element elementFor(Pointer ptr, String factoryName) { Class<? extends Element> cls = typeMap.get(factoryName); cls = (cls == null) ? (Class<Element>) GstTypes.classFor(ptr) : cls; cls = (cls == null || !Element.class.isAssignableFrom(cls)) ? Element.class : cls; return NativeObject.objectFor(ptr, cls); } /** * Creates a new instance of ElementFactory * * @param init internal initialization data. */ public ElementFactory(Initializer init) { super(init); logger.entering("ElementFactory", "<init>", new Object[] {init}); } /** * Creates a new element from the factory. * * @param name the name to assign to the created Element * @return A new {@link Element} */ public Element create(String name) { logger.entering("ElementFactory", "create", name); Pointer elem = gst.ptr_gst_element_factory_create(this, name); logger.log(DEBUG, "gst_element_factory_create returned: " + elem); if (elem == null) { throw new IllegalArgumentException("Cannot create GstElement"); } return elementFor(elem, getName()); } /** * Returns the name of the person who wrote the factory. * * @return The name of the author */ public String getAuthor() { logger.entering("ElementFactory", "getAuthor"); return gst.gst_element_factory_get_author(this); } /** * Returns a description of the factory. * * @return A brief description of the factory. */ public String getDescription() { logger.entering("ElementFactory", "getDescription"); return gst.gst_element_factory_get_description(this); } /** * Returns the long, English name for the factory. * * @return The long, English name for the factory. */ public String getLongName() { logger.entering("ElementFactory", "getLongName"); return gst.gst_element_factory_get_longname(this); } /** * Returns a string describing the type of factory. This is an unordered list separated with * slashes ('/'). * * @return The description of the type of factory. */ public String getKlass() { logger.entering("ElementFactory", "getKlass"); return gst.gst_element_factory_get_klass(this); } /** * Gets the list of {@link StaticPadTemplate} for this factory. * * @return The list of {@link StaticPadTemplate} */ public List<StaticPadTemplate> getStaticPadTemplates() { logger.entering("ElementFactory", "getStaticPadTemplates"); GList glist = gst.gst_element_factory_get_static_pad_templates(this); logger.log(DEBUG, "gst.gst_element_factory_get_static_pad_templates returned: " + glist); List<StaticPadTemplate> templates = new ArrayList<StaticPadTemplate>(); GList next = glist; while (next != null) { if (next.data != null) { GstStaticPadTemplate temp = new GstStaticPadTemplate(next.data); templates.add( new StaticPadTemplate( temp.name_template, temp.direction, temp.presence, gst.gst_static_caps_get(temp.static_caps))); } next = next.next(); } return templates; } public enum ElementFactoryListType { DECODER((long) 1 << 0), ENCODER((long) 1 << 1), SINK((long) 1 << 2), SRC((long) 1 << 3), MUXER((long) 1 << 4), DEMUXER((long) 1 << 5), PARSER((long) 1 << 6), PAYLOADER((long) 1 << 7), DEPAYLOADER((long) 1 << 8), FORMATTER((long) 1 << 9), MAX_ELEMENTS((long) 1 << 48), ANY((((long) 1) << 49) - 1), MEDIA_ANY(~((long) 0) << 48), MEDIA_VIDEO((long) 1 << 49), MEDIA_AUDIO((long) 1 << 50), MEDIA_IMAGE((long) 1 << 51), MEDIA_SUBTITLE((long) 1 << 52), MEDIA_METADATA((long) 1 << 53), VIDEO_ENCODER(ENCODER.getValue() | MEDIA_VIDEO.getValue() | MEDIA_IMAGE.getValue()), AUDIO_ENCODER(ENCODER.getValue() | MEDIA_AUDIO.getValue()), AUDIOVIDEO_SINKS( SINK.getValue() | MEDIA_AUDIO.getValue() | MEDIA_VIDEO.getValue() | MEDIA_IMAGE.getValue()), DECODABLE(ENCODER.getValue() | DEMUXER.getValue() | DEPAYLOADER.getValue() | PARSER.getValue()); private long value; private ElementFactoryListType(long value) { this.value = value; } public long getValue() { return value; } } }