From e45c0e21e68357c105fb74e135d178bb0f25fbeb Mon Sep 17 00:00:00 2001 From: Neil C Smith Date: Fri, 25 Jan 2019 11:01:59 +0000 Subject: [PATCH] Create Bindings to enable WebRTC (#135) * [webrtc] Adding GstPromise and GstWebRTCSessionDescription * [webrtc] [NS] Debugging * Get WebRTC related elements to work without throwing null exceptions * add hasCurrentCaps to pad * Add comments/documentation * create WebRTCBin * add isEqual to Structure * add tests for Promise * handle WebRTCSessionDescription memory ownership properly * self review * use pointer method for Promise.getReply() * Add version testing for GStreamer 1.14 to PromiseTest and webrtc related low level structure tests. * Remove GTYPE field from WebRTCSessionDescription which causes eager loading of native library. --- src/org/freedesktop/gstreamer/Gst.java | 8 +- src/org/freedesktop/gstreamer/Pad.java | 12 +- src/org/freedesktop/gstreamer/Promise.java | 123 ++++++++ .../freedesktop/gstreamer/PromiseResult.java | 36 +++ src/org/freedesktop/gstreamer/SDPMessage.java | 93 ++++++ src/org/freedesktop/gstreamer/SDPResult.java | 47 +++ src/org/freedesktop/gstreamer/Structure.java | 9 + .../gstreamer/WebRTCPeerConnectionState.java | 40 +++ .../freedesktop/gstreamer/WebRTCSDPType.java | 46 +++ .../gstreamer/WebRTCSessionDescription.java | 68 +++++ .../gstreamer/elements/WebRTCBin.java | 271 ++++++++++++++++++ .../gstreamer/lowlevel/GstPadAPI.java | 2 + .../gstreamer/lowlevel/GstPromiseAPI.java | 74 +++++ .../gstreamer/lowlevel/GstSDPMessageAPI.java | 132 +++++++++ .../gstreamer/lowlevel/GstStructureAPI.java | 1 + .../GstWebRTCSessionDescriptionAPI.java | 64 +++++ .../freedesktop/gstreamer/PromiseTest.java | 99 +++++++ .../lowlevel/LowLevelStructureTest.java | 7 +- 18 files changed, 1129 insertions(+), 3 deletions(-) create mode 100644 src/org/freedesktop/gstreamer/Promise.java create mode 100644 src/org/freedesktop/gstreamer/PromiseResult.java create mode 100644 src/org/freedesktop/gstreamer/SDPMessage.java create mode 100644 src/org/freedesktop/gstreamer/SDPResult.java create mode 100644 src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java create mode 100644 src/org/freedesktop/gstreamer/WebRTCSDPType.java create mode 100644 src/org/freedesktop/gstreamer/WebRTCSessionDescription.java create mode 100644 src/org/freedesktop/gstreamer/elements/WebRTCBin.java create mode 100644 src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java create mode 100644 src/org/freedesktop/gstreamer/lowlevel/GstSDPMessageAPI.java create mode 100644 src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java create mode 100644 test/org/freedesktop/gstreamer/PromiseTest.java diff --git a/src/org/freedesktop/gstreamer/Gst.java b/src/org/freedesktop/gstreamer/Gst.java index 7036af39..f55b0f14 100644 --- a/src/org/freedesktop/gstreamer/Gst.java +++ b/src/org/freedesktop/gstreamer/Gst.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2018 Antonio Morales * Copyright (c) 2018 Neil C Smith * Copyright (c) 2007 Wayne Meissner * @@ -44,6 +45,7 @@ import org.freedesktop.gstreamer.elements.DecodeBin; import org.freedesktop.gstreamer.elements.PlayBin; import org.freedesktop.gstreamer.elements.URIDecodeBin; +import org.freedesktop.gstreamer.elements.WebRTCBin; import org.freedesktop.gstreamer.glib.GDate; import org.freedesktop.gstreamer.glib.GInetAddress; import org.freedesktop.gstreamer.glib.GSocketAddress; @@ -619,9 +621,12 @@ private static synchronized void loadAllClasses() { PadTemplate.class, Plugin.class, PluginFeature.class, + Promise.class, Query.class, Registry.class, + SDPMessage.class, Sample.class, + WebRTCSessionDescription.class, // ----------- Elements ------------- AppSink.class, AppSrc.class, @@ -632,6 +637,7 @@ private static synchronized void loadAllClasses() { DecodeBin.class, Pipeline.class, PlayBin.class, - URIDecodeBin.class + URIDecodeBin.class, + WebRTCBin.class ); } diff --git a/src/org/freedesktop/gstreamer/Pad.java b/src/org/freedesktop/gstreamer/Pad.java index 6a5005e6..378c1d34 100644 --- a/src/org/freedesktop/gstreamer/Pad.java +++ b/src/org/freedesktop/gstreamer/Pad.java @@ -1,10 +1,11 @@ /* + * 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 @@ -689,4 +690,13 @@ 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); + } } diff --git a/src/org/freedesktop/gstreamer/Promise.java b/src/org/freedesktop/gstreamer/Promise.java new file mode 100644 index 00000000..c386f05a --- /dev/null +++ b/src/org/freedesktop/gstreamer/Promise.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2018 Vinicius Tona + * Copyright (c) 2018 Antonio Morales + * + * 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 static org.freedesktop.gstreamer.lowlevel.GstPromiseAPI.GSTPROMISE_API; + +import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; + +import com.sun.jna.Pointer; + +public class Promise extends MiniObject { + public static final String GTYPE_NAME = "GstPromise"; + + public static interface PROMISE_CHANGE { + /** + * Called whenever the state of the promise is changed from PENDING to any other {@link PromiseResult} + * + * @param promise the original promise that had the callback attached to + */ + public void onChange(Promise promise); + } + + /** + * Creates a new instance of Promise. This constructor is used internally. + * + * @param init internal initialization data. + */ + public Promise(final Initializer init) { + super(init); + } + + /** + * Creates a new instance of promise + */ + public Promise() { + this(initializer(GSTPROMISE_API.ptr_gst_promise_new())); + } + + /** + * Creates a new instance of promise with a callback attached. + * + * @param listerner Listener to be called whenever the state of a {@link Promise} is changed + */ + public Promise(final PROMISE_CHANGE listener) { + this(new Initializer(GSTPROMISE_API.ptr_gst_promise_new_with_change_func(new GstCallback() { + @SuppressWarnings("unused") + public void callback(Promise promise, Pointer userData) { + listener.onChange(promise); + } + }), false, false)); + } + + protected static Initializer initializer(final Pointer ptr) { + return new Initializer(ptr, false, true); + } + + /** + * Wait for the promise to move out of the PENDING {@link PromiseResult} state. + * If the promise is not in PENDING then it will immediately return. + * + * @return the {@link PromiseResult} of the promise. + */ + public PromiseResult waitResult() { + return GSTPROMISE_API.gst_promise_wait(this); + } + + /** + * Set a reply on the promise. + * + * Will wake up any waiters on the promise with the REPLIED {@link PromiseResult} state. + * If the promise has already been interrupted than the replied will not be visible to any waiters + * + * @param structure the {@link Structure} to reply the promise with + */ + public void reply(final Structure structure) { + GSTPROMISE_API.gst_promise_reply(this, structure); + } + + /** + * Interrupt waiting for the result of the prommise. + * + * Any waiters on the promise will receive the INTERRUPTED {@link PromiseResult} state. + */ + public void interrupt() { + GSTPROMISE_API.gst_promise_interrupt(this); + } + + /** + * Expire a promise. + * + * Any waiters on the promise will recieved the EXPIRED {@link PromiseResult} state. + */ + public void expire() { + GSTPROMISE_API.gst_promise_expire(this); + } + + /** + * Retrieve the reply set on the promise. + * + * The state of the promise must be in the REPLIED {@link PromiseResult} state. + * The return structure is owned by the promise and thus cannot be modified. + * + * @return the {@link Structure} set on the promise reply. + */ + public Structure getReply() { + return Structure.objectFor(GSTPROMISE_API.ptr_gst_promise_get_reply(this), false, false); + } +} diff --git a/src/org/freedesktop/gstreamer/PromiseResult.java b/src/org/freedesktop/gstreamer/PromiseResult.java new file mode 100644 index 00000000..7b9741ef --- /dev/null +++ b/src/org/freedesktop/gstreamer/PromiseResult.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.lowlevel.annotations.DefaultEnumValue; + +/** + * The result of a {@link Promise} + * Available since GStreamer 1.14 + */ +public enum PromiseResult { + /** The initial state of a promise */ + PENDING, + /** The promise was interrupted */ + INTERRUPTED, + /** The promise has been resolved and it has a value */ + REPLIED, + /** The promise is expired and won't return a result */ + EXPIRED, + /** Unknown result */ + @DefaultEnumValue UNKNOWN; +} diff --git a/src/org/freedesktop/gstreamer/SDPMessage.java b/src/org/freedesktop/gstreamer/SDPMessage.java new file mode 100644 index 00000000..38906f08 --- /dev/null +++ b/src/org/freedesktop/gstreamer/SDPMessage.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.nio.charset.StandardCharsets; + +import static org.freedesktop.gstreamer.lowlevel.GstSDPMessageAPI.GSTSDPMESSAGE_API; + +import org.freedesktop.gstreamer.lowlevel.NativeObject; + +import com.sun.jna.Pointer; + +public class SDPMessage extends NativeObject { + public static final String GTYPE_NAME = "GstSDPMessage"; + + /** + * Internally used constructor. Do not use. + * + * @param init internal initialization data + */ + public SDPMessage(Initializer init) { + super(init); + } + + /** + * Creates a new instance of SDPMessage + */ + public SDPMessage() { + this(initializer()); + } + + /** + * A SDP formatted string representation of SDPMessage. + * + * Used for offer/answer exchanges for real time communicationse + * + * @return the SDP string representation of SDPMessage. + */ + public String toString() { + return GSTSDPMESSAGE_API.gst_sdp_message_as_text(this); + } + + /** + * Takes a SDP string and parses it and fills in all fields for SDPMessage. + * + * Look at https://tools.ietf.org/html/rfc4566 for more information on SDP + * + * @param sdpString the sdp string + */ + public void parseBuffer(String sdpString) { + byte[] data = sdpString.getBytes(StandardCharsets.US_ASCII); + int length = sdpString.length(); + GSTSDPMESSAGE_API.gst_sdp_message_parse_buffer(data, length, this); + } + + /** + * Creates a copy of this SDPMessage. + * + * @return a copy of SDPMessage. + */ + public SDPMessage copy(boolean shouldInvalidateOriginal) { + Pointer[] ptr = new Pointer[1]; + GSTSDPMESSAGE_API.gst_sdp_message_copy(this, ptr); + if (shouldInvalidateOriginal) { + this.invalidate(); + } + return new SDPMessage(initializer(ptr[0])); + } + + private static Initializer initializer() { + Pointer[] ptr = new Pointer[1]; + GSTSDPMESSAGE_API.gst_sdp_message_new(ptr); + return initializer(ptr[0]); + } + + protected void disposeNativeHandle(Pointer ptr) { + GSTSDPMESSAGE_API.gst_sdp_message_free(ptr); + } +} diff --git a/src/org/freedesktop/gstreamer/SDPResult.java b/src/org/freedesktop/gstreamer/SDPResult.java new file mode 100644 index 00000000..8ca26724 --- /dev/null +++ b/src/org/freedesktop/gstreamer/SDPResult.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.lowlevel.IntegerEnum; +import org.freedesktop.gstreamer.lowlevel.annotations.DefaultEnumValue; + +/** + * The possible results for {@link SDPMessage} functions + */ +public enum SDPResult implements IntegerEnum { + /** A successful return value*/ + OK(0), + /** A function to SDPMessage was given invalid paramters */ + EINVAL(-1), + /** An unknown result */ + @DefaultEnumValue + __UNKNWON_NATIVE_VALUE(~0); + + SDPResult(int value) { + this.value = value; + } + + /** + * Gets the integer value of the enum + * @return the integer value for this enum. + */ + public int intValue() { + return value; + } + + private int value; +} diff --git a/src/org/freedesktop/gstreamer/Structure.java b/src/org/freedesktop/gstreamer/Structure.java index 2273e2a8..988c0a3c 100644 --- a/src/org/freedesktop/gstreamer/Structure.java +++ b/src/org/freedesktop/gstreamer/Structure.java @@ -623,6 +623,15 @@ public String getName(int i) { return GSTSTRUCTURE_API.gst_structure_nth_field_name(this, i); } + /** + * Checks that two structures are equal + * @param structure the stucture to check if it's equal to this structure + * @return true if both structures are equal + */ + public boolean isEqual(Structure structure) { + return GSTSTRUCTURE_API.gst_structure_is_equal(this, structure); + } + @Override public String toString() { return GSTSTRUCTURE_API.gst_structure_to_string(this); diff --git a/src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java b/src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java new file mode 100644 index 00000000..b41b93cf --- /dev/null +++ b/src/org/freedesktop/gstreamer/WebRTCPeerConnectionState.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.lowlevel.annotations.DefaultEnumValue; + +/** + * The state of a WebRTC peer connection + * Available since GStreamer 1.14 + */ +public enum WebRTCPeerConnectionState { + /** New WebRTC connection */ + NEW, + /** A WebRTC connection is being made */ + CONNECTING, + /** A WebRTC connection has been made */ + CONNECTED, + /** A WebRTC connection has been disconnected */ + DISCONNECTED, + /** Attempt to make a WebRTC connection failed */ + FAILED, + /** A WebRTC connection has been closed */ + CLOSED, + /** Unknown result */ + @DefaultEnumValue UNKNOWN; +} diff --git a/src/org/freedesktop/gstreamer/WebRTCSDPType.java b/src/org/freedesktop/gstreamer/WebRTCSDPType.java new file mode 100644 index 00000000..f363360c --- /dev/null +++ b/src/org/freedesktop/gstreamer/WebRTCSDPType.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.lowlevel.IntegerEnum; + +/** + * The type of a {@link WebRTCSessionDescription} + * + * @see https://w3c.github.io/webrtc-pc/#rtcsdptype + * Available since GStreamer 1.12 + */ +public enum WebRTCSDPType implements IntegerEnum { + OFFER(1), + PRANSWER(2), + ANSWER(3), + ROLLBACK(4); + + WebRTCSDPType(int value) { + this.value = value; + } + + /** + * Gets the integer value of the enum + * @return the integer value for this enum. + */ + public int intValue() { + return value; + } + private final int value; +} diff --git a/src/org/freedesktop/gstreamer/WebRTCSessionDescription.java b/src/org/freedesktop/gstreamer/WebRTCSessionDescription.java new file mode 100644 index 00000000..819a5a80 --- /dev/null +++ b/src/org/freedesktop/gstreamer/WebRTCSessionDescription.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018 Vinicius Tona + * Copyright (c) 2018 Antonio Morales + * + * 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 static org.freedesktop.gstreamer.lowlevel.GstWebRTCSessionDescriptionAPI.GSTWEBRTCSESSIONDESCRIPTION_API; + +import org.freedesktop.gstreamer.lowlevel.GstWebRTCSessionDescriptionAPI; +import org.freedesktop.gstreamer.lowlevel.NativeObject; + +import com.sun.jna.Pointer; + +public class WebRTCSessionDescription extends NativeObject { + public static final String GTYPE_NAME = "GstWebRTCSessionDescription"; + + protected GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct sessionDescriptionStruct; + + /** + * Internally used constructor. Do not use. + * + * @param init internal initialization data. + */ + public WebRTCSessionDescription(Initializer init) { + super(init); + sessionDescriptionStruct = new GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct(handle()); + } + + /** + * Creates a new instance of WebRTCSessionDescription. + * + * @param type The {@link WebRTCSDPType} type of the session description + * @param sdpMessage The {@link SDPMessage} of the session description + */ + public WebRTCSessionDescription(WebRTCSDPType type, SDPMessage sdpMessage) { + this(initializer(GSTWEBRTCSESSIONDESCRIPTION_API.ptr_gst_webrtc_session_description_new(type, sdpMessage))); + } + + /** + * Gets the SDPMessage from the WebRTCSessionDescription. + * + * @return the {@link SDPMessage} for the WebRTCSessionDescription + */ + public SDPMessage getSDPMessage() { + SDPMessage originalSDP = (SDPMessage)sessionDescriptionStruct.readField("sdp"); + // making a copy of the SDPMessage since the original SDPMessage in the struct belongs to WebRTCSessionDescription. + // Once WebRTCSessionDescription is disposed it would also dispose of SDPMessage leading to any objects with a reference + // to the original SDPMessage to be invalid and potentially lead to runtime errors. + return originalSDP.copy(true); + } + + protected void disposeNativeHandle(Pointer ptr) { + GSTWEBRTCSESSIONDESCRIPTION_API.gst_webrtc_session_description_free(ptr); + } +} diff --git a/src/org/freedesktop/gstreamer/elements/WebRTCBin.java b/src/org/freedesktop/gstreamer/elements/WebRTCBin.java new file mode 100644 index 00000000..589d8667 --- /dev/null +++ b/src/org/freedesktop/gstreamer/elements/WebRTCBin.java @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.elements; + +import org.freedesktop.gstreamer.Bin; +import org.freedesktop.gstreamer.Element; +import org.freedesktop.gstreamer.Promise; +import org.freedesktop.gstreamer.State; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.GstObject; +import org.freedesktop.gstreamer.WebRTCSessionDescription; +import org.freedesktop.gstreamer.WebRTCPeerConnectionState; + +import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; + +/** + * WebRTCBin is an abstraction over gstreamers webrtcbin element + * It is structured to mimic the RTCPeerConnection API that is available in web browsers + * @see https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection + * + * @see https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/blob/master/ext/webrtc/gstwebrtcbin.c + * available since Gstreamer 1.14 + */ +public class WebRTCBin extends Bin { + public static final String GST_NAME = "webrtcbin"; + public static final String GTYPE_NAME = "GstWebrtcBin"; + + public WebRTCBin(Initializer init) { + super(init); + } + + public WebRTCBin(String name) { + super(makeRawElement(GST_NAME, name)); + } + + /** + * Signal emitted when this {@link WebRTCBin} is ready to do negotiation to setup a WebRTC connection + * Good starting point to have the WebRTCBin send an offer to potential clients + */ + public static interface ON_NEGOTIATION_NEEDED { + /** + * @param elem the original webrtc bin that had the callback attached to + */ + public void onNegotiationNeeded(Element elem); + } + + /* + * Signal emmited when this {@link WebRTCBin} gets a new ice candidate + */ + public static interface ON_ICE_CANDIDATE { + /** + * @param sdpMLineIndex the zero-based index of the m-line attribute within the SDP to which the candidate should be associated to + * @param candidate the ICE candidate + */ + public void onIceCandidate(int sdpMLineIndex, String candidate); + } + + /** + * Signal emitted when this {@link WebRTCBin} creates an offer + */ + public static interface CREATE_OFFER { + /** + * @param a @WebRTCSessionDescription of the offer + */ + public void onOfferCreated(WebRTCSessionDescription offer); + } + + /** + * Signal emitted when this {@link WebRTCBin} creates an answer + */ + public static interface CREATE_ANSWER { + /** + * @param a @WebRTCSessionDescription of the answer + */ + public void onAnswerCreated(WebRTCSessionDescription answer); + } + + /** + * Adds a listener for the on-negotiation-needed signal. + * @param listener + */ + public void connect(final ON_NEGOTIATION_NEEDED listener) { + connect(ON_NEGOTIATION_NEEDED.class, listener, new GstCallback() { + @SuppressWarnings("unused") + public void callback(Element elem) { + listener.onNegotiationNeeded(elem); + } + }); + } + + /** + * Adds a listener for the on-ice-candidate signal. + * @param listener + */ + public void connect(final ON_ICE_CANDIDATE listener) { + connect(ON_ICE_CANDIDATE.class, listener, new GstCallback() { + @SuppressWarnings("unused") + public void callback(Element elem, int sdpMLineIndex, String candidate) { + listener.onIceCandidate(sdpMLineIndex, candidate); + } + }); + } + + /** + * Create an offer that can be sent to other clients to setup a WebRTC connection. + *

+ * In most cases {@link #setLocalDescription} should be called after an answer is created + * + * @param listener callback that is called when a offer is created + */ + public void createOffer(final CREATE_OFFER listener) { + Promise promise = new Promise(new Promise.PROMISE_CHANGE() { + @SuppressWarnings("unused") + public void onChange(Promise promise) { + Structure reply = promise.getReply(); + WebRTCSessionDescription description = (WebRTCSessionDescription)reply.getValue("offer"); + listener.onOfferCreated(description); + promise.dispose(); + } + }); + emit("create-offer", null, promise); + } + + /** + * Create an answer in response to an offer received in order for the WebRTC signaling protocol to start. + *

+ * Should be called after {@link #setRemoteDescription} is called + *

+ * In most cases {@link #setLocalDescription} should be called after an answer is created + * + * @param listener callback that is called when an answer is created. + */ + public void createAnswer(final CREATE_ANSWER listener) { + Promise promise = new Promise(new Promise.PROMISE_CHANGE() { + @SuppressWarnings("unused") + public void onChange(Promise promise) { + Structure reply = promise.getReply(); + WebRTCSessionDescription description = (WebRTCSessionDescription)reply.getValue("answer"); + listener.onAnswerCreated(description); + promise.dispose(); + } + }); + emit("create-answer", null, promise); + } + + /** + * Adds a remote ice candidate to the bin + * + * @param sdpMLineIndex the zero-based index of the m-line attribute within the SDP to which the candidate should be associated to + * @param candidate the ICE candidate + */ + public void addIceCandidate(int sdpMLineIndex, String candidate) { + emit("add-ice-candidate", sdpMLineIndex, candidate); + } + + /** + * Sets the local description for the WebRTC connection. + * Should be called after {@link #createOffer} or {@link #createAnser} is called. + * + * @param description the {@link WebRTCSessionDescription} to set for the local description + */ + public void setLocalDescription(WebRTCSessionDescription description) { + Promise promise = new Promise(); + // the raw WebRTCBin element gets ownership of the description so it must be disown in order to prevent it from being deallocated + description.disown(); + emit("set-local-description", description, promise); + promise.interrupt(); + promise.dispose(); + } + + /** + * Sets the remote description for the WebRTC connection. + * Shoud be called after receiving an offer or answer from other clients. + * + * @param description the {@link WebRTCSessionDescription} to set for the remote description + */ + public void setRemoteDescription(WebRTCSessionDescription description) { + Promise promise = new Promise(); + // the raw WebRTCBin element gets ownership of the description so it must be disown in order to prevent it from being deallocated + description.disown(); + emit("set-remote-description", description, promise); + promise.interrupt(); + promise.dispose(); + } + + /** + * Sets the stun-server property for this {@link WebRTCBin} which is use to gather ICE data + * + * @param server STUN server url + */ + public void setStunServer(String server) { + set("stun-server", server); + } + + /** + * Retrieves the STUN server that is used. + * + * @return the url for the STUN server + */ + public String getStunServer() { + return (String)get("stun-server"); + } + + /** + * Sets the turn-server property for this {@link WebRTCBin} which is used whenever a direct peer-to-peer connection can be established + * + * @param server TURN server url + */ + public void setTurnServer(String server) { + set("turn-server", server); + } + + /** + * Retrieves the TURN server that is used. + * + * @return the url for the TURN server + */ + public String getTurnServer() { + return (String)get("turn-server"); + } + + /** + * Retrieve the connection state this {@link WebRTCBin} is currently in + * + * @return a {@link WebRTCPeerConnectionState} describing the connection state + */ + public WebRTCPeerConnectionState getConnectionState() { + return (WebRTCPeerConnectionState)get("connection-state"); + } + + /** + * Retrieve the local description for this {@link WebRTCBin} + * + * @return the local {@link WebRTCSessionDescription} + */ + public WebRTCSessionDescription getLocalDescription() { + WebRTCSessionDescription description = (WebRTCSessionDescription)get("local-description"); + description.disown(); + return description; + } + + /** + * Retrieve the remote description for this {@link WebRTCBin} + * + * @return the remote {@link WebRTCSessionDescription} + */ + public WebRTCSessionDescription getRemoteDescription() { + WebRTCSessionDescription description = (WebRTCSessionDescription)get("remote-description"); + description.disown(); + return description; + } +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java index 2f8768bb..3186ad91 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstPadAPI.java @@ -1,4 +1,5 @@ /* + * Copyright (c) 2018 Antonio Morales * Copyright (c) 2014 Tom Greenwood * Copyright (c) 2009 Levente Farkas * Copyright (c) 2007, 2008 Wayne Meissner @@ -99,6 +100,7 @@ public interface GstPadAPI extends com.sun.jna.Library { boolean gst_pad_activate_push(Pad pad, boolean active); boolean gst_pad_is_blocked(Pad pad); boolean gst_pad_is_blocking(Pad pad); + boolean gst_pad_has_current_caps(Pad pad); /* get_pad_template returns a non-refcounted PadTemplate */ PadTemplate gst_pad_get_pad_template(Pad pad); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java new file mode 100644 index 00000000..0cd04d99 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstPromiseAPI.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018 Vinicius Tona + * Copyright (c) 2018 Antonio Morales + * + * 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 java.util.Arrays; +import java.util.List; + +import org.freedesktop.gstreamer.Promise; +import org.freedesktop.gstreamer.PromiseResult; +import org.freedesktop.gstreamer.Structure; +import org.freedesktop.gstreamer.lowlevel.GstAPI.GstCallback; +import org.freedesktop.gstreamer.lowlevel.GstMiniObjectAPI.MiniObjectStruct; +import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; + +import com.sun.jna.Pointer; + +/** + * GstPromise methods and structures + * + * @see https://github.com/GStreamer/gstreamer/blob/master/gst/gstpromise.h + * available since GStreamer 1.14 + */ +public interface GstPromiseAPI extends com.sun.jna.Library { + GstPromiseAPI GSTPROMISE_API = GstNative.load(GstPromiseAPI.class); + + public static final class PromiseStruct extends com.sun.jna.Structure { + public volatile MiniObjectStruct parent; + + public PromiseStruct() { + } + + public PromiseStruct(Pointer ptr) { + useMemory(ptr); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "parent" }); + } + } + + GType gst_promise_get_type(); + GType gst_static_promise_get_type(); + + @CallerOwnsReturn Promise gst_promise_new(); + @CallerOwnsReturn Pointer ptr_gst_promise_new(); + + @CallerOwnsReturn Promise gst_promise_new_with_change_func(GstCallback callback); + @CallerOwnsReturn Pointer ptr_gst_promise_new_with_change_func(GstCallback callback); + + PromiseResult gst_promise_wait(Promise promise); + + void gst_promise_reply(Promise promise, Structure s); + void gst_promise_interrupt(Promise promise); + void gst_promise_expire(Promise promise); + + @CallerOwnsReturn Structure gst_promise_get_reply(Promise promise); + @CallerOwnsReturn Pointer ptr_gst_promise_get_reply(Promise promise); +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstSDPMessageAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstSDPMessageAPI.java new file mode 100644 index 00000000..374e6775 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstSDPMessageAPI.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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 java.util.Arrays; +import java.util.List; + +import org.freedesktop.gstreamer.SDPResult; +import org.freedesktop.gstreamer.SDPMessage; +import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValueArray; + +import com.sun.jna.Pointer; + +/** + * GstSDPMessage methods and structures + * + * @see https://github.com/GStreamer/gst-plugins-base/blob/master/gst-libs/gst/sdp/gstsdpmessage.h + */ +public interface GstSDPMessageAPI extends com.sun.jna.Library { + GstSDPMessageAPI GSTSDPMESSAGE_API = GstNative.load("gstsdp", GstSDPMessageAPI.class); + + public static final class SDPMessageStruct extends com.sun.jna.Structure { + public volatile String version; + public volatile SDPOriginStruct origin; + public volatile String session_name; + public volatile String information; + public volatile String uri; + public volatile GValueArray emails; + public volatile GValueArray phones; + public volatile SDPConnectionStruct connection; + public volatile GValueArray bandwidths; + public volatile GValueArray times; + public volatile GValueArray zones; + public volatile SDPKeyStruct key; + public volatile GValueArray attributes; + public volatile GValueArray medias; + + public SDPMessageStruct(final Pointer ptr) { + useMemory(ptr); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { + "version", "origin", "session_name", "information", "uri", + "emails", "phones", "connection", "bandwidths", "times", + "zones", "key", "attributes", "medias" + }); + } + } + + public static final class SDPOriginStruct extends com.sun.jna.Structure { + public volatile String username; + public volatile String sess_id; + public volatile String sess_version; + public volatile String nettype; + public volatile String addrtype; + public volatile String addr; + + public SDPOriginStruct(final Pointer ptr) { + useMemory(ptr); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { + "username", "sess_id", "sess_version", "nettype", "addrtype", "addr" + }); + } + } + + public static final class SDPKeyStruct extends com.sun.jna.Structure { + public volatile String type; + public volatile String data; + + public SDPKeyStruct(final Pointer ptr) { + useMemory(ptr); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "type", "data" }); + } + } + + public static final class SDPConnectionStruct extends com.sun.jna.Structure { + public volatile String nettype; + public volatile String addrtype; + public volatile String address; + public volatile int ttl; + public volatile int addr_number; + + public SDPConnectionStruct(final Pointer ptr) { + useMemory(ptr); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { + "nettype", "addrtype", "address", "ttl", "addr_number" + }); + } + } + + SDPResult gst_sdp_connection_set(SDPConnectionStruct conn, String nettype, String address, int ttl, int addr_number); + SDPResult gst_sdp_connection_clear(SDPConnectionStruct conn); + + SDPResult gst_sdp_message_free(Pointer msg); + SDPResult ptr_gst_sdp_message_free(Pointer msg); + + SDPResult gst_sdp_message_new(Pointer[] msg); + + SDPResult gst_sdp_message_parse_buffer(byte[] data, int size, Pointer[] msg); + SDPResult gst_sdp_message_parse_buffer(byte[] data, int size, SDPMessage msg); + SDPResult gst_sdp_message_copy(SDPMessage msg, Pointer[] copy); + + String gst_sdp_message_as_text(SDPMessage msg); +} diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java index 8827954f..d5721789 100644 --- a/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java +++ b/src/org/freedesktop/gstreamer/lowlevel/GstStructureAPI.java @@ -57,6 +57,7 @@ public interface GstStructureAPI extends com.sun.jna.Library { String gst_structure_nth_field_name(Structure structure, int index); boolean gst_structure_has_field(Structure structure, String fieldname); boolean gst_structure_has_field_typed(Structure structure, String fieldname, GType type); + boolean gst_structure_is_equal(Structure structure1, Structure structure2); /* utility functions */ boolean gst_structure_get_boolean(Structure structure, String fieldname, int[] value); diff --git a/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java b/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java new file mode 100644 index 00000000..fa926345 --- /dev/null +++ b/src/org/freedesktop/gstreamer/lowlevel/GstWebRTCSessionDescriptionAPI.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2018 Vinicius Tona + * Copyright (c) 2018 Antonio Morales + * + * 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 java.util.Arrays; +import java.util.List; + +import org.freedesktop.gstreamer.WebRTCSessionDescription; +import org.freedesktop.gstreamer.WebRTCSDPType; +import org.freedesktop.gstreamer.SDPMessage; +import org.freedesktop.gstreamer.lowlevel.annotations.CallerOwnsReturn; +import org.freedesktop.gstreamer.lowlevel.GValueAPI.GValueArray; + +import com.sun.jna.Pointer; + +/** + * GstWebRTCSessionDescription methods and structures + * + * @see https://github.com/GStreamer/gst-plugins-bad/blob/master/gst-libs/gst/webrtc/rtcsessiondescription.h + * Available since GStreamer 1.14 + */ +public interface GstWebRTCSessionDescriptionAPI extends com.sun.jna.Library { + GstWebRTCSessionDescriptionAPI GSTWEBRTCSESSIONDESCRIPTION_API = + GstNative.load("gstwebrtc", GstWebRTCSessionDescriptionAPI.class); + + public static final class WebRTCSessionDescriptionStruct extends com.sun.jna.Structure { + public volatile WebRTCSDPType type; + public volatile SDPMessage sdp; + + public WebRTCSessionDescriptionStruct(final Pointer ptr) { + useMemory(ptr); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "type", "sdp" }); + } + } + + GType gst_webrtc_session_description_get_type(); + + @CallerOwnsReturn WebRTCSessionDescription gst_webrtc_session_description_new(WebRTCSDPType type, SDPMessage sdp); + @CallerOwnsReturn Pointer ptr_gst_webrtc_session_description_new(WebRTCSDPType type, SDPMessage sdp); + + @CallerOwnsReturn WebRTCSessionDescription gst_webrtc_session_description_copy(WebRTCSessionDescription src); + @CallerOwnsReturn Pointer ptr_gst_webrtc_session_description_copy(WebRTCSessionDescription src); + + void gst_webrtc_session_description_free(Pointer desc); +} diff --git a/test/org/freedesktop/gstreamer/PromiseTest.java b/test/org/freedesktop/gstreamer/PromiseTest.java new file mode 100644 index 00000000..516ad6ca --- /dev/null +++ b/test/org/freedesktop/gstreamer/PromiseTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Antonio Morales + * + * 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.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.freedesktop.gstreamer.lowlevel.GType; +import org.junit.BeforeClass; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.Test; + +public class PromiseTest { + + public PromiseTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + Gst.init("PromiseTest", new String[] {}); + } + + @AfterClass + public static void tearDownClass() throws Exception { + Gst.deinit(); + } + + @Test + public void testReply() { + if (Gst.getVersion().getMinor() < 14) { + return; + } + Promise promise = new Promise(); + + promise.reply(null); + + PromiseResult promiseStatus = promise.waitResult(); + + assertEquals("promise reply state not correct", promiseStatus, PromiseResult.REPLIED); + } + + @Test + public void testInterrupt() { + if (Gst.getVersion().getMinor() < 14) { + return; + } + Promise promise = new Promise(); + promise.interrupt(); + + PromiseResult promiseStatus = promise.waitResult(); + + assertEquals("promise reply state not correct", promiseStatus, PromiseResult.INTERRUPTED); + } + + @Test + public void testExpire() { + if (Gst.getVersion().getMinor() < 14) { + return; + } + Promise promise = new Promise(); + promise.expire(); + + PromiseResult promiseStatus = promise.waitResult(); + + assertEquals("promise reply state not correct", promiseStatus, PromiseResult.EXPIRED); + } + + @Test + public void testReplyData() { + if (Gst.getVersion().getMinor() < 14) { + return; + } + Promise promise = new Promise(); + Structure data = new Structure("data", "test", GType.UINT, 1); + + promise.reply(data); + assertEquals("promise state not in replied", promise.waitResult(), PromiseResult.REPLIED); + + Structure result = promise.getReply(); + assertTrue("result of promise does not match reply", result.isEqual(data)); + } +} diff --git a/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java b/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java index 80cada23..89fe4592 100644 --- a/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java +++ b/test/org/freedesktop/gstreamer/lowlevel/LowLevelStructureTest.java @@ -17,6 +17,7 @@ import org.junit.Test; import com.sun.jna.Structure; +import org.freedesktop.gstreamer.Gst; /** * @@ -179,7 +180,11 @@ private static void initStructList() { structs.add(GstQueryAPI.QueryStruct.class); - + if (Gst.getVersion().getMinor() >= 14) { + structs.add(GstWebRTCSessionDescriptionAPI.WebRTCSessionDescriptionStruct.class); + structs.add(GstSDPMessageAPI.SDPMessageStruct.class); + structs.add(GstPromiseAPI.PromiseStruct.class); + } } }