Example #1
0
/**
 * 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;
    }
  }
}