All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.freedesktop.gstreamer.Pad Maven / Gradle / Ivy

/* 
 * Copyright (C) 2020 Neil C Smith
 * Copyright (C) 2018 Antonio Morales
 * Copyright (C) 2014 Tom Greenwood 
 * Copyright (C) 2009 Tamas Korodi 
 * Copyright (C) 2007 Wayne Meissner
 * Copyright (C) 1999,2000 Erik Walthinsen 
 *                    2000 Wim Taymans 
 *
 * This file is part of gstreamer-java.
 *
 * This code is free software: you can redistribute it and/or modify it under 
 * the terms of the GNU Lesser General Public License version 3 only, as
 * published by the Free Software Foundation.
 *
 * This code 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 
 * version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with this work.  If not, see .
 */
package org.freedesktop.gstreamer;

import com.sun.jna.NativeLong;
import org.freedesktop.gstreamer.event.Event;
import com.sun.jna.Pointer;
import java.util.HashSet;
import java.util.Set;
import org.freedesktop.gstreamer.glib.NativeFlags;
import org.freedesktop.gstreamer.glib.Natives;

import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback;
import org.freedesktop.gstreamer.lowlevel.GstPadProbeInfo;
import org.freedesktop.gstreamer.lowlevel.GstPadAPI;

import static org.freedesktop.gstreamer.lowlevel.GstPadAPI.GSTPAD_API;
import org.freedesktop.gstreamer.lowlevel.GstPadPtr;

