From ab4fc757494c99dd21bd683defc7e3888d6659ed Mon Sep 17 00:00:00 2001 From: Neil C Smith Date: Fri, 8 Jan 2021 18:34:37 +0000 Subject: [PATCH 1/2] Move query and seek methods to Element as per upstream, and add seekSimple(). --- src/org/freedesktop/gstreamer/Element.java | 130 ++++++++++++++++-- src/org/freedesktop/gstreamer/Pipeline.java | 63 ++++----- .../gstreamer/lowlevel/GstElementAPI.java | 6 +- 3 files changed, 152 insertions(+), 47 deletions(-) diff --git a/src/org/freedesktop/gstreamer/Element.java b/src/org/freedesktop/gstreamer/Element.java index f30017e..57fa69e 100644 --- a/src/org/freedesktop/gstreamer/Element.java +++ b/src/org/freedesktop/gstreamer/Element.java @@ -1,6 +1,6 @@ /* + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2019 Christophe Lafolet - * Copyright (c) 2019 Neil C Smith * Copyright (c) 2009 Levente Farkas * Copyright (C) 2007 Wayne Meissner * Copyright (C) 1999,2000 Erik Walthinsen @@ -22,17 +22,21 @@ */ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.query.Query; -import org.freedesktop.gstreamer.message.Message; -import org.freedesktop.gstreamer.event.Event; import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; +import org.freedesktop.gstreamer.event.Event; +import org.freedesktop.gstreamer.event.SeekEvent; +import org.freedesktop.gstreamer.event.SeekFlags; +import org.freedesktop.gstreamer.event.SeekType; +import org.freedesktop.gstreamer.glib.NativeFlags; import org.freedesktop.gstreamer.glib.Natives; - import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; import org.freedesktop.gstreamer.lowlevel.GstContextPtr; import org.freedesktop.gstreamer.lowlevel.GstIteratorPtr; import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; +import org.freedesktop.gstreamer.message.Message; +import org.freedesktop.gstreamer.query.Query; import static org.freedesktop.gstreamer.lowlevel.GstElementAPI.GSTELEMENT_API; import static org.freedesktop.gstreamer.lowlevel.GObjectAPI.GOBJECT_API; @@ -41,8 +45,8 @@ * Abstract base class for all pipeline elements. *

* See upstream documentation at - * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/GstElement.html + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstelement.html *

