diff --git a/src/org/freedesktop/gstreamer/Buffer.java b/src/org/freedesktop/gstreamer/Buffer.java index db0194bf..3576c8c5 100644 --- a/src/org/freedesktop/gstreamer/Buffer.java +++ b/src/org/freedesktop/gstreamer/Buffer.java @@ -1,20 +1,20 @@ /* + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2019 Christophe Lafolet - * Copyright (c) 2019 Neil C Smith * Copyright (C) 2014 Tom Greenwood * 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 + * 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 + * 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 @@ -31,9 +31,14 @@ import org.freedesktop.gstreamer.lowlevel.GstBufferAPI.MapInfoStruct; import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; import java.util.EnumSet; +import java.util.Iterator; +import java.util.NoSuchElementException; import org.freedesktop.gstreamer.glib.NativeFlags; import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GstMetaPtr; /** * Buffers are the basic unit of data transfer in GStreamer. They contain the @@ -62,7 +67,7 @@ public Buffer() { * Creates a newly allocated buffer with data of the given size. The buffer * memory is not cleared. If the requested amount of memory cannot be * allocated, an exception will be thrown. - * + *

* Note that when size == 0, the buffer data pointer will be NULL. * * @param size @@ -240,7 +245,7 @@ public void setOffsetEnd(long val) { /** * Get the GstBufferFlags describing this buffer. - * + *

* Since GStreamer 1.10 * * @return an EnumSet of {@link BufferFlags} @@ -252,10 +257,75 @@ public EnumSet getFlags() { return NativeFlags.fromInt(BufferFlags.class, nativeInt); } + /** + * Get the metadata for api on buffer. When there is no such metadata, NULL + * is returned. + * + * @param implementation type of metadata + * @param api api type of metadata + * @return meta or null + */ + public T getMeta(Meta.API api) { + GType apiType = api.getAPIGType(); + if (apiType == GType.INVALID) { + return null; + } + GstMetaPtr ptr = GSTBUFFER_API.gst_buffer_get_meta(this, apiType); + // can not create metadata class from null pointer + if (ptr == null) { + return null; + } + return Natives.objectFor(ptr, api.getImplClass(), false, false); + } + + /** + * Iterate all Meta on buffer. + * + * @return iterator of meta + */ + public Iterator iterateMeta() { + return new MetaIterator(this); + } + + /** + * Check if buffer contains metadata for api. + *

+ * Since GStreamer 1.14 + * + * @param implementation type of metadata + * @param api type of metadata + * @return return true only if buffer contains selected type of metadata + */ + @Gst.Since(minor = 14) + public boolean hasMeta(Meta.API api) { + return getMetaCount(api) > 0; + } + + /** + * Check number of metadata for api. There can be more than one metadata in + * case of multiple video/audio layer. + *

+ * Since GStreamer 1.14 + * + * @param implementation type of metadata + * @param api type of metadata + * @return count of metadata of provided api type + */ + @Gst.Since(minor = 14) + public int getMetaCount(Meta.API api) { + Gst.checkVersion(1, 14); + GType apiType = api.getAPIGType(); + if (apiType == GType.INVALID) { + return 0; + } + return GSTBUFFER_API.gst_buffer_get_n_meta(this, apiType); + } + + /** * Set some of the GstBufferFlags describing this buffer. This is a union * operation and does not clear flags that are not mentioned. - * + *

* Since GStreamer 1.10 * * @param flags an EnumSet of {@link BufferFlags} to be set on the buffer. @@ -270,7 +340,7 @@ public boolean setFlags(EnumSet flags) { /** * unset the GstBufferFlags describing this buffer. This is a difference * operation and does not clear flags that are not mentioned. - * + *

* Since GStreamer 1.10 * * @param flags an EnumSet of {@link BufferFlags} to be cleared on the buffer. @@ -283,4 +353,45 @@ public boolean unsetFlags(EnumSet flags) { return GstBufferAPI.GSTBUFFER_API.gst_buffer_unset_flags(this, NativeFlags.toInt(flags)); } + private static class MetaIterator implements Iterator { + + private final PointerByReference state; + private final Buffer buffer; + private Meta next; + + MetaIterator(final Buffer buffer) { + state = new PointerByReference(); + this.buffer = buffer; + } + + @Override + public boolean hasNext() { + if (next == null) { + next = getNext(); + } + return next != null; + } + + @Override + public Meta next() { + if (!hasNext() || next == null) { + throw new NoSuchElementException(); + } + Meta m = next; + next = null; + return m; + } + + private Meta getNext() { + return Natives.objectFor( + GSTBUFFER_API.gst_buffer_iterate_meta(this.buffer, this.state), + Meta.class, + false, + false + ); + } + + + } + } diff --git a/src/org/freedesktop/gstreamer/Gst.java b/src/org/freedesktop/gstreamer/Gst.java index 23fb2da9..49ea186a 100644 --- a/src/org/freedesktop/gstreamer/Gst.java +++ b/src/org/freedesktop/gstreamer/Gst.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Neil C Smith + * Copyright (c) 2020 Neil C Smith * Copyright (c) 2018 Antonio Morales * Copyright (c) 2007 Wayne Meissner * @@ -23,6 +23,7 @@ import org.freedesktop.gstreamer.message.Message; import org.freedesktop.gstreamer.event.Event; import org.freedesktop.gstreamer.glib.GError; + import static org.freedesktop.gstreamer.lowlevel.GstAPI.GST_API; import java.util.ArrayList; @@ -56,9 +57,12 @@ import org.freedesktop.gstreamer.elements.Elements; import org.freedesktop.gstreamer.glib.GLib; import org.freedesktop.gstreamer.glib.GMainContext; +import org.freedesktop.gstreamer.video.Video; + import static org.freedesktop.gstreamer.lowlevel.GstParseAPI.GSTPARSE_API; import static org.freedesktop.gstreamer.glib.Natives.registration; import static org.freedesktop.gstreamer.lowlevel.GlibAPI.GLIB_API; + import org.freedesktop.gstreamer.webrtc.WebRTC; /** @@ -648,7 +652,8 @@ private static synchronized void loadAllClasses() { new Query.Types(), new Controllers(), new Elements(), - new WebRTC.Types()) + new WebRTC.Types(), + new Video.Types()) .flatMap(NativeObject.TypeProvider::types) .forEachOrdered(GstTypes::register); if (!DISABLE_EXTERNAL) { diff --git a/src/org/freedesktop/gstreamer/Meta.java b/src/org/freedesktop/gstreamer/Meta.java new file mode 100644 index 00000000..35fa73b8 --- /dev/null +++ b/src/org/freedesktop/gstreamer/Meta.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * 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 java.util.Objects; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GType; +import org.freedesktop.gstreamer.lowlevel.GstMetaPtr; + +/** + * Base for all metadata types added to a Buffer. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmeta.html + *

+ */ +public class Meta extends NativeObject { + + /** + * Create Meta from Initializer. + * + * @param init initializer. + */ + protected Meta(Initializer init) { + this(new Handle(init.ptr.as(GstMetaPtr.class, GstMetaPtr::new), + init.ownsHandle)); + } + + /** + * Create Meta from Handle. + * + * @param handle native object handle. + */ + protected Meta(Handle handle) { + super(handle); + } + + /** + * NativeObject.Handle implementation. + */ + protected static class Handle extends NativeObject.Handle { + + /** + * Create Handle. + * + * @param ptr pointer to underlying native GstMeta. + * @param ownsHandle + */ + public Handle(GstMetaPtr ptr, boolean ownsHandle) { + super(ptr, ownsHandle); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + } + + } + + /** + * API for Meta subclass. Used for querying from Buffer. + *

+ * The relevant API will usually be available as a public static final field + * on the implementation class. + *

+ * The API type reflects two distinct types (api and implementation) used in + * the underlying GStreamer GstMetaInfo. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmeta.html#GstMetaInfo + *

+ * + * @param implementation type + */ + public static final class API { + + private final Class implClass; + private final String apiTypeName; + + private GType apiType; + + /** + * Create an API for the given implementation type. + * + * @param impl class implementing the API, must be registered to the + * underlying implementation GType + * @param api name of the underlying API GType + */ + public API(Class impl, String api) { + this.implClass = Objects.requireNonNull(impl); + this.apiTypeName = Objects.requireNonNull(api); + apiType = GType.INVALID; + } + + Class getImplClass() { + return implClass; + } + + GType getAPIGType() { + GType type = apiType; + if (type == GType.INVALID) { + type = GType.valueOf(apiTypeName); + apiType = type; + } + return type; + } + + } +} diff --git a/src/org/freedesktop/gstreamer/MetaFlags.java b/src/org/freedesktop/gstreamer/MetaFlags.java new file mode 100644 index 00000000..35e0d244 --- /dev/null +++ b/src/org/freedesktop/gstreamer/MetaFlags.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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 org.freedesktop.gstreamer.glib.NativeFlags; + + /** + * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/gstreamer/gstmeta.html#GstMetaFlags + */ +/*public*/ enum MetaFlags implements NativeFlags { + +// /** +// * no flags +// */ +// GST_META_FLAG_NONE(0), + /** + * metadata should not be modified + */ + GST_META_FLAG_READONLY(1 << 0), + /** + * metadata is managed by a bufferpool + */ + GST_META_FLAG_POOLED(1 << 1), + /** + * metadata should not be removed + */ + GST_META_FLAG_LOCKED(1 << 2), +// /** +// * additional flags can be added starting from this flag. +// */ +// GST_META_FLAG_LAST(1 << 16) +// + ; + + private final int value; + + MetaFlags(int value) { + this.value = value; + } + + /** + * Gets the integer value of the enum. + * + * @return The integer value for this enum. + */ + @Override + public int intValue() { + return value; + } + + +} diff --git a/src/org/freedesktop/gstreamer/glib/NativeObject.java b/src/org/freedesktop/gstreamer/glib/NativeObject.java index dd31e222..4b2a873d 100644 --- a/src/org/freedesktop/gstreamer/glib/NativeObject.java +++ b/src/org/freedesktop/gstreamer/glib/NativeObject.java @@ -59,12 +59,6 @@ public abstract class NativeObject implements AutoCloseable { // */ protected NativeObject(Handle handle) { this.handle = Objects.requireNonNull(handle); - // - // Only store this object in the map if we can tell when it has been disposed - // (i.e. must be at least a GObject - MiniObject and other NativeObject subclasses - // don't signal destruction, so it is impossible to know if the instance - // is stale or not - // this.ptr = handle.ptrRef.get().getPointer(); if (handle.isCacheable()) { // need to put all nativeRef in map now so WeakReference doesn't go out of scope diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java index b0dce298..770bddc7 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstBufferAPI.java @@ -29,6 +29,8 @@ import com.sun.jna.NativeLong; import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + import static org.freedesktop.gstreamer.lowlevel.GstAPI.GST_PADDING; /** @@ -104,7 +106,9 @@ protected List getFieldOrder() { void gst_buffer_unmap(Buffer buffer, MapInfoStruct info); int gst_buffer_n_memory(Buffer buffer); boolean gst_buffer_map_range(Buffer buffer, int idx, int length, MapInfoStruct info, int flags); - + GstMetaPtr gst_buffer_get_meta(Buffer buffer, GType gType); + int gst_buffer_get_n_meta(Buffer buffer,GType gType); + GstMetaPtr gst_buffer_iterate_meta(Buffer buffer, PointerByReference state); // re-introduces in gstreamer 1.9 int gst_buffer_get_flags(Buffer buffer); boolean gst_buffer_set_flags(Buffer buffer, int flags); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMetaAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstMetaAPI.java new file mode 100644 index 00000000..38c6b3ef --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMetaAPI.java @@ -0,0 +1,53 @@ +package org.freedesktop.gstreamer.lowlevel; + +import com.sun.jna.Library; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; + +/** + */ +public interface GstMetaAPI extends Library { + GstMetaAPI GST_META_API = GstNative.load(GstMetaAPI.class); + + + + @Structure.FieldOrder({"flags", "info"}) + class GstMetaStruct extends Structure { + public static final class ByValue extends GstMetaStruct implements Structure.ByValue { + } + + public int flags; + public GstMetaInfoStruct.ByReference info; + + int infoOffset() { + return fieldOffset("info"); + } + + } + + @Structure.FieldOrder({"api", "type", "size"}) + class GstMetaInfoStruct extends Structure { + public static class ByReference extends GstMetaInfoStruct implements Structure.ByReference { + } + + public GstMetaInfoStruct() { + } + + public GstMetaInfoStruct(Pointer p) { + super(p); + read(); + } + + public GType api; + public GType type; + public long size; + + int typeOffset() { + return fieldOffset("type"); + } + + } + + + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstMetaPtr.java b/src/org/freedesktop/gstreamer/lowlevel/GstMetaPtr.java new file mode 100644 index 00000000..8a286ca6 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstMetaPtr.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * 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.lowlevel; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; + +/** + * Base GstMeta pointer + */ +public class GstMetaPtr extends GTypedPtr { + + private static final int INFO_OFFSET; + private static final int IMPL_TYPE_OFFSET; + + static { + INFO_OFFSET = new GstMetaAPI.GstMetaStruct().infoOffset(); + IMPL_TYPE_OFFSET = new GstMetaAPI.GstMetaInfoStruct().typeOffset(); + } + + public GstMetaPtr() { + } + + public GstMetaPtr(Pointer ptr) { + super(ptr); + } + + @Override + public GType getGType() { + // Quick getter for GType without allocation + Pointer metaInfo = getPointer().getPointer(INFO_OFFSET); + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(metaInfo.getLong(IMPL_TYPE_OFFSET)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf(((long) metaInfo.getInt(IMPL_TYPE_OFFSET)) & 0xffffffffL); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + + public GType getAPIGType() { + // Quick getter for GType without allocation + Pointer metaInfo = getPointer().getPointer(INFO_OFFSET); + if (Native.SIZE_T_SIZE == 8) { + return GType.valueOf(metaInfo.getLong(0)); + } else if (Native.SIZE_T_SIZE == 4) { + return GType.valueOf(((long) metaInfo.getInt(0)) & 0xffffffffL); + } else { + throw new IllegalStateException("SIZE_T size not supported: " + Native.SIZE_T_SIZE); + } + } + +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java index 7938e931..f83ced11 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstVideoAPI.java @@ -1,4 +1,4 @@ -/* +/* * This file is part of gstreamer-java. * * This code is free software: you can redistribute it and/or modify it under @@ -21,13 +21,20 @@ import com.sun.jna.Library; import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; +import org.freedesktop.gstreamer.video.VideoTimeCodeFlags; public interface GstVideoAPI extends Library { public final static GstVideoAPI GSTVIDEO_API = GstNative.load("gstvideo", GstVideoAPI.class); + @CallerOwnsReturn + Pointer gst_video_time_code_new_empty(); + void gst_video_time_code_free(Pointer gstVideoTimeCode); GValue gst_video_frame_rate(Pad pad); boolean gst_video_get_size(Pad pad, int [] width, int [] height); - + /* */ Pointer ptr_gst_video_event_new_downstream_force_key_unit( long timestamp, long stream_time, long running_time, @@ -35,4 +42,71 @@ Pointer ptr_gst_video_event_new_downstream_force_key_unit( Pointer ptr_gst_video_event_new_upstream_force_key_unit( long running_time, boolean all_headers, int count); + + GType gst_video_time_code_meta_api_get_type(); + + GType gst_video_crop_meta_api_get_type(); + + GType gst_video_gl_texture_upload_meta_api_get_type(); + + GType gst_video_meta_api_get_type(); + + GType gst_video_region_of_interest_meta_api_get_type(); + +// MetaInfo gst_video_time_code_meta_get_info(); + + + @Structure.FieldOrder({"meta", "tc"}) + class GstVideoTimeCodeMetaStruct extends Structure { + public GstMetaAPI.GstMetaStruct.ByValue meta; + public GstVideoTimeCodeStruct.ByValue tc; + + public GstVideoTimeCodeMetaStruct(Pointer p) { + super(p); + read(); + } + } + + @Structure.FieldOrder({"config", "hours", "minutes", "seconds", "frames", "field_count"}) + @Gst.Since(minor = 10) + class GstVideoTimeCodeStruct extends Structure { + public static class ByValue extends GstVideoTimeCodeStruct implements Structure.ByValue { + } + + public GstVideoTimeCodeConfigStruct.ByValue config; + public int hours; + public int minutes; + public int seconds; + public int frames; + public int field_count; + + public GstVideoTimeCodeStruct() { + } + + public GstVideoTimeCodeStruct(Pointer p) { + super(p); + read(); + } + } + + @Structure.FieldOrder({"fps_n", "fps_d", "flags", "latest_daily_jam"}) + @Gst.Since(minor = 10) + class GstVideoTimeCodeConfigStruct extends Structure { + + public static class ByValue extends GstVideoTimeCodeConfigStruct implements Structure.ByValue { + } + + public int fps_n; + public int fps_d; + public int flags; + public Pointer latest_daily_jam; + + public GstVideoTimeCodeConfigStruct() { + } + + public GstVideoTimeCodeConfigStruct(Pointer p) { + super(p); + read(); + } + } } diff --git a/src/org/freedesktop/gstreamer/video/Video.java b/src/org/freedesktop/gstreamer/video/Video.java new file mode 100644 index 00000000..149217b3 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/Video.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * 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.video; + +import java.util.stream.Stream; +import org.freedesktop.gstreamer.glib.NativeObject; + +import static org.freedesktop.gstreamer.glib.Natives.registration; + +/** + * Utility class for GStreamer Video library. + */ +public final class Video { + + private Video() { + } + + /** + * TypeProvider implementation for GStreamer Video library. + */ + public static class Types implements NativeObject.TypeProvider { + + @Override + public Stream> types() { + return Stream.of( + registration(VideoTimeCodeMeta.class, VideoTimeCodeMeta.GTYPE_NAME, + VideoTimeCodeMeta::new) + ); + } + + } + +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCode.java b/src/org/freedesktop/gstreamer/video/VideoTimeCode.java new file mode 100644 index 00000000..28b053a5 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCode.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GstVideoTimeCodeStruct; + + +/** + * A representation of a SMPTE time code. + * + * See https://gstreamer.freedesktop.org/documentation/video/gstvideotimecode.html + */ +@Gst.Since(minor = 10) +public class VideoTimeCode extends NativeObject { + + private final GstVideoTimeCodeStruct timeCodeStruct; + private final VideoTimeCodeConfig timeCodeConfig; + +// public VideoTimeCode(){ +// this(Natives.initializer(GstVideoAPI.GSTVIDEO_API.gst_video_time_code_new_empty())); +// } + + VideoTimeCode(GstVideoTimeCodeStruct struct) { + this(struct, new Handle(new GPointer(struct.getPointer()), false)); + } + + private VideoTimeCode(GstVideoTimeCodeStruct struct, Handle handle) { + super(handle); + this.timeCodeStruct = struct; + timeCodeConfig = new VideoTimeCodeConfig(timeCodeStruct.config); + } + + public VideoTimeCodeConfig getConfig() { + return timeCodeConfig; + } + + /** + * Hours field, must be less than 24. + * + * @return number of hours + */ + public int getHours() { + return timeCodeStruct.hours; + } + + /** + * Minutes field, must be less than 60. + * + * @return number of minutes + */ + public int getMinutes() { + return timeCodeStruct.minutes; + } + + /** + * Second field, must be less than 60. + * + * @return number of seconds + */ + public int getSeconds() { + return timeCodeStruct.seconds; + } + + /** + * Frames field. + * + * @return number of seconds + */ + public int getFrames() { + return timeCodeStruct.frames; + } + + @Override + public String toString() { + return "GstVideoTimeCode{" + getHours() + ":" + getMinutes() + ":" + getSeconds() + ":" + getFrames() + ", timeconfig=" + timeCodeConfig + "}"; + } + + @Override + public void disown() { + timeCodeConfig.disown(); + super.disown(); + } + + private static final class Handle extends NativeObject.Handle{ + + /** + * Construct a Handle for the supplied native reference. + * + * @param ptr native reference + * @param ownsReference whether the Handle owns the native reference and + */ + public Handle(GPointer ptr, boolean ownsReference) { + super(ptr, ownsReference); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + GstVideoAPI.GSTVIDEO_API.gst_video_time_code_free(ptr.getPointer()); + } + + @Override + protected GPointer getPointer() { + return super.getPointer(); + } + + + } +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCodeConfig.java b/src/org/freedesktop/gstreamer/video/VideoTimeCodeConfig.java new file mode 100644 index 00000000..3a0cd737 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCodeConfig.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import java.util.EnumSet; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.freedesktop.gstreamer.glib.NativeObject; +import org.freedesktop.gstreamer.lowlevel.GPointer; +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GstVideoTimeCodeConfigStruct; + +/** + * The configuration of the time code. + *

+ * See upstream documentation at + * + * https://gstreamer.freedesktop.org/documentation/video/gstvideotimecode.html#GstVideoTimeCodeConfig + */ +@Gst.Since(minor = 10) +public class VideoTimeCodeConfig extends NativeObject { + + private final GstVideoTimeCodeConfigStruct timeCodeConfig; + + VideoTimeCodeConfig(GstVideoTimeCodeConfigStruct struct) { + this(struct, new Handle(new GPointer(struct.getPointer()), false)); + } + + private VideoTimeCodeConfig(GstVideoTimeCodeConfigStruct struct, Handle handle) { + super(handle); + timeCodeConfig = struct; + } + + /** + * The corresponding {@link VideoTimeCodeFlags}. + * + * @return return flags for current timecode + */ + public EnumSet getFlags() { + return NativeFlags.fromInt(VideoTimeCodeFlags.class, timeCodeConfig.flags); + } + + /** + * Numerator of the frame rate. + * + * @return numerator + */ + public int getNumerator() { + return timeCodeConfig.fps_n; + } + + /** + * Denominator of the frame rate. + * + * @return denominator + */ + public int getDenominator() { + return timeCodeConfig.fps_d; + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("GstVideoTimeCodeConfig{"); + sb.append("flags=").append(getFlags()) + .append(", numerator=").append(getNumerator()) + .append(", denominator=").append(getDenominator()) + .append('}'); + return sb.toString(); + } + + private static final class Handle extends NativeObject.Handle { + + /** + * Construct a Handle for the supplied native reference. + * + * @param ptr native reference + * @param ownsReference whether the Handle owns the native reference and + */ + public Handle(GPointer ptr, boolean ownsReference) { + super(ptr, ownsReference); + } + + @Override + protected void disposeNativeHandle(GPointer ptr) { + // usually video timecode config will be released together with video timecode + // GlibAPI.GLIB_API.g_free(ptr.getPointer()); + } + + @Override + protected GPointer getPointer() { + return super.getPointer(); + } + + } +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCodeFlags.java b/src/org/freedesktop/gstreamer/video/VideoTimeCodeFlags.java new file mode 100644 index 00000000..804552c4 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCodeFlags.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import org.freedesktop.gstreamer.glib.NativeFlags; + +/** + * Flags related to the time code information. For drop frame, only 30000/1001 + * and 60000/1001 frame rates are supported. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/video/gstvideotimecode.html#GstVideoTimeCodeFlags + */ +public enum VideoTimeCodeFlags implements NativeFlags { +// /** +// * No flags +// */ +// GST_VIDEO_TIME_CODE_FLAGS_NONE(0), // No flags + /** + * Whether we have drop frame rate + */ + GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME(1), + /** + * Whether we have interlaced video + */ + GST_VIDEO_TIME_CODE_FLAGS_INTERLACED(2); + + + private final int value; + + VideoTimeCodeFlags(int value) { + this.value = value; + } + + @Override + public int intValue() { + return value; + } +} diff --git a/src/org/freedesktop/gstreamer/video/VideoTimeCodeMeta.java b/src/org/freedesktop/gstreamer/video/VideoTimeCodeMeta.java new file mode 100644 index 00000000..ea683655 --- /dev/null +++ b/src/org/freedesktop/gstreamer/video/VideoTimeCodeMeta.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.Meta; + +import static org.freedesktop.gstreamer.lowlevel.GstVideoAPI.GstVideoTimeCodeMetaStruct; + + /** + * Extra buffer metadata describing the GstVideoTimeCode of the frame. + *

+ * See upstream documentation at + * https://gstreamer.freedesktop.org/documentation/video/gstvideometa.html#GstVideoTimeCodeMeta + */ +@Gst.Since(minor = 10) +public class VideoTimeCodeMeta extends Meta { + + /** + * Meta.API for VideoTimeCodeMeta. + */ + public static final API API = + new API(VideoTimeCodeMeta.class, "GstVideoTimeCodeMetaAPI"); + + /** + * Underlying GType name. + */ + public static final String GTYPE_NAME = "GstVideoTimeCodeMeta"; + + private final VideoTimeCode timeCode; + + VideoTimeCodeMeta(Initializer init) { + super(init); + GstVideoTimeCodeMetaStruct metaStruct = + new GstVideoTimeCodeMetaStruct(init.ptr.getPointer()); + timeCode = new VideoTimeCode(metaStruct.tc); + } + + /** + * Retrieve the time code attached to frame. + * + * @return time code + */ + public VideoTimeCode getTimeCode() { + return timeCode; + } + + @Override + public void disown() { + timeCode.disown(); + super.disown(); + } + +} diff --git a/test/org/freedesktop/gstreamer/BufferProbeTester.java b/test/org/freedesktop/gstreamer/BufferProbeTester.java new file mode 100644 index 00000000..7c6d5d5c --- /dev/null +++ b/test/org/freedesktop/gstreamer/BufferProbeTester.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 John Cortell + * + * This file is part of gstreamer-java. + * + * gstreamer-java 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 3 of the License, or + * (at your option) any later version. + * + * gstreamer-java 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 gstreamer-java. If not, see . + */ +package org.freedesktop.gstreamer; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.function.Consumer; + +import org.freedesktop.gstreamer.glib.GError; + +/** + * Utility class for unit testing API that operates on a Buffer. + *

+ * Call {@link BufferTester#test(Consumer)} and pass a callback which will + * perform the test on a Buffer it is supplied. The callback runs on the + * AppSink.NEW_SAMPLE thread. The sample is produced by a simple, ephemeral + * pipeline that is fed by a video test source. + */ +public class BufferProbeTester { + + public static void test(Consumer callback) { + test(callback, "videotestsrc ! videoconvert ! fakesink name=sink"); + } + + public static void test(Consumer callback, String pipelineDescription) { + test(callback, pipelineDescription, 0); + } + + public static void test(Consumer callback, String pipelineDescription, int skipFrames) { + assertNotNull("Pipeline description can not be null", pipelineDescription); + assertFalse("Pipeline description can not be empty", pipelineDescription.isEmpty()); + ArrayList errors = new ArrayList<>(); + Bin bin = Gst.parseBinFromDescription(pipelineDescription, false, errors); + assertNotNull("Unable to create Bin from pipeline description: ", bin); + + Element sink = bin.getElementByName("sink"); + Pad pad = sink.getStaticPad("sink"); + BufferProbe probe = new BufferProbe(callback, skipFrames); + pad.addDataProbe(probe); + + bin.play(); + + // Wait for the sample to arrive and for the client supplied test function to + // complete + try { + synchronized (probe) { + probe.wait(); + } + } catch (InterruptedException e) { + fail("Unexpected interruption waiting for sample"); + } + + bin.stop(); + + // If the test threw an exception on the sample listener thread, throw it here + // (on the main thread) + if (probe.exception != null) { + throw new AssertionError(probe.exception); + } + } + + private static class BufferProbe implements Pad.DATA_PROBE { + + private Consumer callback; + private final int skipFrames; + private Throwable exception; + private int counter = 0; + + BufferProbe(Consumer callback) { + this(callback, 0); + } + + BufferProbe(Consumer callback, int skip) { + this.callback = callback; + skipFrames = skip; + } + + @Override + public PadProbeReturn dataReceived(Pad pad, Buffer buffer) { + if (callback != null) { + if (counter < skipFrames) { + counter++; + return PadProbeReturn.OK; + } + try { + // Run the client's test logic on the buffer (only once) + try { + callback.accept(buffer); + } catch (Throwable exc) { + exception = exc; + } + callback = null; + } finally { + synchronized (this) { + notify(); + } + } + } + return PadProbeReturn.OK; + } + } + +} diff --git a/test/org/freedesktop/gstreamer/SampleTester.java b/test/org/freedesktop/gstreamer/SampleTester.java index 32489da1..2fd8ac32 100644 --- a/test/org/freedesktop/gstreamer/SampleTester.java +++ b/test/org/freedesktop/gstreamer/SampleTester.java @@ -1,6 +1,6 @@ -/* +/* * Copyright (c) 2020 John Cortell - * + * * This file is part of gstreamer-java. * * gstreamer-java is free software: you can redistribute it and/or modify @@ -19,6 +19,7 @@ package org.freedesktop.gstreamer; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -28,9 +29,10 @@ import org.freedesktop.gstreamer.elements.AppSink; import org.freedesktop.gstreamer.glib.GError; + /** * Utility class for unit testing API that operates on a Sample. - * + *

* Call {@link SampleTester#test(Consumer)} and pass a callback which will * perform the test on a Sample it is supplied. The callback runs on the * AppSink.NEW_SAMPLE thread. The sample is produced by a simple, ephemeral @@ -38,21 +40,42 @@ */ public class SampleTester { public static void test(Consumer callback) { - new SampleTester(callback); + test(callback, "videotestsrc ! videoconvert ! appsink name=myappsink"); + } + + public static void test(Consumer callback, String pipelineDescription) { + new SampleTester(callback, pipelineDescription, 0); + } + + public static void test(Consumer callback, String pipelineDescription, int skipFrames) { + new SampleTester(callback, pipelineDescription, skipFrames); } private static class NewSampleListener implements AppSink.NEW_SAMPLE { private Consumer callback; - Throwable exception; + private final int skipFrames; + private Throwable exception; + private int counter = 0; + NewSampleListener(Consumer callback) { + this(callback, 0); + } + + NewSampleListener(Consumer callback, int skip) { this.callback = callback; + skipFrames = skip; } @Override public FlowReturn newSample(AppSink appSink) { if (callback != null) { Sample sample = appSink.pullSample(); + if (counter < skipFrames) { + counter++; + sample.dispose(); + return FlowReturn.OK; + } try { // Run the client's test logic on the sample (only once) try { @@ -74,16 +97,17 @@ public FlowReturn newSample(AppSink appSink) { } } - private SampleTester(Consumer callback) { + private SampleTester(Consumer callback, String pipelineDescription, int skipFrames) { + assertNotNull("Pipeline description can not be null", pipelineDescription); + assertFalse("Pipeline description can not be empty", pipelineDescription.isEmpty()); ArrayList errors = new ArrayList(); - String pipeline_descr = "videotestsrc ! videoconvert ! appsink name=myappsink"; - Bin bin = Gst.parseBinFromDescription(pipeline_descr, false, errors); + Bin bin = Gst.parseBinFromDescription(pipelineDescription, false, errors); assertNotNull("Unable to create Bin from pipeline description: ", bin); AppSink appSink = (AppSink)bin.getElementByName("myappsink"); appSink.set("emit-signals", true); - NewSampleListener sampleListener = new NewSampleListener(callback); + NewSampleListener sampleListener = new NewSampleListener(callback, skipFrames); appSink.connect(sampleListener); bin.play(); @@ -99,7 +123,7 @@ private SampleTester(Consumer callback) { } bin.stop(); - appSink.disconnect(sampleListener); + appSink.disconnect(sampleListener); // If the test threw an exception on the sample listener thread, throw it here // (on the main thread) diff --git a/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java b/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java index c69851d3..54422e14 100644 --- a/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java +++ b/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java @@ -17,12 +17,9 @@ import org.junit.Test; import com.sun.jna.Structure; +import java.util.Arrays; import org.freedesktop.gstreamer.Gst; -/** - * - * @author Neil C Smith - */ public class LowLevelStructureTest { private final static Logger LOG = Logger.getLogger(LowLevelStructureTest.class.getName()); @@ -88,9 +85,14 @@ private void testStruct(Class struct) { } try { - Method getFieldOrder = inst.getClass().getDeclaredMethod("getFieldOrder"); - getFieldOrder.setAccessible(true); - fields = (List) getFieldOrder.invoke(inst); + Structure.FieldOrder fieldOrder = inst.getClass().getAnnotation(Structure.FieldOrder.class); + if (fieldOrder != null) { + fields = Arrays.asList(fieldOrder.value()); + } else { + Method getFieldOrder = inst.getClass().getDeclaredMethod("getFieldOrder"); + getFieldOrder.setAccessible(true); + fields = (List) getFieldOrder.invoke(inst); + } } catch (Exception ex) { LOG.log(Level.SEVERE, "Can't find getFieldOrder() method", ex); assertTrue(false); @@ -110,7 +112,7 @@ private void testFields(Structure inst, List expectedFields) { } private static void initStructList() { - structs = new ArrayList>(); + structs = new ArrayList<>(); structs.add(BaseSinkAPI.GstBaseSinkStruct.class); structs.add(BaseSinkAPI.GstBaseSinkClass.class); @@ -164,6 +166,10 @@ private static void initStructList() { structs.add(GstElementAPI.GstElementClass.class); structs.add(GstEventAPI.EventStruct.class); + structs.add(GstVideoAPI.GstVideoTimeCodeMetaStruct.class); + structs.add(GstVideoAPI.GstVideoTimeCodeStruct.class); + structs.add(GstMetaAPI.GstMetaInfoStruct.class); + structs.add(GstMetaAPI.GstMetaStruct.class); // structs.add(GstInterpolationControlSourceAPI.GstInterpolationControlSourceStruct.class); // structs.add(GstInterpolationControlSourceAPI.GstInterpolationControlSourceClass.class); diff --git a/test/org/freedesktop/gstreamer/util/TestAssumptions.java b/test/org/freedesktop/gstreamer/util/TestAssumptions.java index ce3cf163..20c1fffa 100644 --- a/test/org/freedesktop/gstreamer/util/TestAssumptions.java +++ b/test/org/freedesktop/gstreamer/util/TestAssumptions.java @@ -1,5 +1,6 @@ package org.freedesktop.gstreamer.util; +import org.freedesktop.gstreamer.ElementFactory; import org.freedesktop.gstreamer.Gst; import org.junit.Assume; @@ -16,4 +17,17 @@ public class TestAssumptions { public static void requireGstVersion(int major, int minor) { Assume.assumeTrue(Gst.testVersion(major, minor)); } + + /** + * Assume a GStreamer installation has the required element. + * + * @param elementType element type + */ + public static void requireElement(String elementType) { + ElementFactory factory = null; + try { + factory = ElementFactory.find(elementType); + } catch (Exception ex) {} + Assume.assumeNotNull(factory); + } } diff --git a/test/org/freedesktop/gstreamer/video/VideoCropMetaTest.java b/test/org/freedesktop/gstreamer/video/VideoCropMetaTest.java new file mode 100644 index 00000000..134f0639 --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoCropMetaTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 Neil C Smith + * + * 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.video; + +import org.freedesktop.gstreamer.BufferProbeTester; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.glib.Natives; +import org.freedesktop.gstreamer.lowlevel.GstMetaPtr; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + + + +public class VideoCropMetaTest { + + @BeforeClass + public static void beforeClass() { + Gst.init(Gst.getVersion()); + } + + @AfterClass + public static void afterClass() { + Gst.deinit(); + } + + @Test + public void testIterateWithCrop() { + TestAssumptions.requireGstVersion(1, 14); + TestAssumptions.requireElement("fakevideosink"); + BufferProbeTester.test(buffer -> { + buffer.iterateMeta().forEachRemaining(meta -> { + GstMetaPtr ptr = Natives.getPointer(meta).as(GstMetaPtr.class, GstMetaPtr::new); + System.out.println(ptr.getGType().getTypeName()); + System.out.println(ptr.getAPIGType().getTypeName()); + }); + + }, "videotestsrc ! videocrop top=10 left=10 bottom=50 right=50 ! fakevideosink name=sink"); + } + +} diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeConfigTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeConfigTest.java new file mode 100644 index 00000000..8a9d9f9a --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeConfigTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import org.freedesktop.gstreamer.glib.NativeFlags; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; + +public class VideoTimeCodeConfigTest { + private GstVideoAPI.GstVideoTimeCodeConfigStruct origStruct; + private VideoTimeCodeConfig codeConfig; + + + @Before + public void setUp() { + origStruct = new GstVideoAPI.GstVideoTimeCodeConfigStruct(); + origStruct.fps_d = 25; + origStruct.fps_n = 1; + origStruct.flags = VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME.intValue(); + origStruct.write(); + + codeConfig = new VideoTimeCodeConfig(origStruct); + } + + @Test + public void testGetTimeCodeFlags() { + assertEquals(origStruct.flags, NativeFlags.toInt(codeConfig.getFlags())); + } + + @Test + public void testGetFramerateNumerator() { + assertEquals(origStruct.fps_n, codeConfig.getNumerator()); + } + + @Test + public void testGetFramerateDenominator() { + assertEquals(origStruct.fps_d, codeConfig.getDenominator()); + } +} \ No newline at end of file diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeFlagsTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeFlagsTest.java new file mode 100644 index 00000000..4f3752ad --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeFlagsTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import java.util.Arrays; +import java.util.Collection; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import static org.junit.Assert.assertEquals; + + +@RunWith(Parameterized.class) +public class VideoTimeCodeFlagsTest { + + private final VideoTimeCodeFlags flags; + private final int intValue; + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[][]{ +// {VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_NONE, 0}, + {VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME, 1}, + {VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_INTERLACED, 2} + }); + } + + public VideoTimeCodeFlagsTest(VideoTimeCodeFlags flags, int intValue) { + this.flags = flags; + this.intValue = intValue; + } + + @Test + public void testIntValue() { + assertEquals(intValue,flags.intValue()); + } +} \ No newline at end of file diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeMetaTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeMetaTest.java new file mode 100644 index 00000000..b748b687 --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeMetaTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import org.freedesktop.gstreamer.Buffer; +import org.freedesktop.gstreamer.Gst; +import org.freedesktop.gstreamer.SampleTester; +import org.freedesktop.gstreamer.util.TestAssumptions; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.freedesktop.gstreamer.video.VideoTimeCodeFlags.GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + + +public class VideoTimeCodeMetaTest { + + @BeforeClass + public static void beforeClass() { + Gst.init(Gst.getVersion()); + } + + @AfterClass + public static void afterClass() { + Gst.deinit(); + } + + @Test + public void testVideoWithoutTimeCodeMeta() { + // method containsMetadata is available since 1.14 + TestAssumptions.requireGstVersion(1, 14); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + assertFalse("Default video not contains timecode metadata", buffer.hasMeta(VideoTimeCodeMeta.API)); + }, "videotestsrc do-timestamp=true ! x264enc ! mxfmux ! decodebin ! appsink name=myappsink"); + } + + @Test + public void testVideoTimeCodeMetaPal() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1,10); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API); + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00:00 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(0, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // PAL standard 25/1 + assertEquals(25, timeCodeConfig.getNumerator()); + assertEquals(1, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().isEmpty()); + + }, "videotestsrc do-timestamp=true ! video/x-raw,framerate=25/1 ! timecodestamper ! videoconvert ! appsink name=myappsink"); + } + + @Test + public void testVideoTimeCodeNTSCDrop() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API); + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00;00 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(0, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // NTSC drop standard 30000/1001 + assertEquals(30000, timeCodeConfig.getNumerator()); + assertEquals(1001, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().contains(GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME)); + + }, "videotestsrc ! video/x-raw,framerate=30000/1001 ! timecodestamper drop-frame=true ! videoconvert ! appsink name=myappsink"); + } + + /** + * Handle last frame of first minute + */ + @Test + public void testVideoTimeCodeNTSCDropFrame() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API); + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00;29 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(29, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // NTSC drop standard 30000/1001 + assertEquals(30000, timeCodeConfig.getNumerator()); + assertEquals(1001, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().contains(GST_VIDEO_TIME_CODE_FLAGS_DROP_FRAME)); + + }, "videotestsrc ! video/x-raw,framerate=30000/1001 ! videoconvert ! timecodestamper drop-frame=true ! videoconvert ! appsink name=myappsink", 29); + } + + @Test + public void testVideoTimeCodeNTSCNonDrop() { + // timecodestamper is available since 1.10 + TestAssumptions.requireGstVersion(1, 10); + SampleTester.test(sample -> { + Buffer buffer = sample.getBuffer(); + if (Gst.testVersion(1, 14)) { + assertTrue("Video should contains timecode meta", buffer.hasMeta(VideoTimeCodeMeta.API)); + } + VideoTimeCodeMeta meta = buffer.getMeta(VideoTimeCodeMeta.API); + assertNotNull(meta); + VideoTimeCode timeCode = meta.getTimeCode(); + + //first frame 00:00:00:00 + assertEquals(0, timeCode.getHours()); + assertEquals(0, timeCode.getMinutes()); + assertEquals(0, timeCode.getSeconds()); + assertEquals(0, timeCode.getFrames()); + + VideoTimeCodeConfig timeCodeConfig = timeCode.getConfig(); + // NTSC drop standard 30/1 + assertEquals(30, timeCodeConfig.getNumerator()); + assertEquals(1, timeCodeConfig.getDenominator()); + assertTrue(timeCodeConfig.getFlags().isEmpty()); + + }, "videotestsrc ! video/x-raw,framerate=30/1 ! timecodestamper ! videoconvert ! appsink name=myappsink"); + } +} diff --git a/test/org/freedesktop/gstreamer/video/VideoTimeCodeTest.java b/test/org/freedesktop/gstreamer/video/VideoTimeCodeTest.java new file mode 100644 index 00000000..bca8fa5e --- /dev/null +++ b/test/org/freedesktop/gstreamer/video/VideoTimeCodeTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2020 Neil C Smith + * Copyright (c) 2020 Petr Lastovka + * + * 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.video; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.freedesktop.gstreamer.lowlevel.GstVideoAPI; + + +public class VideoTimeCodeTest { + + + private GstVideoAPI.GstVideoTimeCodeStruct timeCodeStruct; + private GstVideoAPI.GstVideoTimeCodeConfigStruct.ByValue configStruct; + + private VideoTimeCode timeCode; + + @Before + public void setUp() { + timeCodeStruct = new GstVideoAPI.GstVideoTimeCodeStruct(); + configStruct = new GstVideoAPI.GstVideoTimeCodeConfigStruct.ByValue(); + + // 01:02:03:04 + timeCodeStruct.hours = 1; + timeCodeStruct.minutes = 2; + timeCodeStruct.seconds = 3; + timeCodeStruct.frames = 4; + + timeCodeStruct.field_count = 55; + + // config + timeCodeStruct.config = configStruct; + + timeCodeStruct.write(); + timeCode = new VideoTimeCode(timeCodeStruct); + + + } + + @Test + public void testGetTCConfig() { + assertNotNull(timeCode.getConfig()); + } + + @Test + public void testGetHours() { + assertEquals(1,timeCode.getHours()); + } + + @Test + public void testGetMinutes() { + assertEquals(2,timeCode.getMinutes()); + } + + @Test + public void testGetSeconds() { + assertEquals(3,timeCode.getSeconds()); + } + + @Test + public void testGetFrames() { + assertEquals(4,timeCode.getFrames()); + } +}