/**
 * Object contained by elements that allows links to other elements.
 * 

* See upstream documentation at * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstPad.html *

* An {@link Element} is linked to other elements via "pads", which are extremely * light-weight generic link points. After two pads are retrieved from an * element with {@link Element#getPad}, the pads can be link with {@link #link}. * (For quick links, you can also use {@link Element#link}, which will make the * obvious link for you if it's straightforward.) *

* Pads are typically created from a {@link PadTemplate} with * {@link #Pad(PadTemplate, String)}. *

* Pads have {@link Caps} attached to it to describe the media type they are * capable of dealing with. {@link #queryCaps} and {@link #setCaps} are used to * manipulate the caps of the pads. Pads created from a pad template cannot set * capabilities that are incompatible with the pad template capabilities. *

* Pads without pad templates can be created with gst_pad_new(), which takes a * direction and a name as an argument. If the name is NULL, then a guaranteed * unique name will be assigned to it. *

* {@link #getParentElement} will retrieve the Element that owns the pad. *

* An Element creating a pad will typically use the various * gst_pad_set_*_function() calls to register callbacks for various events on * the pads. *

* GstElements will use gst_pad_push() and gst_pad_pull_range() to push out or * pull in a buffer. *

* To send an Event on a pad, use {@link #sendEvent} and {@link #pushEvent}. * * @see PadTemplate * @see Element * @see Event */ public class Pad extends GstObject { public static final String GTYPE_NAME = "GstPad"; private static final int EVENT_HAS_INFO_MASK = GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_UPSTREAM; private final Handle handle; /** * Creates a new instance of Pad */ Pad(Initializer init) { this(new Handle(init.ptr.as(GstPadPtr.class, GstPadPtr::new), init.ownsHandle), init.needRef); } private Pad(Handle handle, boolean needRef) { super(handle, needRef); this.handle = handle; } /** * Creates a new pad with the given name in the given direction. If name is * null, a guaranteed unique name (across all pads) will be assigned. * * @param name The name of the new pad. * @param direction The direction of the new pad. */ public Pad(String name, PadDirection direction) { this(Natives.initializer(GSTPAD_API.ptr_gst_pad_new(name, direction), false)); } /** * Creates a new pad with the given name from the given template. * * If name is null, a guaranteed unique name (across all pads) will be * assigned. * * @param template The pad template to use. * @param name The name of the new pad. */ public Pad(PadTemplate template, String name) { this(Natives.initializer(GSTPAD_API.ptr_gst_pad_new_from_template(template, name), false)); } /** * Gets the capabilities this pad can produce or consume. Note that this * method doesn't necessarily return the caps set by sending a * gst_event_new_caps() - use {@link #getCurrentCaps() } for that instead. * queryCaps returns all possible caps a pad can operate with, * using the pad's CAPS query function, If the query fails, this function * will return filter, if not NULL, otherwise ANY. *

* When called on sinkpads filter contains the caps that upstream could * produce in the order preferred by upstream. When called on srcpads filter * contains the caps accepted by downstream in the preferred order. filter * might be NULL but if it is not NULL the returned caps will be a subset of * filter . *

* Note that this function does not return writable Caps. * * @param filter suggested Caps or null * @return a newly allocated copy of the {@link Caps} of this pad. */ public Caps queryCaps(Caps filter) { return GSTPAD_API.gst_pad_query_caps(this, filter); } /** * Gets the capabilities of the allowed media types that can flow through * this pad and its peer. *

* The allowed capabilities is calculated as the intersection of the results * of calling {@link #queryCaps} on this pad and its peer. *

* MT safe. * * @return The allowed {@link Caps} of the pad link, or null if this pad has * no peer. */ public Caps getAllowedCaps() { return GSTPAD_API.gst_pad_get_allowed_caps(this); } /** * Gets the capabilities currently configured on pad with the last * GST_EVENT_CAPS event. * * @return the negotiated #GstCaps or null if this pad has * no current caps * */ public Caps getCurrentCaps() { return GSTPAD_API.gst_pad_get_current_caps(this); } /** * Get the peer of this pad. * * MT safe. * * @return The peer Pad of this Pad. */ public Pad getPeer() { return GSTPAD_API.gst_pad_get_peer(this); } /** * Get the capabilities of the peer connected to this pad. *

* When called on srcpads filter contains the caps that upstream could * produce in the order preferred by upstream. When called on sinkpads * filter contains the caps accepted by downstream in the preferred order. * filter might be NULL but if it is not NULL the returned caps will be a * subset of filter . * * @param filter Caps to filter by, or null * @return the {@link Caps} of the peer pad, or null if there is no peer * pad. */ public Caps peerQueryCaps(Caps filter) { return GSTPAD_API.gst_pad_peer_query_caps(this, filter); } /** * Check if the pad accepts the given caps. * * @param caps a {@link Caps} to check on the pad. * @return true if the pad can accept the caps. */ public boolean queryAcceptCaps(Caps caps) { return GSTPAD_API.gst_pad_query_accept_caps(this, caps); } /** * Check if the peer of this pad accepts the caps. If this pad has no peer, * this method returns true. * * @param caps {@link Caps} to check on the pad * @return true if the peer pad can accept the caps or this pad no peer. */ public boolean peerQueryAcceptCaps(Caps caps) { return GSTPAD_API.gst_pad_peer_query_accept_caps(this, caps); } /** * Links this source pad and a sink pad. * * MT Safe. * * @param sink the sink Pad to link. * @throws PadLinkException if pads cannot be linked. */ public void link(Pad sink) throws PadLinkException { PadLinkReturn result = GSTPAD_API.gst_pad_link(this, sink); if (result != PadLinkReturn.OK) { throw new PadLinkException(result); } } /** * * Unlinks the source pad from the sink pad. Will emit the "unlinked" signal * on both pads. * * MT safe. * * @param pad the sink Pad to unlink. * @return true if the pads were unlinked. This function returns false if * the pads were not linked together. */ public boolean unlink(Pad pad) { return GSTPAD_API.gst_pad_unlink(this, pad); } /** * Check if this pad is linked to another pad or not. * * @return true if the pad is linked, else false. */ public boolean isLinked() { return GSTPAD_API.gst_pad_is_linked(this); } /** * Get the direction of the pad. The direction of the pad is decided at * construction time so this function does not take the LOCK. * * @return The {@link PadDirection} of the pad. */ public PadDirection getDirection() { return GSTPAD_API.gst_pad_get_direction(this); } /** * Get the parent of this pad, cast to an {@link Element}. If this pad has no * parent or its parent is not an element, returns null. * * @return The parent of the pad. */ public Element getParentElement() { return GSTPAD_API.gst_pad_get_parent_element(this); } /** * Activates or deactivates the given pad. Normally called from within core * state change functions. *

* If active is true, makes sure the pad is active. If it is already active, * either in push or pull mode, just return. Otherwise dispatches to the * pad's activate function to perform the actual activation. *

* If not @active, checks the pad's current mode and calls * gst_pad_activate_push() or gst_pad_activate_pull(), as appropriate, with * a FALSE argument. * * @param active whether or not the pad should be active. * @return true if the operation was successful. */ public boolean setActive(boolean active) { return GSTPAD_API.gst_pad_set_active(this, active); } /** * Checks if the pad is blocked or not. This function returns the last * requested state of the pad. It is not certain that the pad is actually * blocking at this point (see {@link #isBlocking}). * * @return true if the pad is blocked. */ public boolean isBlocked() { return GSTPAD_API.gst_pad_is_blocked(this); } /** * Run a runnable under a blocked state * * @param callback The code to run when pad is blocked */ public void block(final Runnable callback) { addEventProbe(new EVENT_PROBE() { public PadProbeReturn eventReceived(Pad pad, Event event) { callback.run(); pad.removeCallback(EVENT_PROBE.class, this); return PadProbeReturn.REMOVE; } }, GstPadAPI.GST_PAD_PROBE_TYPE_IDLE); } /** * Checks if the pad is blocking or not. This is a guaranteed state of * whether the pad is actually blocking on a {@link Buffer} or an * {@link Event}. * * @return true if the pad is blocking. */ public boolean isBlocking() { return GSTPAD_API.gst_pad_is_blocking(this); } /** * Add a listener for the linked signal on this {@link Pad} * * @param listener The listener to be called when a peer {@link Pad} is * linked. */ public void connect(final LINKED listener) { connect(LINKED.class, listener, new GstCallback() { @SuppressWarnings("unused") public boolean callback(Pad pad, Pad peer) { listener.linked(pad, peer); return true; } }); } /** * Remove a listener for the linked signal on this {@link Pad} * * @param listener The listener previously added for this signal. */ public void disconnect(LINKED listener) { disconnect(LINKED.class, listener); } /** * Add a listener for the unlinked signal on this {@link Pad} * * @param listener The listener to be called when when a peer {@link Pad} is * unlinked. */ public void connect(final UNLINKED listener) { connect(UNLINKED.class, listener, new GstCallback() { @SuppressWarnings("unused") public boolean callback(Pad pad, Pad peer) { listener.unlinked(pad, peer); return true; } }); } /** * Remove a listener for the unlinked signal on this * {@link Pad} * * @param listener The listener previously added for this signal. */ public void disconnect(UNLINKED listener) { disconnect(UNLINKED.class, listener); } /** * Be notified of different states of pads. The provided callback is called * for every state that matches mask. *

* Probes are called in groups: First {@link PadProbeType#BLOCK} probes are * called, then others, then finally {@link PadProbeType#IDLE}. The only * exception here are IDLE probes that are called immediately if the pad is * already idle while calling addProbe(). In each of the groups, probes are * called in the order in which they were added. * * @param mask set of mask flags for probe - common options are fields of * {@link PadProbeType} * @param callback callback that will be called with notifications of the * pad state */ public void addProbe(final Set mask, PROBE callback) { addProbe(NativeFlags.toInt(mask), callback); } /** * Be notified of different states of pads. The provided callback is called * for every state that matches mask. *

* Probes are called in groups: First {@link PadProbeType#BLOCK} probes are * called, then others, then finally {@link PadProbeType#IDLE}. The only * exception here are IDLE probes that are called immediately if the pad is * already idle while calling addProbe(). In each of the groups, probes are * called in the order in which they were added. * * @param mask mask flag for probe * @param callback callback that will be called with notifications of the * pad state */ public void addProbe(PadProbeType mask, PROBE callback) { addProbe(mask.intValue(), callback); } synchronized void addProbe(int mask, PROBE callback) { final GstPadAPI.PadProbeCallback probe = new GstPadAPI.PadProbeCallback() { @Override public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { PadProbeInfo info = new PadProbeInfo(probeInfo); PadProbeReturn ret = callback.probeCallback(pad, info); info.invalidate(); if (ret == PadProbeReturn.REMOVE) { // don't want handle to try and remove in GCallback::disconnect // @TODO move to Map of probes over callback handle.probes.remove(probeInfo.id); removeCallback(PROBE.class, callback); } return ret; } }; NativeLong id = handle.addProbe(mask, probe); if (id.longValue() == 0) { // the Probe was an IDLE-Probe and it was already handled synchronously in handle.addProbe, // so no Callback needs to be registered return; } GCallback cb = new GCallback(id, probe) { @Override protected void disconnect() { handle.removeProbe(id); } }; addCallback(PROBE.class, callback, cb); } /** * Remove the provided probe callback from the Pad. * * @param callback callback to remove */ public synchronized void removeProbe(PROBE callback) { removeCallback(PROBE.class, callback); } public void addEventProbe(final EVENT_PROBE listener) { final int mask = GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_BOTH | GstPadAPI.GST_PAD_PROBE_TYPE_EVENT_FLUSH; addEventProbe(listener, mask); } synchronized void addEventProbe(final EVENT_PROBE listener, final int mask) { final GstPadAPI.PadProbeCallback probe = new GstPadAPI.PadProbeCallback() { public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { if ((probeInfo.padProbeType & mask) != 0) { Event event = null; if ((probeInfo.padProbeType & EVENT_HAS_INFO_MASK) != 0) { event = GSTPAD_API.gst_pad_probe_info_get_event(probeInfo); } PadProbeReturn ret = listener.eventReceived(pad, event); if (ret == PadProbeReturn.REMOVE) { // don't want handle to try and remove in GCallback::disconnect handle.probes.remove(probeInfo.id); removeCallback(EVENT_PROBE.class, listener); } return ret; } return PadProbeReturn.OK; } }; NativeLong id = handle.addProbe(mask, probe); if (id.longValue() == 0) { // the Probe was an IDLE-Probe and it was already handled synchronously in handle.addProbe, // so no Callback needs to be registered return; } GCallback cb = new GCallback(id, probe) { @Override protected void disconnect() { handle.removeProbe(id); } }; addCallback(EVENT_PROBE.class, listener, cb); } public void removeEventProbe(EVENT_PROBE listener) { removeCallback(EVENT_PROBE.class, listener); } public synchronized void addDataProbe(final DATA_PROBE listener) { final GstPadAPI.PadProbeCallback probe = new GstPadAPI.PadProbeCallback() { public PadProbeReturn callback(Pad pad, GstPadProbeInfo probeInfo, Pointer user_data) { if ((probeInfo.padProbeType & GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER) != 0) { Buffer buffer = GSTPAD_API.gst_pad_probe_info_get_buffer(probeInfo); PadProbeReturn ret = listener.dataReceived(pad, buffer); if (ret == PadProbeReturn.REMOVE) { // don't want handle to try and remove in GCallback::disconnect handle.probes.remove(probeInfo.id); removeCallback(DATA_PROBE.class, listener); } return ret; } return PadProbeReturn.OK; } }; GCallback cb = new GCallback(handle.addProbe(GstPadAPI.GST_PAD_PROBE_TYPE_BUFFER, probe), probe) { @Override protected void disconnect() { handle.removeProbe(id); } }; addCallback(DATA_PROBE.class, listener, cb); } public void removeDataProbe(DATA_PROBE listener) { removeCallback(DATA_PROBE.class, listener); } /** * Sends the event to this pad. *

* This function can be used by applications to send events in the pipeline. * *

* If this pad is a source pad, event should be an upstream event. * If this pad is a sink pad, event should be a downstream event. *

* For example, you would not send a {@link EventType#EOS} on a src pad; EOS * events only propagate downstream. * *

* Furthermore, some downstream events have to be serialized with data flow, * like EOS, while some can travel out-of-band, like * {@link EventType#FLUSH_START}. If the event needs to be serialized with * data flow, this function will take the pad's stream lock while calling * its event function. * * @param event the event to send. * @return true if the event was handled. */ public boolean sendEvent(Event event) { return GSTPAD_API.gst_pad_send_event(this, event); } /** * Sends the event to the peer of this pad. * *

* This function is mainly used by elements to send events to their peer * elements. * * @param event the event to send * @return true if the event was handled */ public boolean pushEvent(Event event) { return GSTPAD_API.gst_pad_push_event(this, event); } /** * Chain a buffer to pad. *

* The function returns {@link FlowReturn#FLUSHING} if the * pad was flushing. *

* If the caps on buffer are different from the current caps on pad, this * function will call any function installed on pad (see * gst_pad_set_setcaps_function()). If the new caps are not acceptable for * pad, this function returns * {@link FlowReturn#NOT_NEGOTIATED}. *

* The function proceeds calling the chain function installed on pad and the * return value of that function is returned to the caller. * {@link FlowReturn#NOT_SUPPORTED} is returned if pad has no * chain function. *

* In all cases, success or failure, the caller loses its reference to * buffer after calling this function. * * @param buffer the Buffer, returns {@link FlowReturn#ERROR} * if NULL. * @return a org.gstreamer.FlowReturn */ public FlowReturn chain(Buffer buffer) { return GSTPAD_API.gst_pad_chain(this, buffer); } /** * When pad is flushing this function returns * {@link FlowReturn#FLUSHING} immediately. *

* Calls the getRange function of pad, see GstPadGetRangeFunction for a * description of a getRange function. If pad has no getRange function * installed (see gst_pad_set_getrange_function()) this function returns * {@link FlowReturn#NOT_SUPPORTED}. *

* This is a lowlevel function. Usualy {@link Pad#pullRange} is used. * * @param offset The start offset of the buffer * @param size The length of the buffer * @param buffer the Buffer, returns {@link FlowReturn#ERROR} if NULL. * @return a FlowReturn from the peer pad. When this function returns OK, * buffer will contain a valid Buffer. */ public FlowReturn getRange(long offset, int size, Buffer[] buffer) { return GSTPAD_API.gst_pad_get_range(this, offset, size, buffer); } /** * Pulls a buffer from the peer pad. *

* This function will first trigger the pad block signal if it was * installed. *

* When pad is not linked {@link FlowReturn#NOT_LINKED} is returned else * this function returns the result of {@link Pad#getRange} on the peer pad. * See {@link Pad#getRange} for a list of return values and for the * semantics of the arguments of this function. *

* buffer's caps must either be unset or the same as what is already * configured on pad. Renegotiation within a running pull-mode pipeline is * not supported. * * @param offset The start offset of the buffer * @param size The length of the buffer * @param buffer the Buffer, returns {@link FlowReturn#ERROR} if NULL. * @return a FlowReturn from the peer pad. When this function returns OK, * buffer will contain a valid Buffer. MT safe. */ public FlowReturn pullRange(long offset, int size, Buffer[] buffer) { return GSTPAD_API.gst_pad_pull_range(this, offset, size, buffer); } /** * Pushes a buffer to the peer of pad . This function will call installed * block probes before triggering any installed data probes. *

* The function proceeds calling gst_pad_chain() on the peer pad and returns * the value from that function. If pad has no peer, {@link FlowReturn#NOT_LINKED} * will be returned. *

* In all cases, success or failure, the caller loses its reference to * buffer after calling this function. * * @param buffer the GstBuffer to push returns GST_FLOW_ERROR if not. * [transfer full] * @return a GstFlowReturn from the peer pad. * * MT safe. */ public FlowReturn push(final Buffer buffer) { return GSTPAD_API.gst_pad_push(this, buffer); } /** * Gets the template for pad. * * @return the GstPadTemplate from which this pad was instantiated, or NULL * if this pad has no template. */ public PadTemplate getTemplate() { return GSTPAD_API.gst_pad_get_pad_template(this); } /** * Check if the pad has caps set on it with a GST_EVENT_CAPS events * * @return true if the pad has caps set */ public boolean hasCurrentCaps() { return GSTPAD_API.gst_pad_has_current_caps(this); } /** * Signal emitted when new this {@link Pad} is linked to another {@link Pad} * * @see #connect(LINKED) * @see #disconnect(LINKED) */ public static interface LINKED { /** * Called when a {@link Pad} is linked to another Pad. * * @param pad the pad that emitted the signal. * @param peer the peer pad that has been connected. */ public void linked(Pad pad, Pad peer); } /** * Signal emitted when new this {@link Pad} is disconnected from a peer * {@link Pad} * * @see #connect(UNLINKED) * @see #disconnect(UNLINKED) */ public static interface UNLINKED { /** * Called when a {@link Pad} is unlinked from another Pad. * * @param pad the pad that emitted the signal. * @param peer the peer pad that has been connected. */ public void unlinked(Pad pad, Pad peer); } /** * Callback used by * {@link #addProbe(java.util.EnumSet, org.freedesktop.gstreamer.Pad.PROBE)} */ public static interface PROBE { /** * Callback used by * {@link #addProbe(java.util.EnumSet, org.freedesktop.gstreamer.Pad.PROBE)}. * Gets called to notify about the current blocking type. *

* The PadProbeInfo and any Buffer, Event or Query referenced from * it, is only valid for the duration of the callback. * * @param pad Pad that is blocked * @param info PadProbeInfo with access to underlying data * @return PadProbeReturn value */ public PadProbeReturn probeCallback(Pad pad, PadProbeInfo info); } /** * Probe for listening when an event passes through this Pad. * * @see #addEventProbe(EVENT_PROBE) * @see #removeEventProbe(EVENT_PROBE) */ public static interface EVENT_PROBE { public PadProbeReturn eventReceived(Pad pad, Event event); } /** * Probe for listening when new data is available on the Pad. * * @see #addDataProbe(DATA_PROBE) * @see #removeDataProbe(DATA_PROBE) */ public static interface DATA_PROBE { public PadProbeReturn dataReceived(Pad pad, Buffer buffer); } private static class Handle extends GstObject.Handle { private final Set probes; private Handle(GstPadPtr ptr, boolean ownsHandle) { super(ptr, ownsHandle); probes = new HashSet<>(); } @Override protected GstPadPtr getPointer() { return (GstPadPtr) super.getPointer(); } private synchronized NativeLong addProbe(int mask, GstPadAPI.PadProbeCallback probe) { NativeLong id = GSTPAD_API.gst_pad_add_probe(getPointer(), mask, probe, null, null); if (id.longValue() != 0) { probes.add(id); } return id; } private synchronized void removeProbe(NativeLong id) { if (probes.remove(id)) { GSTPAD_API.gst_pad_remove_probe(getPointer(), id); } } private synchronized void clearProbes() { probes.forEach(id -> GSTPAD_API.gst_pad_remove_probe(getPointer(), id)); probes.clear(); } @Override public void invalidate() { clearProbes(); super.invalidate(); } @Override public void dispose() { clearProbes(); super.dispose(); } } }