* Element is the abstract base class needed to construct an element that can be * used in a GStreamer pipeline. Please refer to the plugin writers guide for @@ -185,28 +189,44 @@ public boolean isPlaying() { } /** - * Tells the Element to start playing the media stream. + * Tells the Element to start playing the media stream. Equivalent to + * calling {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#PLAYING}. + * + * @return the status of the element's state change. */ public StateChangeReturn play() { return setState(State.PLAYING); } /** - * Tells the Element to set ready the media stream. + * Tells the Element to set ready the media stream. Equivalent to calling + * {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#READY}. + * + * @return the status of the element's state change. */ public StateChangeReturn ready() { return setState(State.READY); } /** - * Tells the Element to pause playing the media stream. + * Tells the Element to pause playing the media stream. Equivalent to + * calling {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#PAUSED}. + * + * @return the status of the element's state change. */ public StateChangeReturn pause() { return setState(State.PAUSED); } /** - * Tells the Element to pause playing the media stream. + * Tells the Element to pause playing the media stream. Equivalent to + * calling {@link #setState(org.freedesktop.gstreamer.State)} with + * {@link State#NULL}. + * + * @return the status of the element's state change. */ public StateChangeReturn stop() { return setState(State.NULL); @@ -784,6 +804,94 @@ public Context getContext(String context_type) { return gstContextPtr != null ? Natives.callerOwnsReturn(gstContextPtr, Context.class) : null; } + /** + * Queries an element (usually top-level pipeline or playbin element) for + * the total stream duration in nanoseconds. This query will only work once + * the pipeline is prerolled (i.e. reached PAUSED or PLAYING state). The + * application will receive an ASYNC_DONE message on the pipeline bus when + * that is the case. + *

+ * If the duration changes for some reason, you will get a DURATION_CHANGED + * message on the pipeline bus, in which case you should re-query the + * duration using this function. + * + * @param format the {@code Format} to return the duration in + * @return the total duration of the current media stream, or -1 if the + * query failed + */ + public long queryDuration(Format format) { + long[] dur = {0}; + return GSTELEMENT_API.gst_element_query_duration(this, format, dur) ? dur[0] : -1L; + } + + /** + * Queries an element (usually top-level pipeline or playbin element) for + * the stream position in nanoseconds. This will be a value between 0 and + * the stream duration (if the stream duration is known). This query will + * usually only work once the pipeline is prerolled (i.e. reached PAUSED or + * PLAYING state). The application will receive an ASYNC_DONE message on the + * pipeline bus when that is the case. + * + * @param format The {@link Format} to return the position in + * @return the current position or -1 if the query failed. + */ + public long queryPosition(Format format) { + long[] pos = {0}; + return GSTELEMENT_API.gst_element_query_position(this, format, pos) ? pos[0] : -1L; + } + + /** + * Sends a seek event to an element. See {@link SeekEvent} for the details + * of the parameters. The seek event is sent to the element using the native + * equivalent of {@link #sendEvent(org.freedesktop.gstreamer.event.Event)}. + * + * @param rate the new playback rate + * @param format the format of the seek values + * @param seekFlags the seek flags + * @param startType the type and flags for the new start position + * @param start the value of the new start position + * @param stopType the type and flags for the new stop position + * @param stop the value of the new stop position + * @return true if seek operation succeeded. Flushing seeks will trigger a + * preroll, which will emit an ASYNC_DONE message + */ + public boolean seek(double rate, Format format, Set seekFlags, + SeekType startType, long start, SeekType stopType, long stop) { + + return GSTELEMENT_API.gst_element_seek(this, rate, format, + NativeFlags.toInt(seekFlags), startType, start, stopType, stop); + } + + /** + * Simple API to perform a seek on the given element, meaning it just seeks + * to the given position relative to the start of the stream. For more + * complex operations like segment seeks (e.g. for looping) or changing the + * playback rate or seeking relative to the last configured playback segment + * you should use + * {@link #seek(double, org.freedesktop.gstreamer.Format, java.util.Set, org.freedesktop.gstreamer.event.SeekType, long, org.freedesktop.gstreamer.event.SeekType, long)}. + *

+ * In a completely prerolled PAUSED or PLAYING pipeline, seeking is always + * guaranteed to return TRUE on a seekable media type or FALSE when the + * media type is certainly not seekable (such as a live stream). + *

+ * Some elements allow for seeking in the READY state, in this case they + * will store the seek event and execute it when they are put to PAUSED. If + * the element supports seek in READY, it will always return TRUE when it + * receives the event in the READY state. + * + * @param format a {@link Format} to seek in, such as {@link Format#TIME} + * @param seekFlags seek options; playback applications will usually want to + * use {@link SeekFlags#FLUSH} and {@link SeekFlags#KEY_UNIT} here + * @param seekPosition position to seek to (relative to the start); if you + * are doing a seek in format TIME this value is in nanoseconds + * @return true if seek operation succeeded. Flushing seeks will trigger a + * preroll, which will emit an ASYNC_DONE message + */ + public boolean seekSimple(Format format, Set seekFlags, long seekPosition) { + return GSTELEMENT_API.gst_element_seek_simple(this, format, + NativeFlags.toInt(seekFlags), seekPosition); + } + static class Handle extends GstObject.Handle { public Handle(GstObjectPtr ptr, boolean ownsHandle) { diff --git a/src/org/freedesktop/gstreamer/Pipeline.java b/src/org/freedesktop/gstreamer/Pipeline.java index 4ca63a4..49f1375 100644 --- a/src/org/freedesktop/gstreamer/Pipeline.java +++ b/src/org/freedesktop/gstreamer/Pipeline.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2008 Wayne Meissner * Copyright (C) 2000 Erik Walthinsen * 2005 Wim Taymans @@ -20,23 +20,21 @@ */ package org.freedesktop.gstreamer; -import org.freedesktop.gstreamer.event.SeekType; -import org.freedesktop.gstreamer.event.SeekFlags; -import org.freedesktop.gstreamer.query.Query; -import java.util.concurrent.TimeUnit; - import com.sun.jna.Pointer; import java.util.EnumSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; - +import org.freedesktop.gstreamer.event.SeekFlags; +import org.freedesktop.gstreamer.event.SeekType; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; +import org.freedesktop.gstreamer.query.Query; import static org.freedesktop.gstreamer.lowlevel.GstElementAPI.GSTELEMENT_API; import static org.freedesktop.gstreamer.lowlevel.GstPipelineAPI.GSTPIPELINE_API; import static org.freedesktop.gstreamer.lowlevel.GstQueryAPI.GSTQUERY_API; -import org.freedesktop.gstreamer.glib.NativeFlags; -import org.freedesktop.gstreamer.glib.Natives; -import org.freedesktop.gstreamer.lowlevel.GstObjectPtr; /** * A {@code Pipeline} is a special {@link Bin} used as the top level container @@ -217,6 +215,9 @@ public Bus getBus() { /** * Sets the position in the media stream to time in nanoseconds. + *

+ * Prefer use of + * {@link Element#seekSimple(org.freedesktop.gstreamer.Format, java.util.Set, long)}. * * @param time The time to change the position to. * @return true if seek is successful @@ -228,6 +229,9 @@ public boolean seek(long time) { /** * Sets the current position in the media stream. + *

+ * Prefer use of + * {@link Element#seekSimple(org.freedesktop.gstreamer.Format, java.util.Set, long)}. * * @param time the time to change the position to. * @param unit the {@code TimeUnit} the time is expressed in. @@ -253,10 +257,11 @@ public boolean seek(long time, TimeUnit unit) { * position of 0, a stop position of -1 and a rate of 1.0. The currently * configured playback segment can be queried with #GST_QUERY_SEGMENT. *

- * + * means that the position should not be updated. + *

* When the rate is positive and start has been updated, playback will start * from the newly configured start position. *

@@ -279,13 +284,18 @@ public boolean seek(long time, TimeUnit unit) { * @param stop the value of the new stop position * @return true if seek is successful */ + // for compatibility public boolean seek(double rate, Format format, EnumSet seekFlags, SeekType startType, long start, SeekType stopType, long stop) { - - return GSTELEMENT_API.gst_element_seek(this, rate, format, NativeFlags.toInt(seekFlags), - startType, start, stopType, stop); + return super.seek(rate, format, seekFlags, startType, start, stopType, stop); } - + + @Override + public boolean seek(double rate, Format format, Set seekFlags, + SeekType startType, long start, SeekType stopType, long stop) { + return super.seek(rate, format, seekFlags, startType, start, stopType, stop); + } + /** * Gets the current position in the media stream. * @@ -296,15 +306,9 @@ public long queryPosition(TimeUnit unit) { return unit.convert(queryPosition(Format.TIME), TimeUnit.NANOSECONDS); } - /** - * Gets the current position in terms of the specified {@link Format}. - * - * @param format The {@link Format} to return the position in. - * @return The current position or -1 if the query failed. - */ + @Override public long queryPosition(Format format) { - long[] pos = {0}; - return GSTELEMENT_API.gst_element_query_position(this, format, pos) ? pos[0] : -1L; + return super.queryPosition(format); } /** @@ -317,16 +321,9 @@ public long queryDuration(TimeUnit unit) { return unit.convert(queryDuration(Format.TIME), TimeUnit.NANOSECONDS); } - /** - * Gets the duration of the current media stream in terms of the specified - * {@link Format}. - * - * @param format the {@code Format} to return the duration in. - * @return The total duration of the current media stream. - */ + @Override public long queryDuration(Format format) { - long[] dur = {0}; - return GSTELEMENT_API.gst_element_query_duration(this, format, dur) ? dur[0] : -1L; + return super.queryDuration(format); } /** diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java index 777e414..ecdb8e8 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstElementAPI.java @@ -64,9 +64,9 @@ public interface GstElementAPI extends com.sun.jna.Library { boolean gst_element_query_position(Element elem, Format fmt, long[] pos); boolean gst_element_query_duration(Element elem, Format fmt, long[] pos); boolean gst_element_query(Element elem, Query query); - boolean gst_element_seek(Element elem, double rate, Format format, int flags, - SeekType cur_type, long cur, SeekType stop_type, long stop); - boolean gst_element_seek_simple(Element elem, Format format, int flags, long pos); + boolean gst_element_seek(Element element, double rate, Format format, int flags, + SeekType start_type, long start, SeekType stop_type, long stop); + boolean gst_element_seek_simple(Element elem, Format format, int seek_flags, long seek_pos); boolean gst_element_link(Element elem1, Element elem2); boolean gst_element_link_filtered(Element elem1, Element elem2, Caps filter); boolean gst_element_link_many(Element... elements); From df7001a9c03223f086ea107abf26fa4d7eb10bdf Mon Sep 17 00:00:00 2001 From: Neil C Smith Date: Fri, 8 Jan 2021 18:55:52 +0000 Subject: [PATCH 2/2] Add Version.of(major, minor) method for creating version for Gst.init(..) --- src/org/freedesktop/gstreamer/Version.java | 36 ++++++++++++++++---- test/org/freedesktop/gstreamer/InitTest.java | 2 +- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/org/freedesktop/gstreamer/Version.java b/src/org/freedesktop/gstreamer/Version.java index 6c493a4..32f598d 100644 --- a/src/org/freedesktop/gstreamer/Version.java +++ b/src/org/freedesktop/gstreamer/Version.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2021 Neil C Smith * Copyright (c) 2008 Wayne Meissner * * This file is part of gstreamer-java. @@ -22,8 +22,8 @@ * Describes a GStreamer version. *

* Also upstream documentation at - * https://gstreamer.freedesktop.org/data/doc/gstreamer/stable/gstreamer/html/gstreamer-GstVersion.html + * https://gstreamer.freedesktop.org/documentation/gstreamer/gst.html#gst_version *

*/ public class Version { @@ -37,9 +37,9 @@ public class Version { private final int major, minor, micro, nano; /** - * Constructor for creating a Version with micro and nano set to zero - most - * useful for requesting version in {@link Gst#init(org.freedesktop.gstreamer.Version) - * } + * Constructor for creating a Version with micro and nano set to zero. For + * requesting version in {@link Gst#init(org.freedesktop.gstreamer.Version)} + * prefer using {@link #of(int, int)}. *

* The library only supports major version 1 * @@ -130,4 +130,28 @@ public boolean checkSatisfies(Version required) { || (major == required.major && minor == required.minor && micro >= required.micro); } + /** + * Create a Version with specified major and minor version, and micro and + * nano version set to zero. Useful for specifying a required version in + * {@link Gst#init(org.freedesktop.gstreamer.Version)}. + *

+ * The library only supports major version 1 + *

+ * Unlike the constructor this method will throw an exception if the version + * is not greater or equal to {@link #BASELINE}, or the major version isn't + * 1. + * + * @param major major version, currently must be 1 + * @param minor minor version, greater or equal to 8 + * @return requested version + * @throws IllegalArgumentException if the requested version is invalid + */ + public static Version of(int major, int minor) { + if (major == BASELINE.getMajor() + && minor >= BASELINE.getMinor()) { + return new Version(major, minor); + } + throw new IllegalArgumentException("Invalid version"); + } + } diff --git a/test/org/freedesktop/gstreamer/InitTest.java b/test/org/freedesktop/gstreamer/InitTest.java index 97f0161..6ad71c4 100644 --- a/test/org/freedesktop/gstreamer/InitTest.java +++ b/test/org/freedesktop/gstreamer/InitTest.java @@ -39,7 +39,7 @@ public InitTest() { @Test public void testInit() { Version available = Gst.getVersion(); - Version notAvailable = new Version(available.getMajor(), available.getMinor() + 2); + Version notAvailable = Version.of(available.getMajor(), available.getMinor() + 2); try { Gst.init(notAvailable); assertTrue("Version check exception not thrown!", false);